[{"data":1,"prerenderedAt":2909},["ShallowReactive",2],{"projects":3},[4,440,879,1267,1597,1897,2244,2566],{"id":5,"title":6,"body":7,"description":404,"extension":426,"featured":427,"meta":428,"navigation":427,"path":429,"seo":430,"sortOrder":431,"stem":432,"tagline":433,"tags":434,"__hash__":439},"projects/projects/climate-risk-map.md","Climate Risk Map Platform",{"type":8,"value":9,"toc":403},"minimark",[10,60,64,68,71,76,80,83,86,94,96,100,151,153,157,160,163,177,180,182,186,189,192,209,212,214,218,223,237,241,252,256,267,271,282,286,294,296,300,308,312,326,330,344,348,362,364,368,394,396,400],[11,12,27,28,27,37],"div",{"className":13},[14,15,16,17,18,19,20,21,22,23,24,25,26],"mb-8","flex","items-center","gap-4","rounded-xl","border","border-slate-200","bg-white/60","px-4","py-3","backdrop-blur","dark:border-slate-800","dark:bg-slate-950/50","\n  ",[29,30],"img",{"src":31,"alt":32,"className":33},"/images/logos/climasens.jpeg","Climasens",[34,35,36],"h-12","w-auto","object-contain",[11,38,42,43,42,52,27],{"className":39},[15,40,41],"flex-col","leading-tight","\n    ",[44,45,51],"span",{"className":46},[47,48,49,50],"text-xs","font-medium","text-slate-500","dark:text-slate-400","\n      Client\n    ",[44,53,59],{"className":54},[55,56,57,58],"text-sm","font-semibold","text-slate-900","dark:text-slate-100","\n      Climasens\n    ",[61,62],"stack-chips",{":items":63},"[\"Vue.js\", \"Nuxt.js\", \"Tailwind CSS\", \"Mapbox GL JS\", \"Firebase\", \"Carbon Vue\", \"JavaScript/TypeScript\"]",[65,66],"media-carousel",{":items":67},"[{\"type\": \"image\", \"src\": \"/images/projects/heat-risk/overview.webp\", \"caption\": \"Enterprise climate risk map interface with dynamic hazard layers, filters, and analytics views.\"}, {\"type\": \"image\", \"src\": \"/images/projects/heat-risk/futureHeatCropped.png\", \"caption\": \"Long-term heat risk visualization with scenario-based projections and time-horizon exploration.\"}, {\"type\": \"image\", \"src\": \"/images/projects/heat-risk/buildings.png\", \"caption\": \"Asset-level map overlays for property analysis and location-specific climate risk inspection.\"}]",[69,70],"hr",{},[72,73,75],"h2",{"id":74},"overview","Overview",[77,78,79],"p",{},"Climasens is an enterprise climate risk platform that helps businesses and governments understand and manage physical climate risk using geospatial data and climate models.",[77,81,82],{},"The platform supports multiple hazards, including floods, fire, extreme heat, and wind. It allows users to analyze risk across different time horizons, from short-term events to long-term projections spanning decades.",[77,84,85],{},"I lead the frontend of the platform. For over a year, I have been responsible for building and evolving the map experience, data visualization flows, and interactive features in a complex geospatial SaaS product.",[87,88,91],"callout",{"title":89,"type":90},"Project Scope","info",[77,92,93],{},"This case study focuses on my frontend work: architecture decisions, Mapbox integration, dynamic hazard layers, API integration, performance optimization, component refactoring, and translating Figma designs into production-ready features.",[69,95],{},[72,97,99],{"id":98},"my-role","My Role",[101,102,104],"contribution",{"role":103},"Frontend Lead",[105,106,107,111,114,117,120,128,131,134],"ul",{},[108,109,110],"li",{},"Lead frontend architecture across map, analytics, and interaction layers",[108,112,113],{},"Develop new features for an evolving enterprise SaaS product",[108,115,116],{},"Maintain and refactor existing components to improve scalability and code quality",[108,118,119],{},"Integrate frontend features with backend risk and analytics APIs",[108,121,122,123,127],{},"Manage ",[124,125,126],"code",{},"Mapbox GL JS"," layers for multiple climate hazards",[108,129,130],{},"Build dynamic indicators, filters, overlays, and data visualization components",[108,132,133],{},"Optimize performance for map rendering and data-heavy interfaces",[108,135,136,137,140,141,140,144,147,148],{},"Translate Figma designs into production-ready components using ",[124,138,139],{},"Nuxt",", ",[124,142,143],{},"Vue",[124,145,146],{},"Carbon Vue",", and ",[124,149,150],{},"Tailwind CSS",[69,152],{},[72,154,156],{"id":155},"problem","Problem",[77,158,159],{},"The platform needed to present large volumes of climate data in a way that was clear, fast, and interactive.",[77,161,162],{},"Users must:",[105,164,165,168,171,174],{},[108,166,167],{},"Explore risk across millions of properties",[108,169,170],{},"Compare multiple hazards",[108,172,173],{},"Switch between scenarios and time horizons",[108,175,176],{},"Interact with data-heavy maps and analytics panels",[77,178,179],{},"As the product grew, frontend complexity increased. The challenge was not only adding features, but keeping the system maintainable and responsive as new datasets and capabilities were introduced.",[69,181],{},[72,183,185],{"id":184},"solution","Solution",[77,187,188],{},"I evolved the frontend architecture with a focus on modularity, performance, and long-term maintainability.",[77,190,191],{},"Key improvements included:",[105,193,194,197,200,203,206],{},[108,195,196],{},"Separating map logic into clearer modules and composables",[108,198,199],{},"Reducing coupling between hazard configuration, layer rendering, and UI controls",[108,201,202],{},"Standardizing data flow between filters, indicators, map overlays, and API responses",[108,204,205],{},"Refactoring large components into smaller, easier-to-maintain units",[108,207,208],{},"Preserving performance while adding new hazards, projections, and analytics features",[77,210,211],{},"This approach allowed the product to grow without sacrificing stability or user experience.",[69,213],{},[72,215,217],{"id":216},"application-features","Application Features",[219,220,222],"h3",{"id":221},"geospatial-hazard-visualization","Geospatial Hazard Visualization",[105,224,225,231,234],{},[108,226,227,228,230],{},"Interactive ",[124,229,126],{}," maps for flood, fire, heat, and wind risks",[108,232,233],{},"Dynamic hazard switching and overlay controls",[108,235,236],{},"Region-aware map behavior across different datasets",[219,238,240],{"id":239},"asset-level-risk-analysis","Asset-Level Risk Analysis",[105,242,243,246,249],{},[108,244,245],{},"Property-level overlays for location-specific risk inspection",[108,247,248],{},"Integration with backend risk scoring APIs",[108,250,251],{},"Context-aware map interactions linked to selected assets",[219,253,255],{"id":254},"multi-horizon-scenario-exploration","Multi-Horizon & Scenario Exploration",[105,257,258,261,264],{},[108,259,260],{},"Short-term and long-term climate projections",[108,262,263],{},"Scenario comparison (e.g., SSP pathways)",[108,265,266],{},"Dynamic legends, indicators, and visual updates",[219,268,270],{"id":269},"data-rich-analytics-filtering","Data-Rich Analytics & Filtering",[105,272,273,276,279],{},[108,274,275],{},"Interactive filters for exploring complex risk data",[108,277,278],{},"Risk summary panels and supporting metrics",[108,280,281],{},"Coordinated state across map, panels, and overlays",[219,283,285],{"id":284},"alerts-real-time-views","Alerts & Real-Time Views",[105,287,288,291],{},[108,289,290],{},"Frontend support for real-time data updates",[108,292,293],{},"Stable UI behavior during asynchronous refresh cycles",[69,295],{},[72,297,299],{"id":298},"technical-highlights","Technical Highlights",[301,302,305],"decision",{"title":303,"tradeoff":304},"Modular frontend architecture for enterprise geospatial SaaS","Initial refactoring effort → long-term scalability and safer feature expansion",[77,306,307],{},"I refactored large map and analytics components into modular composables and reusable UI patterns. This reduced tight coupling between map rendering and business logic, making it easier to add new hazards, indicators, and features over time.",[219,309,311],{"id":310},"frontend-architecture-nuxt-vue","Frontend Architecture (Nuxt + Vue)",[105,313,314,317,320,323],{},[108,315,316],{},"Composable-based patterns for map behavior and UI state management",[108,318,319],{},"Refactoring large components into smaller, focused units",[108,321,322],{},"Reusable components for filters, panels, and indicator displays",[108,324,325],{},"Clean API integration without mixing UI logic and data-processing logic",[219,327,329],{"id":328},"geospatial-rendering-performance","Geospatial Rendering & Performance",[105,331,332,335,338,341],{},[108,333,334],{},"Optimized dynamic layer rendering and frequent state updates",[108,336,337],{},"Careful update strategies for overlays, legends, and interactions",[108,339,340],{},"Handling high-density geospatial data while maintaining smooth performance",[108,342,343],{},"Synchronization between map state and analytics panels",[219,345,347],{"id":346},"complex-state-management","Complex State Management",[105,349,350,353,356,359],{},[108,351,352],{},"Coordinated UI state across map controls and projection modes",[108,354,355],{},"Support for multi-scenario climate projections",[108,357,358],{},"Stable handling of asynchronous data updates",[108,360,361],{},"Designed for enterprise workflows involving repeated, data-heavy exploration",[69,363],{},[72,365,367],{"id":366},"what-this-project-demonstrates","What This Project Demonstrates",[105,369,370,373,376,382,385,388,391],{},[108,371,372],{},"Frontend leadership on an enterprise geospatial SaaS platform",[108,374,375],{},"Long-term ownership of a complex production frontend (1+ year)",[108,377,378,379,381],{},"Strong ",[124,380,126],{}," integration and geospatial engineering",[108,383,384],{},"Scalable frontend architecture for data-heavy applications",[108,386,387],{},"Performance optimization for interactive map systems",[108,389,390],{},"Collaboration within a multi-team product environment",[108,392,393],{},"Production implementation of UI/UX designs in a technically complex domain",[69,395],{},[72,397,399],{"id":398},"impact","Impact",[77,401,402],{},"This project represents my most advanced frontend work to date. I have led the evolution of a production climate intelligence platform, focusing on scalability, maintainability, and performance. My contributions ensure the frontend can safely grow as new hazards, datasets, and analytics capabilities are added over time.",{"title":404,"searchDepth":405,"depth":405,"links":406},"",2,[407,408,409,410,411,419,424,425],{"id":74,"depth":405,"text":75},{"id":98,"depth":405,"text":99},{"id":155,"depth":405,"text":156},{"id":184,"depth":405,"text":185},{"id":216,"depth":405,"text":217,"children":412},[413,415,416,417,418],{"id":221,"depth":414,"text":222},3,{"id":239,"depth":414,"text":240},{"id":254,"depth":414,"text":255},{"id":269,"depth":414,"text":270},{"id":284,"depth":414,"text":285},{"id":298,"depth":405,"text":299,"children":420},[421,422,423],{"id":310,"depth":414,"text":311},{"id":328,"depth":414,"text":329},{"id":346,"depth":414,"text":347},{"id":366,"depth":405,"text":367},{"id":398,"depth":405,"text":399},"md",true,{},"/projects/climate-risk-map",{"title":6,"description":404},1,"projects/climate-risk-map","Enterprise geospatial SaaS frontend for climate risk analysis and decision support",[435,436,150,126,437,146,438],"Vue.js","Nuxt.js","Firebase","JavaScript/TypeScript","H_GzckHX149Hmli8P6T-GYFhE6x8nxpBJcu5YMPY-oU",{"id":441,"title":442,"body":443,"description":404,"extension":426,"featured":427,"meta":870,"navigation":427,"path":871,"seo":872,"sortOrder":414,"stem":873,"tagline":874,"tags":875,"__hash__":878},"projects/projects/qualivendas-platform.md","QualiVendas Insurance Sales Platform",{"type":8,"value":444,"toc":849},[445,464,467,473,479,481,483,486,489,492,507,510,512,514,518,521,524,563,566,568,572,575,598,601,603,605,608,611,631,634,636,638,641,658,665,668,670,672,675,701,704,721,725,739,741,743,747,768,772,788,792,809,811,813,839,841,843,846],[11,446,27,448,27,454],{"className":447},[14,15,16,17,18,19,20,21,22,23,24,25,26],[29,449],{"src":450,"alt":451,"className":452},"/images/logos/qualicorp.png","Qualicorp",[453,35,36],"h-8",[11,455,42,457,42,460,27],{"className":456},[15,40,41],[44,458,51],{"className":459},[47,48,49,50],[44,461,463],{"className":462},[55,56,57,58],"\n      Qualicorp - Brazil's largest health insurance administrator\n    ",[61,465],{":items":466},"[\"Node.js\", \"Express.js\", \"Vue.js\", \"Neo4j\", \"BootstrapVue\", \"Vuetify\", \"AWS S3\", \"JavaScript/TypeScript\"]",[468,469],"screenshot",{"caption":470,"src":471,"type":472},"Enterprise application ecosystem for health insurance proposal creation, governance, and operational management","/images/projects/qualivendas/overview.mov","video",[87,474,476],{"title":475,"type":90},"Context",[77,477,478],{},"QualiVendas is a nationwide digital ecosystem used to manage the full lifecycle of health insurance sales in Brazil. It includes a broker platform for proposal creation and submission, and an admin platform for governance, permissions, and operational control.",[69,480],{},[72,482,75],{"id":74},[77,484,485],{},"QualiVendas is a nationwide digital platform used to manage the full lifecycle of health insurance sales in Brazil.",[77,487,488],{},"The system replaced manual and fragmented processes with a structured digital workflow capable of handling high proposal volume while enforcing strict business and regulatory rules.",[77,490,491],{},"The ecosystem includes two connected platforms:",[105,493,494,501],{},[108,495,496,500],{},[497,498,499],"strong",{},"Broker Platform",": used by brokers to create and submit health insurance proposals",[108,502,503,506],{},[497,504,505],{},"Admin Platform",": used by supervisors and managers to control permissions, monitor operations, and manage governance",[77,508,509],{},"I worked on the project in two phases: first as a Full-Stack Engineer building core features, and later as a Technical Lead guiding technical decisions, code quality, and delivery execution.",[69,511],{},[72,513,99],{"id":98},[219,515,517],{"id":516},"full-stack-engineer","Full-Stack Engineer",[77,519,520],{},"I built and maintained features across both frontend and backend layers.",[77,522,523],{},"My responsibilities included:",[105,525,526,531,541,547,550,553,560],{},[108,527,528,529],{},"Building scalable frontend flows using ",[124,530,435],{},[108,532,533,534,537,538],{},"Developing REST APIs using ",[124,535,536],{},"Node.js"," and ",[124,539,540],{},"Express.js",[108,542,543,544],{},"Modeling complex business relationships in ",[124,545,546],{},"Neo4j",[108,548,549],{},"Implementing proposal validations and eligibility rules",[108,551,552],{},"Managing proposal status transitions (draft, submitted, approved, rejected, returned)",[108,554,555,556,559],{},"Integrating document generation and ",[124,557,558],{},"AWS S3"," storage",[108,561,562],{},"Supporting production issues and improving system stability",[77,564,565],{},"I worked across the full lifecycle of a proposal, from UI interaction to backend processing and database persistence.",[69,567],{},[219,569,571],{"id":570},"technical-lead-later-phase","Technical Lead (Later Phase)",[77,573,574],{},"After transitioning into a Technical Lead role, I expanded my responsibilities to include:",[105,576,577,580,583,586,589,592,595],{},[108,578,579],{},"Defining technical approaches for new features",[108,581,582],{},"Reviewing pull requests and maintaining code quality standards",[108,584,585],{},"Coordinating frontend, backend, and QA teams",[108,587,588],{},"Supporting sprint planning and technical discussions",[108,590,591],{},"Ensuring delivery consistency across releases",[108,593,594],{},"Acting as a bridge between business stakeholders and developers",[108,596,597],{},"Managing urgent production issues and hotfixes",[77,599,600],{},"I continued contributing code while helping guide the technical direction of the platform.",[69,602],{},[72,604,156],{"id":155},[77,606,607],{},"Health insurance sales require strict control over eligibility rules, billing logic, and proposal validation. Manual processes created delays, inconsistencies, and operational risk.",[77,609,610],{},"The platform needed to:",[105,612,613,616,619,622,625,628],{},[108,614,615],{},"Support high-volume proposal creation nationwide",[108,617,618],{},"Enforce strict business and regulatory rules",[108,620,621],{},"Serve multiple user roles with different permissions",[108,623,624],{},"Track proposal status across its full lifecycle",[108,626,627],{},"Allow corrections and resubmissions safely",[108,629,630],{},"Provide governance and visibility for supervisors and managers",[77,632,633],{},"The challenge was building a reliable system that could handle operational complexity while remaining scalable and maintainable.",[69,635],{},[72,637,185],{"id":184},[77,639,640],{},"I helped build and evolve both the broker and admin platforms with a focus on:",[105,642,643,646,649,652,655],{},[108,644,645],{},"Reliable proposal workflows",[108,647,648],{},"Strong validation and rule enforcement",[108,650,651],{},"Clear lifecycle control",[108,653,654],{},"Scalable architecture",[108,656,657],{},"Production stability",[77,659,660,661,664],{},"On the frontend, I implemented structured multi-step workflows and role-based UI restrictions.",[662,663],"br",{},"\nOn the backend, I implemented business logic, validation layers, lifecycle transitions, and domain modeling.",[77,666,667],{},"As the platform matured, I also helped improve long-term maintainability through refactoring, code review, and technical leadership.",[69,669],{},[72,671,217],{"id":216},[219,673,499],{"id":674},"broker-platform",[105,676,677,680,683,686,689,695,698],{},[108,678,679],{},"Multi-step proposal creation workflow",[108,681,682],{},"Policyholder and dependent registration",[108,684,685],{},"Plan eligibility validation",[108,687,688],{},"Billing configuration and data verification",[108,690,691,692,694],{},"Digital document generation and storage (",[124,693,558],{},")",[108,696,697],{},"Proposal submission and status tracking",[108,699,700],{},"Correction and resubmission flows",[219,702,505],{"id":703},"admin-platform",[105,705,706,709,712,715,718],{},[108,707,708],{},"Role-based access control",[108,710,711],{},"Hierarchical permission management",[108,713,714],{},"Operational oversight for supervisors and managers",[108,716,717],{},"Proposal monitoring and workflow visibility",[108,719,720],{},"Governance controls across broker structures",[219,722,724],{"id":723},"shared-workflow-across-platforms","Shared Workflow Across Platforms",[105,726,727,730,733,736],{},[108,728,729],{},"Consistent proposal status tracking across broker and admin systems",[108,731,732],{},"Controlled status transitions with validation checks",[108,734,735],{},"Centralized rule enforcement at the application level",[108,737,738],{},"Production support tools to maintain operational continuity",[69,740],{},[72,742,299],{"id":298},[219,744,746],{"id":745},"full-stack-engineering-vue-express","Full-Stack Engineering (Vue + Express)",[105,748,749,754,762,765],{},[108,750,751,753],{},[124,752,435],{}," frontend architecture for scalable enterprise workflows",[108,755,756,757,759,760],{},"REST API development with ",[124,758,536],{}," / ",[124,761,540],{},[108,763,764],{},"Role-based UI and backend validation working together",[108,766,767],{},"Continuous refactoring to maintain clarity in complex workflows",[219,769,771],{"id":770},"domain-modeling-workflow-control","Domain Modeling & Workflow Control",[105,773,774,779,782,785],{},[108,775,776,778],{},[124,777,546],{}," graph modeling for relationship-heavy insurance logic",[108,780,781],{},"Controlled proposal lifecycle transitions",[108,783,784],{},"Validation across proposal, dependents, billing, and document flows",[108,786,787],{},"Strict enforcement of regulatory and business rules",[219,789,791],{"id":790},"delivery-production-reliability","Delivery & Production Reliability",[105,793,794,797,800,803,806],{},[108,795,796],{},"Transition from feature development to technical leadership",[108,798,799],{},"Code review and standards enforcement",[108,801,802],{},"Cross-team coordination (frontend, backend, QA, PO, PM)",[108,804,805],{},"Sprint planning and execution in a Scrum/Kanban hybrid model",[108,807,808],{},"Rapid response to production issues and urgent fixes",[69,810],{},[72,812,367],{"id":366},[105,814,815,818,821,824,830,833,836],{},[108,816,817],{},"Full-stack engineering in a business-critical insurance domain",[108,819,820],{},"Experience building high-volume production workflows",[108,822,823],{},"Strong business rule enforcement and lifecycle management",[108,825,826,827,829],{},"Graph database modeling (",[124,828,546],{},") for complex relationships",[108,831,832],{},"Governance-aware system design with hierarchical permissions",[108,834,835],{},"Technical leadership progression within a live enterprise product",[108,837,838],{},"Delivery ownership across development, maintenance, and production support",[69,840],{},[72,842,399],{"id":398},[77,844,845],{},"QualiVendas is a large-scale production system directly supporting nationwide health insurance sales operations.",[77,847,848],{},"My contributions helped build, stabilize, and evolve the platform across multiple phases, from full-stack feature development to technical leadership, ensuring reliable proposal workflows, controlled governance, and consistent delivery in a complex enterprise environment.",{"title":404,"searchDepth":405,"depth":405,"links":850},[851,852,856,857,858,863,868,869],{"id":74,"depth":405,"text":75},{"id":98,"depth":405,"text":99,"children":853},[854,855],{"id":516,"depth":414,"text":517},{"id":570,"depth":414,"text":571},{"id":155,"depth":405,"text":156},{"id":184,"depth":405,"text":185},{"id":216,"depth":405,"text":217,"children":859},[860,861,862],{"id":674,"depth":414,"text":499},{"id":703,"depth":414,"text":505},{"id":723,"depth":414,"text":724},{"id":298,"depth":405,"text":299,"children":864},[865,866,867],{"id":745,"depth":414,"text":746},{"id":770,"depth":414,"text":771},{"id":790,"depth":414,"text":791},{"id":366,"depth":405,"text":367},{"id":398,"depth":405,"text":399},{},"/projects/qualivendas-platform",{"title":442,"description":404},"projects/qualivendas-platform","Enterprise insurance sales ecosystem for proposal workflow, governance, and operations at nationwide scale",[536,435,540,546,876,877,558,438],"BootstrapVue","Vuetify","zotKptX6oL6dc6PbiXlixAeG9XIvcJJoE2sn0_IWHSU",{"id":880,"title":881,"body":882,"description":404,"extension":426,"featured":427,"meta":1258,"navigation":427,"path":1259,"seo":1260,"sortOrder":1261,"stem":1262,"tagline":1263,"tags":1264,"__hash__":1266},"projects/projects/cool-cats-event.md","Cooltopia - Cool Cats NFT",{"type":8,"value":883,"toc":1238},[884,902,905,909,914,916,918,921,924,926,928,988,990,992,995,998,1000,1002,1005,1008,1035,1038,1040,1042,1046,1057,1061,1072,1076,1087,1091,1102,1106,1117,1119,1121,1134,1138,1159,1163,1179,1183,1205,1207,1209,1229,1231,1233,1236],[11,885,27,887,27,892],{"className":886},[14,15,16,17,18,19,20,21,22,23,24,25,26],[29,888],{"src":889,"alt":890,"className":891},"/images/logos/coolcats.png","Cool Cats",[34,35,36],[11,893,42,895,42,898,27],{"className":894},[15,40,41],[44,896,51],{"className":897},[47,48,49,50],[44,899,901],{"className":900},[55,56,57,58],"\n      Cooltopia 2022 - New York, USA\n    ",[61,903],{":items":904},"[\"Node.js\", \"NestJS\", \"MongoDB\", \"RabbitMQ\", \"MQTT\", \"SendGrid\", \"Vue.js\", \"Quasar Framework\", \"Electron\", \"Ionic Vue\", \"JavaScript/TypeScript\"]",[468,906],{"caption":907,"src":908,"type":472},"Application layer powering NFC check-ins, game participation, scoring, and reward redemption during the Cooltopia live event","/images/projects/cooltopia/overview.mp4",[87,910,911],{"title":475,"type":90},[77,912,913],{},"Cooltopia was a large physical Cool Cats NFT event held in July 2022 at Center415 in New York City (30,000 sq ft venue). The event operated as an immersive mini amusement park where each attendee used an NFC-enabled bracelet to access activities, track points, and redeem rewards across the venue.",[69,915],{},[72,917,75],{"id":74},[77,919,920],{},"Cooltopia was a connected physical-to-digital event system where attendees used NFC bracelets to start games, track performance, accumulate points in real time, and redeem merchandise or food rewards. The platform acted as the operational application layer for participant identity, scoring, activity tracking, and reward redemption across multiple stations in the venue.",[77,922,923],{},"I built the full application layer of the system, including backend APIs, scoring logic, participant tracking, totem interfaces, a reward redemption app, and an event CMS. The result was a real-world event platform designed to support high traffic, distributed interactions, and real-time state consistency during live operations.",[69,925],{},[72,927,99],{"id":98},[101,929,930],{"role":517},[105,931,932,946,949,952,955,958,965,978,985],{},[108,933,934,935,140,937,140,940,147,943],{},"Designed and implemented backend APIs using ",[124,936,536],{},[124,938,939],{},"NestJS",[124,941,942],{},"MongoDB",[124,944,945],{},"TypeScript",[108,947,948],{},"Built participant identity and activity tracking flows based on NFC bracelet scans",[108,950,951],{},"Implemented game scoring logic, including time-based scoring and accumulated points management",[108,953,954],{},"Ingested score data from multiple sources, including external digital game systems",[108,956,957],{},"Stored participant activity history and synchronized score/points updates across the event system",[108,959,960,961,964],{},"Integrated backend workflows with event devices and partner systems, including treasure chest integration via ",[124,962,963],{},"MQTT"," and API-based integrations for totems and other event flows",[108,966,967,968,970,971,970,974,977],{},"Built totem frontend applications (",[124,969,435],{}," + ",[124,972,973],{},"Quasar",[124,975,976],{},"Electron",") for participant interactions",[108,979,980,981,984],{},"Built a separate reward redemption app (",[124,982,983],{},"Ionic Vue",") with real-time point deduction and backend sync",[108,986,987],{},"Built an event CMS for participant management, activity administration, email sending, and manual point adjustments",[69,989],{},[72,991,156],{"id":155},[77,993,994],{},"The event needed a reliable application system to coordinate participant activity across many physical stations in real time. Each attendee used a single NFC bracelet identity across games, scoring stations, and reward redemption, so the platform had to maintain consistent participant state while handling high event traffic and multiple score sources.",[77,996,997],{},"The engineering challenge was to connect physical interactions and digital systems into one operational workflow without delays, double counting, or point inconsistencies during a live event.",[69,999],{},[72,1001,185],{"id":184},[77,1003,1004],{},"I built a full-stack event platform centered on NFC-based participant identity and real-time point tracking. The backend handled participant state, score ingestion, scoring rules, activity history, and redemption logic, while the frontend applications handled participant-facing totems, reward redemption interactions, and event operations through a CMS.",[77,1006,1007],{},"The system was designed to support live-event reliability by:",[105,1009,1010,1013,1016,1019,1022],{},[108,1011,1012],{},"Using a centralized participant record tied to NFC bracelet scans",[108,1014,1015],{},"Processing score updates from time-based activities and external digital games",[108,1017,1018],{},"Synchronizing points and redemption state across totems, reward devices, and admin tools",[108,1020,1021],{},"Separating operational interfaces by role (participant totems, redemption app, CMS)",[108,1023,1024,1025,1027,1028,970,1031,1034],{},"Integrating treasure chest communication (",[124,1026,963],{},") and participant email workflows (",[124,1029,1030],{},"RabbitMQ",[124,1032,1033],{},"SendGrid",") alongside API-based event integrations",[77,1036,1037],{},"This approach made the platform the operational brain of the event, connecting physical installations and digital experiences through a shared application layer.",[69,1039],{},[72,1041,217],{"id":216},[219,1043,1045],{"id":1044},"nfc-bracelet-identity-participant-tracking","NFC Bracelet Identity & Participant Tracking",[105,1047,1048,1051,1054],{},[108,1049,1050],{},"NFC scan-based participant identification across games, activities, and reward redemption",[108,1052,1053],{},"Centralized participant profile and points tracking across the venue",[108,1055,1056],{},"Full activity history storage for auditing and operational support",[219,1058,1060],{"id":1059},"totem-applications-vue-quasar-electron","Totem Applications (Vue + Quasar + Electron)",[105,1062,1063,1066,1069],{},[108,1064,1065],{},"Participant-facing totem interfaces to start and finish activities",[108,1067,1068],{},"Real-time display of results and accumulated points",[108,1070,1071],{},"Fast, simple interactions designed for high throughput during live event traffic",[219,1073,1075],{"id":1074},"scoring-score-ingestion-system","Scoring & Score Ingestion System",[105,1077,1078,1081,1084],{},[108,1079,1080],{},"Time-based game scoring logic managed in the backend",[108,1082,1083],{},"Score ingestion from external digital games and integrated partner systems",[108,1085,1086],{},"Point accumulation updates synchronized to participant records and frontends",[219,1088,1090],{"id":1089},"reward-redemption-app-ionic-vue","Reward Redemption App (Ionic Vue)",[105,1092,1093,1096,1099],{},[108,1094,1095],{},"Separate redemption application for merchandise and food selection",[108,1097,1098],{},"NFC scan to identify participant and fetch current points",[108,1100,1101],{},"Automatic point deduction and real-time backend synchronization during redemption",[219,1103,1105],{"id":1104},"event-cms-operations","Event CMS & Operations",[105,1107,1108,1111,1114],{},[108,1109,1110],{},"CMS for participant management and activity administration",[108,1112,1113],{},"Email sending workflows for event operations and communication",[108,1115,1116],{},"Manual point adjustment tools for operational corrections when needed",[69,1118],{},[72,1120,299],{"id":298},[301,1122,1125],{"title":1123,"tradeoff":1124},"Using the right integration path for each event workflow","Multiple integration patterns → clearer responsibilities and more reliable operations",[77,1126,1127,1128,1130,1131,1133],{},"I used ",[124,1129,963],{}," specifically for the treasure chest integration, while totems and other event interactions used normal API-based communication. ",[124,1132,1030],{}," was used for asynchronous participant email workflows. This separation matched the runtime needs of each integration and kept the system easier to reason about during live operations.",[219,1135,1137],{"id":1136},"backend-application-layer-nestjs-mongodb","Backend Application Layer (NestJS + MongoDB)",[105,1139,1140,1143,1146,1149],{},[108,1141,1142],{},"API design for participant tracking, activities, scoring, redemption, and CMS operations",[108,1144,1145],{},"Centralized participant state management tied to NFC bracelet identity",[108,1147,1148],{},"Storage of detailed activity history and accumulated points",[108,1150,1151,1152,1154,1155,970,1157,694],{},"Integration points for external digital game systems, API-based event clients (including totems), treasure chest communication (",[124,1153,963],{},"), and email workflows (",[124,1156,1030],{},[124,1158,1033],{},[219,1160,1162],{"id":1161},"real-time-event-integration-consistency","Real-Time Event Integration & Consistency",[105,1164,1165,1168,1173,1176],{},[108,1166,1167],{},"Multi-source score ingestion (time-based games + external game APIs)",[108,1169,1170,1172],{},[124,1171,963],{}," integration for the treasure chest physical interaction workflow",[108,1174,1175],{},"Consistency of points and redemption state across distributed devices and apps",[108,1177,1178],{},"Cross-team integration with digital game systems, totem/client API flows, and physical installation workflows",[219,1180,1182],{"id":1181},"frontend-applications-totems-redemption-app-cms","Frontend Applications (Totems, Redemption App, CMS)",[105,1184,1185,1194,1199,1202],{},[108,1186,1187,970,1189,970,1191,1193],{},[124,1188,435],{},[124,1190,973],{},[124,1192,976],{}," totem interfaces optimized for event-floor usage",[108,1195,1196,1198],{},[124,1197,983],{}," redemption app for staff-operated reward and food redemption flows",[108,1200,1201],{},"Admin CMS interfaces supporting live event operations and exception handling",[108,1203,1204],{},"UI behavior designed for quick interactions, clear feedback, and continuous operation",[69,1206],{},[72,1208,367],{"id":366},[105,1210,1211,1214,1217,1220,1223,1226],{},[108,1212,1213],{},"Full-stack ownership of a real-world event platform application layer",[108,1215,1216],{},"Real-time systems engineering for live operational environments",[108,1218,1219],{},"NFC-based identity management and physical-to-digital workflow integration",[108,1221,1222],{},"Backend architecture for distributed scoring, participant tracking, and redemption logic",[108,1224,1225],{},"Cross-team technical integration with digital game teams and physical installation teams",[108,1227,1228],{},"Event-scale reliability and state consistency across multiple apps and devices",[69,1230],{},[72,1232,399],{"id":398},[77,1234,1235],{},"Cooltopia demonstrates full-stack engineering ownership in a high-scale live event environment. The system successfully connected NFC identity, scoring, participant tracking, and reward redemption across physical stations and digital applications, enabling the event to operate as a unified gamified experience in real time.",[69,1237],{},{"title":404,"searchDepth":405,"depth":405,"links":1239},[1240,1241,1242,1243,1244,1251,1256,1257],{"id":74,"depth":405,"text":75},{"id":98,"depth":405,"text":99},{"id":155,"depth":405,"text":156},{"id":184,"depth":405,"text":185},{"id":216,"depth":405,"text":217,"children":1245},[1246,1247,1248,1249,1250],{"id":1044,"depth":414,"text":1045},{"id":1059,"depth":414,"text":1060},{"id":1074,"depth":414,"text":1075},{"id":1089,"depth":414,"text":1090},{"id":1104,"depth":414,"text":1105},{"id":298,"depth":405,"text":299,"children":1252},[1253,1254,1255],{"id":1136,"depth":414,"text":1137},{"id":1161,"depth":414,"text":1162},{"id":1181,"depth":414,"text":1182},{"id":366,"depth":405,"text":367},{"id":398,"depth":405,"text":399},{},"/projects/cool-cats-event",{"title":881,"description":404},4,"projects/cool-cats-event","Full-stack NFC-powered event platform for a large-scale interactive live experience",[536,939,942,1030,963,1033,435,1265,976,983,438],"Quasar Framework","CBFwZdCRo82J4LLR--likd4HNq8m47JjdfgTeCxDh3Q",{"id":1268,"title":1269,"body":1270,"description":404,"extension":426,"featured":1587,"meta":1588,"navigation":427,"path":1589,"seo":1590,"sortOrder":1591,"stem":1592,"tagline":1593,"tags":1594,"__hash__":1596},"projects/projects/valorant-live-robot.md","Valorant Live Interaction System",{"type":8,"value":1271,"toc":1568},[1272,1290,1293,1296,1301,1303,1305,1308,1311,1314,1317,1319,1321,1346,1348,1350,1353,1356,1358,1360,1363,1366,1393,1396,1398,1400,1404,1421,1425,1439,1443,1454,1458,1469,1471,1473,1480,1484,1495,1499,1510,1514,1528,1530,1532,1558,1560,1562,1565],[11,1273,27,1275,27,1280],{"className":1274},[14,15,16,17,18,19,20,21,22,23,24,25,26],[29,1276],{"src":1277,"alt":1278,"className":1279},"/images/logos/riot.png","Riot Games",[34,35,36],[11,1281,42,1283,42,1286,27],{"className":1282},[15,40,41],[44,1284,51],{"className":1285},[47,48,49,50],[44,1287,1289],{"className":1288},[55,56,57,58],"\n      Riot Games - São Paulo, Brazil\n    ",[61,1291],{":items":1292},"[\"Node.js\", \"NestJS\", \"WebSocket\", \"MongoDB\", \"JavaScript/TypeScript\"]",[65,1294],{":items":1295},"[{\"type\": \"image\", \"src\": \"/images/projects/riot/cover.jpeg\", \"caption\": \"Live Valorant broadcast setup where selected Twitch viewers controlled a physical studio robot.\"}, {\"type\": \"image\", \"src\": \"/images/projects/riot/fromrobot.jpeg\", \"caption\": \"Robot perspective during the live challenge track controlled by a remote participant.\"}, {\"type\": \"image\", \"src\": \"/images/projects/riot/chat.jpeg\", \"caption\": \"Twitch chat participation flow used to select viewers for the live control session.\"}, {\"type\": \"video\", \"src\": \"/images/projects/riot/overview.mp4\", \"poster\": \"/images/projects/riot/overviewcover.png\", \"caption\": \"End-to-end interaction: participant selection, joystick control, backend bridge, and robot response in real time.\"}]",[87,1297,1298],{"title":475,"type":90},[77,1299,1300],{},"This system was built for a live Valorant broadcast on Twitch. Selected viewers from chat could control a physical car robot in the studio using a joystick on mobile or desktop.",[69,1302],{},[72,1304,75],{"id":74},[77,1306,1307],{},"The Valorant Live Interaction System was built for a live Twitch broadcast. During the show, selected viewers from chat could remotely control a physical car robot inside the studio using a joystick interface on mobile or desktop.",[77,1309,1310],{},"The goal was to complete a physical track as fast as possible.",[77,1312,1313],{},"The project created a real-time connection between an online audience and a physical setup in a live studio.",[77,1315,1316],{},"I built the full application layer: joystick frontend and backend bridge.",[69,1318],{},[72,1320,99],{"id":98},[101,1322,1323],{"role":517},[105,1324,1325,1328,1331,1334,1337,1340,1343],{},[108,1326,1327],{},"Built the joystick web interface used by selected participants",[108,1329,1330],{},"Built the backend system that received joystick data and forwarded it to the robot",[108,1332,1333],{},"Managed real-time communication between participant and robot using WebSocket",[108,1335,1336],{},"Implemented participant registration and unique access link flow",[108,1338,1339],{},"Managed active session control so only the selected user could drive the robot",[108,1341,1342],{},"Added validation to prevent multiple users controlling the robot at the same time",[108,1344,1345],{},"Focused on frontend and backend application logic for live broadcast reliability",[69,1347],{},[72,1349,156],{"id":155},[77,1351,1352],{},"The live show needed a reliable way for a selected viewer to control a physical robot remotely. The system had to keep commands fast, block non-selected users, and stay stable during the broadcast.",[77,1354,1355],{},"Any delay or control conflict would affect the live segment immediately.",[69,1357],{},[72,1359,185],{"id":184},[77,1361,1362],{},"I built a simple real-time flow at the application level:",[77,1364,1365],{},"The workflow operated as follows:",[1367,1368,1369,1372,1375,1378,1381,1384,1387,1390],"ol",{},[108,1370,1371],{},"A viewer was selected from Twitch chat.",[108,1373,1374],{},"Our team confirmed their participation.",[108,1376,1377],{},"I registered the participant in the system.",[108,1379,1380],{},"The participant received a unique link.",[108,1382,1383],{},"The link opened a joystick interface in the browser.",[108,1385,1386],{},"The joystick sent X and Y movement values to the backend.",[108,1388,1389],{},"The backend forwarded those values to the robot via WebSocket.",[108,1391,1392],{},"The robot responded in real time.",[77,1394,1395],{},"The backend acted as the bridge between the joystick interface and the physical robot.",[69,1397],{},[72,1399,217],{"id":216},[219,1401,1403],{"id":1402},"joystick-frontend","Joystick Frontend",[105,1405,1406,1409,1412,1415,1418],{},[108,1407,1408],{},"Browser-based joystick accessible from mobile or desktop",[108,1410,1411],{},"Sent X and Y movement values in real time",[108,1413,1414],{},"WebSocket connection from frontend to backend",[108,1416,1417],{},"Participant ID validation before control started",[108,1419,1420],{},"Smooth interaction designed for live use",[219,1422,1424],{"id":1423},"backend-bridge","Backend Bridge",[105,1426,1427,1430,1433,1436],{},[108,1428,1429],{},"WebSocket server for real-time communication",[108,1431,1432],{},"Forwarded joystick input to the robot",[108,1434,1435],{},"Managed active participant sessions",[108,1437,1438],{},"Restricted control to one selected user at a time",[219,1440,1442],{"id":1441},"session-access-control","Session & Access Control",[105,1444,1445,1448,1451],{},[108,1446,1447],{},"Only the active participant could send control commands",[108,1449,1450],{},"Blocked simultaneous control attempts from other users",[108,1452,1453],{},"Controlled start and end of each live session",[219,1455,1457],{"id":1456},"live-broadcast-flow","Live Broadcast Flow",[105,1459,1460,1463,1466],{},[108,1461,1462],{},"Participant registration and unique control link generation",[108,1464,1465],{},"Quick handoff from Twitch selection to joystick access",[108,1467,1468],{},"Stable command flow during the on-air segment",[69,1470],{},[72,1472,299],{"id":298},[301,1474,1477],{"title":1475,"tradeoff":1476},"Real-time communication using WebSocket with control restricted to the active participant","Strict session control logic → reliable live interaction",[77,1478,1479],{},"WebSocket handled the live joystick stream. Session checks ensured only the selected participant could control the robot at any time.",[219,1481,1483],{"id":1482},"real-time-communication","Real-Time Communication",[105,1485,1486,1489,1492],{},[108,1487,1488],{},"WebSocket communication from joystick frontend to backend bridge",[108,1490,1491],{},"Low-latency transfer of joystick X/Y values",[108,1493,1494],{},"Real-time forwarding from backend to robot",[219,1496,1498],{"id":1497},"frontend","Frontend",[105,1500,1501,1504,1507],{},[108,1502,1503],{},"Simple joystick interface for mobile and desktop",[108,1505,1506],{},"Participant ID validation in the control flow",[108,1508,1509],{},"Focused UI to reduce mistakes during live interaction",[219,1511,1513],{"id":1512},"backend","Backend",[105,1515,1516,1519,1522,1525],{},[108,1517,1518],{},"Active participant session management",[108,1520,1521],{},"Control restriction to one user at a time",[108,1523,1524],{},"Reliable bridge between web interface and robot controller",[108,1526,1527],{},"Stable behavior under live broadcast conditions",[69,1529],{},[72,1531,367],{"id":366},[105,1533,1534,1537,1540,1543,1546,1549,1552,1555],{},[108,1535,1536],{},"Real-time system design",[108,1538,1539],{},"WebSocket implementation",[108,1541,1542],{},"Hardware control via backend bridge architecture",[108,1544,1545],{},"Live broadcast engineering",[108,1547,1548],{},"Session control and validation",[108,1550,1551],{},"Full-stack application ownership",[108,1553,1554],{},"Low-latency interaction handling",[108,1556,1557],{},"Production reliability during live event conditions",[69,1559],{},[72,1561,399],{"id":398},[77,1563,1564],{},"The system enabled selected Twitch viewers to control a physical robot during a live broadcast with low latency and stable control.",[77,1566,1567],{},"My work ensured reliable real-time communication, correct session control, and smooth live operation at the application layer.",{"title":404,"searchDepth":405,"depth":405,"links":1569},[1570,1571,1572,1573,1574,1580,1585,1586],{"id":74,"depth":405,"text":75},{"id":98,"depth":405,"text":99},{"id":155,"depth":405,"text":156},{"id":184,"depth":405,"text":185},{"id":216,"depth":405,"text":217,"children":1575},[1576,1577,1578,1579],{"id":1402,"depth":414,"text":1403},{"id":1423,"depth":414,"text":1424},{"id":1441,"depth":414,"text":1442},{"id":1456,"depth":414,"text":1457},{"id":298,"depth":405,"text":299,"children":1581},[1582,1583,1584],{"id":1482,"depth":414,"text":1483},{"id":1497,"depth":414,"text":1498},{"id":1512,"depth":414,"text":1513},{"id":366,"depth":405,"text":367},{"id":398,"depth":405,"text":399},false,{},"/projects/valorant-live-robot",{"title":1269,"description":404},5,"projects/valorant-live-robot","Real-time audience control platform for a live Twitch broadcast",[536,939,1595,942,438],"WebSocket","CTdfIqjXK2CIR6OcsWDDxDWTwJ2C5_Ji1kvQXljnyjA",{"id":1598,"title":1599,"body":1600,"description":404,"extension":426,"featured":1587,"meta":1888,"navigation":427,"path":1889,"seo":1890,"sortOrder":1891,"stem":1892,"tagline":1893,"tags":1894,"__hash__":1896},"projects/projects/bk-million-dollar-whopper.md","BK Million Dollar Whopper Platform",{"type":8,"value":1601,"toc":1870},[1602,1620,1623,1627,1629,1631,1634,1637,1642,1644,1646,1687,1689,1691,1694,1696,1698,1701,1704,1718,1721,1723,1725,1729,1740,1744,1755,1759,1770,1774,1785,1787,1789,1796,1800,1816,1820,1834,1836,1838,1861,1863,1865,1868],[11,1603,27,1605,27,1610],{"className":1604},[14,15,16,17,18,19,20,21,22,23,24,25,26],[29,1606],{"src":1607,"alt":1608,"className":1609},"/images/logos/burger-king.png","Burger King",[34,35,36],[11,1611,42,1613,42,1616,27],{"className":1612},[15,40,41],[44,1614,51],{"className":1615},[47,48,49,50],[44,1617,1619],{"className":1618},[55,56,57,58],"\n      Burger King - Global Marketing Campaign\n    ",[61,1621],{":items":1622},"[\"React.js\", \"Redux\", \"Material UI\", \"Tailwind CSS\", \"JavaScript/TypeScript\"]",[468,1624],{"caption":1625,"src":1626,"type":472},"Frontend campaign experience for burger customization, AI-generated content, and competition submission","/images/projects/bk/overview.mp4",[69,1628],{},[72,1630,75],{"id":74},[77,1632,1633],{},"BK Million Dollar Whopper was a national campaign experience where users could build a custom Whopper, generate an AI image and jingle, and submit their creation to compete for a $1,000,000 prize.",[77,1635,1636],{},"I owned the frontend application and integration layer, building the full user experience and connecting it to backend APIs and AI generation services built by other teams.",[87,1638,1639],{"title":89,"type":90},[77,1640,1641],{},"This case study focuses on the frontend implementation work I delivered: building the experience from provided Figma designs, Redux state management, API integration, async AI response handling, and stable UI states during content generation.",[69,1643],{},[72,1645,99],{"id":98},[101,1647,1649],{"role":1648},"Frontend Engineer",[105,1650,1651,1663,1666,1672,1675,1678,1681,1684],{},[108,1652,1653,1654,140,1657,147,1660,1662],{},"Built the full frontend application in ",[124,1655,1656],{},"React",[124,1658,1659],{},"Material UI",[124,1661,150],{}," based on designs provided by the UI/UX team",[108,1664,1665],{},"Implemented the multi-step burger configuration and submission flow",[108,1667,1668,1669],{},"Managed cross-step application state with ",[124,1670,1671],{},"Redux",[108,1673,1674],{},"Integrated frontend flows with backend APIs and AI generation endpoints",[108,1676,1677],{},"Handled asynchronous AI responses for image and audio generation",[108,1679,1680],{},"Rendered AI-generated content dynamically within the user journey",[108,1682,1683],{},"Implemented loading, error, and fallback states for network and generation steps",[108,1685,1686],{},"Implemented a responsive, polished interface across devices following the approved Figma designs",[69,1688],{},[72,1690,156],{"id":155},[77,1692,1693],{},"The campaign needed a consumer-facing experience that combined interactive customization with AI-generated outputs, while still feeling clear and responsive. The frontend had to manage several dependent steps, keep user selections across the flow, and maintain a good experience during AI processing delays.",[69,1695],{},[72,1697,185],{"id":184},[77,1699,1700],{},"I built the frontend from the UI/UX team's Figma designs as a structured multi-step flow, using Redux for centralized state management and reliable transitions between configuration, generation, review, and submission steps.",[77,1702,1703],{},"The interface was designed to handle asynchronous AI generation well by:",[105,1705,1706,1709,1712,1715],{},[108,1707,1708],{},"Keeping user progress while requests were in progress",[108,1710,1711],{},"Showing clear loading states and next-step guidance",[108,1713,1714],{},"Rendering AI-generated image and audio outputs dynamically when available",[108,1716,1717],{},"Providing error and fallback states so users could recover without restarting",[77,1719,1720],{},"This approach kept the experience stable and polished even when AI responses had variable latency.",[69,1722],{},[72,1724,217],{"id":216},[219,1726,1728],{"id":1727},"burger-configuration-flow","Burger Configuration Flow",[105,1730,1731,1734,1737],{},[108,1732,1733],{},"Multi-step ingredient selection flow for building a custom Whopper",[108,1735,1736],{},"Guided progression with state persistence across steps",[108,1738,1739],{},"Validation to ensure users completed required inputs before continuing",[219,1741,1743],{"id":1742},"ai-generated-burger-image","AI-Generated Burger Image",[105,1745,1746,1749,1752],{},[108,1747,1748],{},"Triggered image generation from selected burger ingredients",[108,1750,1751],{},"Dynamic rendering of returned AI-generated visuals in the flow",[108,1753,1754],{},"Loading and retry-friendly UI states during generation",[219,1756,1758],{"id":1757},"ai-generated-jingle","AI-Generated Jingle",[105,1760,1761,1764,1767],{},[108,1762,1763],{},"Triggered audio/jingle generation tied to the user burger concept",[108,1765,1766],{},"Playback-ready rendering of generated content in the submission journey",[108,1768,1769],{},"Graceful handling of delayed or failed responses",[219,1771,1773],{"id":1772},"submission-competition-flow","Submission & Competition Flow",[105,1775,1776,1779,1782],{},[108,1777,1778],{},"Review step for confirming generated assets and ingredient selections",[108,1780,1781],{},"Final submission flow connected to backend APIs",[108,1783,1784],{},"UX continuity across step transitions, async responses, and completion states",[69,1786],{},[72,1788,299],{"id":298},[301,1790,1793],{"title":1791,"tradeoff":1792},"Centralized state management for a multi-step AI flow","More upfront state modeling → more predictable UX across async steps",[77,1794,1795],{},"Redux kept configuration data, generation status, returned AI assets, and submission state synchronized across the full flow. This reduced UI inconsistencies and made async transitions easier to manage.",[219,1797,1799],{"id":1798},"frontend-architecture-react-redux","Frontend Architecture (React + Redux)",[105,1801,1802,1805,1808],{},[108,1803,1804],{},"Centralized state for ingredient selections, step progression, generated assets, and request status",[108,1806,1807],{},"Predictable transitions between configuration, generation, preview, and submission steps",[108,1809,1810,1811,537,1813,1815],{},"UI implemented from Figma designs using ",[124,1812,1659],{},[124,1814,150],{}," for consistent styling and layout",[219,1817,1819],{"id":1818},"async-ux-reliability","Async UX & Reliability",[105,1821,1822,1825,1828,1831],{},[108,1823,1824],{},"Loading state design for AI image/audio generation delays",[108,1826,1827],{},"Error and fallback handling for failed or delayed API responses",[108,1829,1830],{},"Dynamic rendering of generated content without breaking user progress",[108,1832,1833],{},"Responsive layout behavior for mobile and desktop campaign participation",[69,1835],{},[72,1837,367],{"id":366},[105,1839,1840,1843,1846,1849,1852,1855,1858],{},[108,1841,1842],{},"Frontend ownership of a high-visibility campaign application",[108,1844,1845],{},"Frontend implementation of product and UI/UX team designs in production-ready code",[108,1847,1848],{},"Delivery of a complex multi-step UX with centralized state management",[108,1850,1851],{},"Integration with backend APIs and AI-driven content generation systems",[108,1853,1854],{},"Strong async handling, loading states, and recovery flows",[108,1856,1857],{},"Responsive UI implementation for consumer-facing campaign experiences",[108,1859,1860],{},"Ability to build polished interaction layers on top of AI-powered workflows",[69,1862],{},[72,1864,399],{"id":398},[77,1866,1867],{},"The application delivered a polished frontend experience for a Burger King AI-driven campaign, guiding users from burger customization to AI-generated content and final competition submission in one clear flow.",[69,1869],{},{"title":404,"searchDepth":405,"depth":405,"links":1871},[1872,1873,1874,1875,1876,1882,1886,1887],{"id":74,"depth":405,"text":75},{"id":98,"depth":405,"text":99},{"id":155,"depth":405,"text":156},{"id":184,"depth":405,"text":185},{"id":216,"depth":405,"text":217,"children":1877},[1878,1879,1880,1881],{"id":1727,"depth":414,"text":1728},{"id":1742,"depth":414,"text":1743},{"id":1757,"depth":414,"text":1758},{"id":1772,"depth":414,"text":1773},{"id":298,"depth":405,"text":299,"children":1883},[1884,1885],{"id":1798,"depth":414,"text":1799},{"id":1818,"depth":414,"text":1819},{"id":366,"depth":405,"text":367},{"id":398,"depth":405,"text":399},{},"/projects/bk-million-dollar-whopper",{"title":1599,"description":404},6,"projects/bk-million-dollar-whopper","Frontend experience for an AI-powered Burger King campaign",[1895,1671,1659,150,438],"React.js","pA2D-6JbCQQbmx0-dwojmAUJZY-fMOqXYadZxEzP5Gw",{"id":1898,"title":1899,"body":1900,"description":404,"extension":426,"featured":1587,"meta":2233,"navigation":427,"path":2234,"seo":2235,"sortOrder":2236,"stem":2237,"tagline":2238,"tags":2239,"__hash__":2243},"projects/projects/bet-monks-world-cup-predictions.md","Bet.Monks World Cup Predictions",{"type":8,"value":1901,"toc":2213},[1902,1922,1925,1928,1930,1933,1936,1941,1943,1947,1981,1983,1985,1988,1990,1992,1995,2012,2015,2017,2019,2023,2031,2035,2038,2058,2062,2076,2080,2094,2098,2109,2111,2115,2118,2121,2135,2138,2140,2142,2149,2153,2167,2171,2182,2184,2186,2206,2208,2210],[11,1903,27,1905,27,1912],{"className":1904},[14,15,16,17,18,19,20,21,22,23,24,25,26],[29,1906],{"src":1907,"alt":1908,"className":1909},"/images/logos/mm.svg","MM",[1910,1911,35,36],"bet-monks-mm-logo","h-6",[11,1913,42,1915,42,1918,27],{"className":1914},[15,40,41],[44,1916,51],{"className":1917},[47,48,49,50],[44,1919,1921],{"className":1920},[55,56,57,58],"\n      Media.Monks\n    ",[61,1923],{":items":1924},"[\"Node.js\", \"NestJS\", \"TypeScript\", \"MongoDB\", \"Vue.js\", \"Quasar Framework\", \"OAuth 2.0 (Google)\", \"AWS S3\", \"Simple Queue Service (SQS)\", \"Swagger\"]",[65,1926],{":items":1927},"[{\"type\": \"image\", \"src\": \"/images/projects/betmonks/loginpage.png\", \"caption\": \"Google OAuth login screen restricting access to authorized Media.Monks employee accounts.\"}, {\"type\": \"image\", \"src\": \"/images/projects/betmonks/fmybets.png\", \"caption\": \"My Bets dashboard showing upcoming matches, submitted predictions, points, and personal ranking metrics.\"}, {\"type\": \"image\", \"src\": \"/images/projects/betmonks/smybets.png\", \"caption\": \"Detailed My Bets view with match-by-match prediction status, results, and scoring feedback.\"}, {\"type\": \"image\", \"src\": \"/images/projects/betmonks/results.png\", \"caption\": \"Tournament results page displaying match outcomes and stage progression across the competition.\"}, {\"type\": \"image\", \"src\": \"/images/projects/betmonks/ostandings.png\", \"caption\": \"Overall standings leaderboard with participant rankings, accumulated points, and tie-break ordering.\"}, {\"type\": \"image\", \"src\": \"/images/projects/betmonks/rules.png\", \"caption\": \"Rules page explaining the scoring system used to evaluate predictions fairly and consistently.\"}, {\"type\": \"image\", \"src\": \"/images/projects/betmonks/srules.png\", \"caption\": \"Scoring rules detail view highlighting priority-based point assignment for each prediction outcome.\"}]",[72,1929,75],{"id":74},[77,1931,1932],{},"Bet.Monks was an internal prediction platform built for Media.Monks during the FIFA Women's World Cup 2023. Employees could submit score predictions before kickoff, earn points based on scoring rules, and compete in a live leaderboard throughout the tournament.",[77,1934,1935],{},"I owned the application layer end to end, building both the backend and frontend that powered authentication, prediction flows, scoring, rankings, and tournament views.",[87,1937,1938],{"title":89,"type":90},[77,1939,1940],{},"This case study focuses on the application development work I delivered: backend APIs, frontend experience, data modeling, scoring logic, ranking updates, and asynchronous result processing.",[69,1942],{},[72,1944,1946],{"id":1945},"my-contribution","My Contribution",[101,1948,1949],{"role":517},[105,1950,1951,1959,1966,1969,1972,1975,1978],{},[108,1952,1953,1954,1956,1957,694],{},"Designed and implemented the backend API in ",[124,1955,939],{}," (",[124,1958,945],{},[108,1960,1961,1962,970,1964],{},"Built the frontend application in ",[124,1963,435],{},[124,1965,973],{},[108,1967,1968],{},"Implemented Google OAuth 2.0 login restricted to authorized company accounts",[108,1970,1971],{},"Designed MongoDB models for matches, predictions, users, standings, and tournament data",[108,1973,1974],{},"Built the scoring and ranking engine with deterministic rule evaluation",[108,1976,1977],{},"Implemented asynchronous processing for match result updates and leaderboard recalculation",[108,1979,1980],{},"Documented backend endpoints with Swagger for maintainability and team collaboration",[69,1982],{},[72,1984,156],{"id":155},[77,1986,1987],{},"Media.Monks needed an internal-only platform that could keep employees engaged throughout the tournament while handling a core product challenge: predictions had to be easy to submit before kickoff, but scoring and ranking also needed to remain consistent, fair, and automatically updated as official match results came in.",[69,1989],{},[72,1991,185],{"id":184},[77,1993,1994],{},"I built Bet.Monks as a full-stack web application with a modular backend and a responsive frontend experience tailored to tournament participation. The system supported the full prediction lifecycle:",[105,1996,1997,2000,2003,2006,2009],{},[108,1998,1999],{},"Secure employee authentication via Google OAuth",[108,2001,2002],{},"Pre-kickoff score submissions",[108,2004,2005],{},"Match result display and point breakdowns",[108,2007,2008],{},"Automatic ranking recalculation after final results",[108,2010,2011],{},"Tournament progress tracking across group and knockout stages",[77,2013,2014],{},"To keep the user experience responsive, match finalization and scoring recalculation were processed asynchronously instead of blocking user-facing requests.",[69,2016],{},[72,2018,217],{"id":216},[219,2020,2022],{"id":2021},"login-page","Login Page",[105,2024,2025,2028],{},[108,2026,2027],{},"Google OAuth 2.0 authentication for employee access",[108,2029,2030],{},"Internal-only access control using authorized company accounts",[219,2032,2034],{"id":2033},"my-bets-page","My Bets Page",[77,2036,2037],{},"Primary interaction surface for the tournament.",[105,2039,2040,2043,2046,2049,2052,2055],{},[108,2041,2042],{},"View upcoming matches",[108,2044,2045],{},"Submit score predictions before kickoff",[108,2047,2048],{},"See completed match results and points earned",[108,2050,2051],{},"Track total points, ranking position, and prediction count",[108,2053,2054],{},"View the most predicted score for each match",[108,2056,2057],{},"Dynamic UI updates based on match status and user activity",[219,2059,2061],{"id":2060},"results-page","Results Page",[105,2063,2064,2067,2070,2073],{},[108,2065,2066],{},"Group stage tables",[108,2068,2069],{},"Knockout bracket rounds and final standings",[108,2071,2072],{},"Match metadata (date, time, location, score)",[108,2074,2075],{},"Dynamic handling of different tournament stages",[219,2077,2079],{"id":2078},"overall-standings-page","Overall Standings Page",[105,2081,2082,2085,2088,2091],{},[108,2083,2084],{},"Live participant leaderboard",[108,2086,2087],{},"Total accumulated points and ranking positions",[108,2089,2090],{},"Tie-break ordering and comparative standings",[108,2092,2093],{},"Automatic recalculation after result processing",[219,2095,2097],{"id":2096},"rules-page","Rules Page",[105,2099,2100,2103,2106],{},[108,2101,2102],{},"Clear scoring rules for participants",[108,2104,2105],{},"Priority-based point assignment to ensure each prediction is scored once per match",[108,2107,2108],{},"Consistent rule enforcement across all users",[69,2110],{},[72,2112,2114],{"id":2113},"scoring-ranking-system","Scoring & Ranking System",[77,2116,2117],{},"The scoring engine was the core application component and a key part of the backend design.",[77,2119,2120],{},"When a match result became final, the system:",[105,2122,2123,2126,2129,2132],{},[108,2124,2125],{},"Evaluated all related predictions",[108,2127,2128],{},"Applied scoring rules using a single-priority outcome per prediction",[108,2130,2131],{},"Recalculated user totals",[108,2133,2134],{},"Updated leaderboard positions and tie-break ordering",[77,2136,2137],{},"This logic was implemented to be deterministic and repeatable, which was essential for fairness in a competitive internal platform.",[69,2139],{},[72,2141,299],{"id":298},[301,2143,2146],{"title":2144,"tradeoff":2145},"Asynchronous result processing for scoring updates","More backend coordination → faster user-facing interactions",[77,2147,2148],{},"Scoring and leaderboard recalculation were triggered asynchronously after match results were finalized. This kept the application responsive while processing many predictions and ranking updates in the background.",[219,2150,2152],{"id":2151},"backend-nestjs","Backend (NestJS)",[105,2154,2155,2158,2161,2164],{},[108,2156,2157],{},"Modular API design for authentication, predictions, matches, results, and standings",[108,2159,2160],{},"Swagger documentation for endpoint visibility and faster maintenance",[108,2162,2163],{},"MongoDB data modeling aligned with tournament entities and prediction workflows",[108,2165,2166],{},"Queue-based asynchronous processing for result finalization and scoring updates",[219,2168,2170],{"id":2169},"frontend-vuejs-quasar","Frontend (Vue.js + Quasar)",[105,2172,2173,2176,2179],{},[108,2174,2175],{},"Responsive UI for desktop and mobile participation during the tournament",[108,2177,2178],{},"Tournament-focused screens for bets, results, standings, and rules",[108,2180,2181],{},"Dynamic state-driven updates based on match lifecycle and user predictions",[69,2183],{},[72,2185,367],{"id":366},[105,2187,2188,2191,2194,2197,2200,2203],{},[108,2189,2190],{},"Full-stack ownership of an application used during a live event timeline",[108,2192,2193],{},"Implementation of competitive scoring and ranking logic",[108,2195,2196],{},"Secure internal authentication with OAuth 2.0",[108,2198,2199],{},"Asynchronous backend workflows for data processing",[108,2201,2202],{},"Modular backend architecture and API documentation",[108,2204,2205],{},"Interactive frontend development for real-time tournament participation",[69,2207],{},[72,2209,399],{"id":398},[77,2211,2212],{},"Bet.Monks gave Media.Monks employees a dedicated internal platform to participate in a tournament-long prediction competition, combining a polished user experience with reliable scoring and leaderboard updates throughout the FIFA Women's World Cup 2023.",{"title":404,"searchDepth":405,"depth":405,"links":2214},[2215,2216,2217,2218,2219,2226,2227,2231,2232],{"id":74,"depth":405,"text":75},{"id":1945,"depth":405,"text":1946},{"id":155,"depth":405,"text":156},{"id":184,"depth":405,"text":185},{"id":216,"depth":405,"text":217,"children":2220},[2221,2222,2223,2224,2225],{"id":2021,"depth":414,"text":2022},{"id":2033,"depth":414,"text":2034},{"id":2060,"depth":414,"text":2061},{"id":2078,"depth":414,"text":2079},{"id":2096,"depth":414,"text":2097},{"id":2113,"depth":405,"text":2114},{"id":298,"depth":405,"text":299,"children":2228},[2229,2230],{"id":2151,"depth":414,"text":2152},{"id":2169,"depth":414,"text":2170},{"id":366,"depth":405,"text":367},{"id":398,"depth":405,"text":399},{},"/projects/bet-monks-world-cup-predictions",{"title":1899,"description":404},7,"projects/bet-monks-world-cup-predictions","Full-stack internal prediction platform for Media.Monks employees during the FIFA Women's World Cup 2023",[536,939,945,942,435,1265,2240,558,2241,2242],"OAuth 2.0 (Google)","Simple Queue Service (SQS)","Swagger","T2CuRIbPOVkSCaxciJse2badAwM5L9nwlGIIqj6Wd90",{"id":2245,"title":2246,"body":2247,"description":404,"extension":426,"featured":1587,"meta":2559,"navigation":427,"path":2560,"seo":2561,"sortOrder":2236,"stem":2562,"tagline":2563,"tags":2564,"__hash__":2565},"projects/projects/twitchcon-photo-system.md","TwitchCon - The Emotifier",{"type":8,"value":2248,"toc":2540},[2249,2267,2270,2273,2278,2280,2282,2285,2288,2291,2293,2295,2326,2328,2330,2333,2350,2353,2355,2357,2360,2363,2386,2389,2391,2393,2397,2408,2412,2423,2427,2435,2439,2447,2449,2451,2458,2462,2476,2480,2491,2495,2506,2508,2510,2530,2532,2534,2537],[11,2250,27,2252,27,2257],{"className":2251},[14,15,16,17,18,19,20,21,22,23,24,25,26],[29,2253],{"src":2254,"alt":2255,"className":2256},"/images/logos/twitch.png","Twitch",[34,35,36],[11,2258,42,2260,42,2263,27],{"className":2259},[15,40,41],[44,2261,51],{"className":2262},[47,48,49,50],[44,2264,2266],{"className":2265},[55,56,57,58],"\n      TwitchCon 2022 - San Diego, USA\n    ",[61,2268],{":items":2269},"[\"Node.js\", \"Vue.js\", \"NestJS\", \"Quasar Framework\", \"MongoDB\", \"SendGrid\", \"AWS S3\", \"JavaScript/TypeScript\"]",[65,2271],{":items":2272},"[{\"type\": \"image\", \"src\": \"/images/projects/twitchcon/stand.jpeg\", \"caption\": \"Main Emotifier display wall showing approved photos during TwitchCon event.\"}, {\"type\": \"image\", \"src\": \"/images/projects/twitchcon/IMG_6305.jpg\", \"caption\": \"Built photo booth system managing captures, accessories, and processing pipeline.\"}, {\"type\": \"video\", \"src\": \"/images/projects/twitchcon/visu.mov\", \"poster\": \"/images/projects/twitchcon/poster.png\", \"caption\": \"Touchscreen photo management interface used to customize and send images.\"}]",[87,2274,2275],{"title":475,"type":90},[77,2276,2277],{},"Developed for TwitchCon 2022, this system powered an interactive event experience where attendees could take photos, apply digital accessories, and instantly view their images on large event screens while receiving personalized copies via email.",[69,2279],{},[72,2281,75],{"id":74},[77,2283,2284],{},"The Emotifier was an interactive stand at TwitchCon 2022 in San Diego, USA. Visitors entered the experience, took a photo using a tablet, applied digital accessories (such as glasses and hats), and instantly saw their image displayed on large event screens. They also received a personalized copy of their photo via email.",[77,2286,2287],{},"The system required real-time image processing, moderation control, cloud storage, and live display integration during a high-traffic public event.",[77,2289,2290],{},"I was responsible for building the backend system and the CMS used to manage and moderate the experience.",[69,2292],{},[72,2294,99],{"id":98},[101,2296,2298],{"role":2297},"Backend & CMS Developer",[105,2299,2300,2303,2308,2311,2314,2317,2320,2323],{},[108,2301,2302],{},"Built the backend APIs for photo ingestion, moderation, and event screen delivery",[108,2304,2305,2306],{},"Received images from the tablet application and stored them in ",[124,2307,558],{},[108,2309,2310],{},"Saved participant and photo data in the database",[108,2312,2313],{},"Implemented moderation logic (approve/reject) before public display",[108,2315,2316],{},"Sent approval-based email flows for participants",[108,2318,2319],{},"Built the CMS moderation dashboard for event managers",[108,2321,2322],{},"Enabled managers to review submissions, approve/reject photos, and monitor activity",[108,2324,2325],{},"Ensured only approved images reached the live event screens",[69,2327],{},[72,2329,156],{"id":155},[77,2331,2332],{},"The event needed a reliable system that could:",[105,2334,2335,2338,2341,2344,2347],{},[108,2336,2337],{},"Handle live photo submissions",[108,2339,2340],{},"Display approved images instantly on large screens",[108,2342,2343],{},"Send personalized emails automatically",[108,2345,2346],{},"Prevent inappropriate content from being shown publicly",[108,2348,2349],{},"Remain stable during high visitor traffic",[77,2351,2352],{},"Because this was a public live event, failures or delays would immediately impact the experience.",[69,2354],{},[72,2356,185],{"id":184},[77,2358,2359],{},"I built a backend system that connected the tablet application, cloud storage, moderation dashboard, email system, and live display panels.",[77,2361,2362],{},"The workflow worked as follows:",[1367,2364,2365,2368,2371,2374,2377,2380,2383],{},[108,2366,2367],{},"User takes photo on tablet",[108,2369,2370],{},"Tablet sends image to backend API",[108,2372,2373],{},"Image is stored in AWS S3",[108,2375,2376],{},"Photo appears in CMS moderation dashboard",[108,2378,2379],{},"Admin approves or rejects the photo",[108,2381,2382],{},"Approved photos are pushed to the event display screens",[108,2384,2385],{},"User receives an automated email with their image",[77,2387,2388],{},"This created a controlled, real-time content pipeline for a live event environment.",[69,2390],{},[72,2392,217],{"id":216},[219,2394,2396],{"id":2395},"live-photo-processing","Live Photo Processing",[105,2398,2399,2402,2405],{},[108,2400,2401],{},"API-based image ingestion from external application",[108,2403,2404],{},"Secure cloud storage via AWS S3",[108,2406,2407],{},"Real-time moderation workflow",[219,2409,2411],{"id":2410},"moderation-content-control","Moderation & Content Control",[105,2413,2414,2417,2420],{},[108,2415,2416],{},"Admin approval / rejection system",[108,2418,2419],{},"Prevention of inappropriate public display",[108,2421,2422],{},"Separate email flows for approved and rejected photos",[219,2424,2426],{"id":2425},"event-screen-integration","Event Screen Integration",[105,2428,2429,2432],{},[108,2430,2431],{},"Automatic push of approved images to large display panels",[108,2433,2434],{},"Controlled content flow to public screens",[219,2436,2438],{"id":2437},"email-automation","Email Automation",[105,2440,2441,2444],{},[108,2442,2443],{},"Automated personalized email delivery",[108,2445,2446],{},"Approval-based email logic",[69,2448],{},[72,2450,299],{"id":298},[301,2452,2455],{"title":2453,"tradeoff":2454},"Moderated publish flow before live screen display","Extra moderation step → safer public content and more reliable live operations",[77,2456,2457],{},"I implemented a moderation-first pipeline where photos were reviewed in CMS before being published to event screens. This reduced the risk of inappropriate content and kept operations controlled during peak traffic.",[219,2459,2461],{"id":2460},"backend-system-design","Backend System Design",[105,2463,2464,2467,2470,2473],{},[108,2465,2466],{},"REST API design for external application consumption",[108,2468,2469],{},"Image upload handling and cloud storage integration",[108,2471,2472],{},"Moderation state management",[108,2474,2475],{},"Reliable email automation workflows",[219,2477,2479],{"id":2478},"cms-development","CMS Development",[105,2481,2482,2485,2488],{},[108,2483,2484],{},"Built custom dashboard for live moderation",[108,2486,2487],{},"Clear UI for fast decision-making during event traffic",[108,2489,2490],{},"Real-time updates for image status changes",[219,2492,2494],{"id":2493},"event-reliability","Event Reliability",[105,2496,2497,2500,2503],{},[108,2498,2499],{},"Built for live production environment",[108,2501,2502],{},"Stable under high user interaction",[108,2504,2505],{},"Designed to prevent inappropriate public content",[69,2507],{},[72,2509,367],{"id":366},[105,2511,2512,2515,2518,2521,2524,2527],{},[108,2513,2514],{},"Backend ownership for live event system",[108,2516,2517],{},"Real-time media handling and moderation",[108,2519,2520],{},"Cloud storage integration (AWS S3)",[108,2522,2523],{},"API design for external client applications",[108,2525,2526],{},"Email automation workflows",[108,2528,2529],{},"Production reliability in high-traffic environments",[69,2531],{},[72,2533,399],{"id":398},[77,2535,2536],{},"The Emotifier successfully powered a live interactive experience at TwitchCon 2022, allowing visitors to instantly see their customized images on public screens while receiving personalized digital copies.",[77,2538,2539],{},"My backend and CMS ensured content control, automation, and operational stability throughout the event.",{"title":404,"searchDepth":405,"depth":405,"links":2541},[2542,2543,2544,2545,2546,2552,2557,2558],{"id":74,"depth":405,"text":75},{"id":98,"depth":405,"text":99},{"id":155,"depth":405,"text":156},{"id":184,"depth":405,"text":185},{"id":216,"depth":405,"text":217,"children":2547},[2548,2549,2550,2551],{"id":2395,"depth":414,"text":2396},{"id":2410,"depth":414,"text":2411},{"id":2425,"depth":414,"text":2426},{"id":2437,"depth":414,"text":2438},{"id":298,"depth":405,"text":299,"children":2553},[2554,2555,2556],{"id":2460,"depth":414,"text":2461},{"id":2478,"depth":414,"text":2479},{"id":2493,"depth":414,"text":2494},{"id":366,"depth":405,"text":367},{"id":398,"depth":405,"text":399},{},"/projects/twitchcon-photo-system",{"title":2246,"description":404},"projects/twitchcon-photo-system","Real-time photo management platform built for a large-scale live event",[536,435,939,942,1265,1033,558,438],"hQK1ln3eyLwFXqL6S6W_NSpW-r9WWJy2h2Ntg_HS5RE",{"id":2567,"title":2568,"body":2569,"description":404,"extension":426,"featured":1587,"meta":2899,"navigation":427,"path":2900,"seo":2901,"sortOrder":2902,"stem":2903,"tagline":2904,"tags":2905,"__hash__":2908},"projects/projects/whatsapp-clone.md","WhatsApp Clone",{"type":8,"value":2570,"toc":2881},[2571,2574,2577,2579,2581,2584,2587,2592,2594,2596,2600,2603,2624,2626,2633,2637,2721,2724,2728,2731,2733,2735,2739,2759,2763,2777,2781,2801,2805,2822,2824,2828,2837,2839,2841,2864,2866,2870],[61,2572],{":items":2573},"[\"Nuxt 4\", \"Vue 3\", \"NestJS\", \"MongoDB\", \"WebSockets\", \"TypeScript\", \"Tailwind CSS\", \"Pinia\", \"Docker\", \"Playwright\", \"Vitest\", \"Jest\"]",[65,2575],{":items":2576},"[{\"type\": \"image\", \"src\": \"/images/projects/whatsapp/group-chat-dark.png\", \"caption\": \"Group chat with multiple members in dark mode - real-time messaging with message status indicators.\"}, {\"type\": \"image\", \"src\": \"/images/projects/whatsapp/1on1-chat-dark.png\", \"caption\": \"Private 1:1 conversation in dark mode - end-to-end encrypted messaging with delivery and read receipts.\"}, {\"type\": \"video\", \"src\": \"/images/projects/whatsapp/1on1-chat-dark.mp4\", \"poster\": \"/images/projects/whatsapp/1on1-chat-dark-cover.png\", \"caption\": \"Real-time 1:1 encrypted messaging - two separate browser sessions exchanging messages instantly via WebSockets.\"}, {\"type\": \"video\", \"src\": \"/images/projects/whatsapp/group-chat-light.mp4\", \"poster\": \"/images/projects/whatsapp/group-chat-light-cover.png\", \"caption\": \"Group chat with 4 members - real-time message delivery, reply, forward and delete across simultaneous sessions.\"}, {\"type\": \"image\", \"src\": \"/images/projects/whatsapp/group-chat-light.png\", \"caption\": \"Group chat in light mode - full dark and light theme support.\"}, {\"type\": \"image\", \"src\": \"/images/projects/whatsapp/group-chat-info-light.png\", \"caption\": \"Group info panel - member management, admin controls, and group settings.\"}]",[69,2578],{},[72,2580,75],{"id":74},[77,2582,2583],{},"A full-stack WhatsApp Web clone built as a showcase project. The app replicates the core experience of WhatsApp Web - real-time encrypted messaging between users, group chats, message status indicators, and a UI that mirrors the original application.",[77,2585,2586],{},"Built to demonstrate full-stack development, WebSocket communication, client-side cryptography, and professional testing practices including unit, integration, and end-to-end tests with a CI pipeline.",[87,2588,2589],{"title":89,"type":90},[77,2590,2591],{},"This is a personal showcase project built entirely from scratch to demonstrate technical depth across the full stack - from cryptography and real-time communication to automated testing and CI pipelines.",[69,2593],{},[72,2595,299],{"id":298},[219,2597,2599],{"id":2598},"end-to-end-encryption","End-to-End Encryption",[77,2601,2602],{},"The most technically demanding part of this project. Messages are encrypted client-side using the Web Crypto API before being sent to the server. The server never has access to plaintext message content.",[301,2604,2607],{"title":2605,"tradeoff":2606},"Client-side E2EE using Web Crypto API","Implementation complexity → true end-to-end encryption without trusting the server",[105,2608,2609,2612,2615,2618,2621],{},[108,2610,2611],{},"RSA-OAEP key pairs generated per user on registration",[108,2613,2614],{},"Per-conversation AES-256 keys encrypted with each member's public key",[108,2616,2617],{},"Private keys encrypted with a user-defined passphrase using AES-GCM",[108,2619,2620],{},"Keys stored locally in IndexedDB - never sent to the server in plaintext",[108,2622,2623],{},"Recovery codes generated as alternative passphrase recovery options",[219,2625,1483],{"id":1482},[77,2627,2628,2629,2632],{},"WebSocket connections managed by a custom ",[124,2630,2631],{},"WsClientManager"," service on the backend. Handles connection lifecycle, online/offline status, message delivery, and group broadcast without any third-party real-time service.",[219,2634,2636],{"id":2635},"testing-161-tests","Testing - 161 Tests",[2638,2639,2640,2659],"table",{},[2641,2642,2643],"thead",{},[2644,2645,2646,2650,2653,2656],"tr",{},[2647,2648,2649],"th",{},"Suite",[2647,2651,2652],{},"Tests",[2647,2654,2655],{},"Tool",[2647,2657,2658],{},"What's covered",[2660,2661,2662,2677,2691,2705],"tbody",{},[2644,2663,2664,2668,2671,2674],{},[2665,2666,2667],"td",{},"Backend unit",[2665,2669,2670],{},"52",[2665,2672,2673],{},"Jest",[2665,2675,2676],{},"Services, guards, strategies, WebSocket manager",[2644,2678,2679,2682,2685,2688],{},[2665,2680,2681],{},"Frontend unit/nuxt",[2665,2683,2684],{},"69",[2665,2686,2687],{},"Vitest",[2665,2689,2690],{},"Stores, composables, E2EE crypto",[2644,2692,2693,2696,2699,2702],{},[2665,2694,2695],{},"Frontend E2E",[2665,2697,2698],{},"40",[2665,2700,2701],{},"Playwright",[2665,2703,2704],{},"Auth, chat, messaging, block/unblock, profile, reply, forward, sign out",[2644,2706,2707,2712,2717,2719],{},[2665,2708,2709],{},[497,2710,2711],{},"Total",[2665,2713,2714],{},[497,2715,2716],{},"161",[2665,2718],{},[2665,2720],{},[77,2722,2723],{},"The most notable tests are the multi-browser E2E tests - two and three simultaneous Playwright browser contexts exchanging real-time encrypted messages, verifying the full stack from registration through encryption and delivery.",[219,2725,2727],{"id":2726},"ci-pipeline","CI Pipeline",[77,2729,2730],{},"GitHub Actions runs all three test suites on every push. The E2E job starts MongoDB, the NestJS backend, and the Nuxt frontend via Docker Compose, then runs 40 Playwright tests in Chromium. Branch protection rules block merges unless all 161 tests pass.",[69,2732],{},[72,2734,217],{"id":216},[219,2736,2738],{"id":2737},"messaging","Messaging",[105,2740,2741,2744,2747,2750,2753,2756],{},[108,2742,2743],{},"Real-time 1:1 and group messaging via WebSockets",[108,2745,2746],{},"Message status indicators: Sent, Received, Read",[108,2748,2749],{},"Reply to messages with quoted preview",[108,2751,2752],{},"Forward messages to other chats",[108,2754,2755],{},"Delete messages",[108,2757,2758],{},"Message info (delivery and read receipts)",[219,2760,2762],{"id":2761},"security-encryption","Security & Encryption",[105,2764,2765,2768,2771,2774],{},[108,2766,2767],{},"End-to-end encryption using the Web Crypto API",[108,2769,2770],{},"JWT authentication with HTTP-only cookies",[108,2772,2773],{},"Session token validation on every authenticated request",[108,2775,2776],{},"Rate limiting on all API endpoints",[219,2778,2780],{"id":2779},"users-chats","Users & Chats",[105,2782,2783,2786,2789,2792,2795,2798],{},[108,2784,2785],{},"Registration with passphrase setup and recovery codes",[108,2787,2788],{},"Profile management: name and about",[108,2790,2791],{},"Block and unblock users",[108,2793,2794],{},"Online/offline status with last seen timestamps",[108,2796,2797],{},"1:1 and group chats with admin management",[108,2799,2800],{},"Add and remove group members",[219,2802,2804],{"id":2803},"ui","UI",[105,2806,2807,2810,2813,2816,2819],{},[108,2808,2809],{},"Dark and light mode",[108,2811,2812],{},"Emoji picker",[108,2814,2815],{},"Message search within chats",[108,2817,2818],{},"Typing indicators",[108,2820,2821],{},"Unread message counts",[69,2823],{},[72,2825,2827],{"id":2826},"architecture","Architecture",[2829,2830,2835],"pre",{"className":2831,"code":2833,"language":2834},[2832],"language-text","Client (Browser)\n├── Nuxt 4 + Vue 3 + Pinia\n├── Web Crypto API - E2EE\n├── IndexedDB - private key storage\n└── WebSocket client\n        │ HTTP / WebSocket\nNestJS Backend\n├── REST API (auth, users, chats, messages)\n├── WebSocket Gateway (real-time events)\n├── JWT + Cookie authentication\n└── Rate limiting\n        │ Mongoose\nMongoDB\n","text",[124,2836,2833],{"__ignoreMap":404},[69,2838],{},[72,2840,367],{"id":366},[105,2842,2843,2846,2849,2852,2855,2858,2861],{},[108,2844,2845],{},"Full-stack TypeScript development across NestJS and Nuxt 4",[108,2847,2848],{},"Client-side cryptography using the Web Crypto API - RSA-OAEP + AES-256",[108,2850,2851],{},"Real-time WebSocket communication with custom connection management",[108,2853,2854],{},"Comprehensive testing: 161 tests across unit, integration, and E2E",[108,2856,2857],{},"Multi-browser E2E testing with Playwright - simultaneous sessions verifying real-time encrypted delivery",[108,2859,2860],{},"CI pipeline with Docker Compose running the full stack in GitHub Actions",[108,2862,2863],{},"Professional engineering practices: branch protection, conventional commits, documented architecture",[69,2865],{},[72,2867,2869],{"id":2868},"links","Links",[105,2871,2872],{},[108,2873,2874],{},[2875,2876,2880],"a",{"href":2877,"rel":2878},"https://github.com/vortizz/whatsapp",[2879],"nofollow","GitHub Repository",{"title":404,"searchDepth":405,"depth":405,"links":2882},[2883,2884,2890,2896,2897,2898],{"id":74,"depth":405,"text":75},{"id":298,"depth":405,"text":299,"children":2885},[2886,2887,2888,2889],{"id":2598,"depth":414,"text":2599},{"id":1482,"depth":414,"text":1483},{"id":2635,"depth":414,"text":2636},{"id":2726,"depth":414,"text":2727},{"id":216,"depth":405,"text":217,"children":2891},[2892,2893,2894,2895],{"id":2737,"depth":414,"text":2738},{"id":2761,"depth":414,"text":2762},{"id":2779,"depth":414,"text":2780},{"id":2803,"depth":414,"text":2804},{"id":2826,"depth":405,"text":2827},{"id":366,"depth":405,"text":367},{"id":2868,"depth":405,"text":2869},{},"/projects/whatsapp-clone",{"title":2568,"description":404},8,"projects/whatsapp-clone","Full-stack real-time messaging app with end-to-end encryption, group chats, and 161 automated tests",[435,436,939,942,2906,945,150,2907,2701],"WebSockets","Docker","Sy2df_MOfOYZsjKvWR9hKdKiDEH89jMDxh-xZ8Vi_Jw",1778044211393]