[{"data":1,"prerenderedAt":93},["ShallowReactive",2],{"project:accunix:en":3,"i-tabler:language":79,"i-tabler:eye":83,"i-tabler:database":85,"i-tabler:messages":87,"i-tabler:components":89,"i-tabler:shield-check":91},{"slug":4,"name":5,"summary":6,"role":7,"updateNote":-1,"meta":8,"coverUrl":-1,"lang":27,"isFallback":28,"featuredRank":29,"badge":30,"lastActivity":22,"tapeNumber":31,"blocks":32},"accunix","AccuNix Social CRM Platform","An enterprise admin platform that brings LINE, Facebook and Instagram marketing and member data together, covering social operations, campaigns, member management and performance tracking for multiple business clients.","Frontend Engineer",{"origin":9,"categories":10,"tags":12,"released":22,"company":23,"period":24},"company",[11],"frontend",[13,14,15,16,17,18,19,20,21],"vue","typescript","pinia","element-plus","vue-i18n","axios","pusher","tailwindcss","scss","2025-03-01","accuhit",{"from":25,"to":26},"2025-03","2026-03","en",false,1,null,3,[33,60,67,73],{"type":34,"items":35},"features",[36,40,44,48,52,56],{"title":37,"description":38,"icon":39},"Multilingual architecture","Maintained and improved the Chinese–English i18n architecture so dynamic content and UI components switch languages correctly.","tabler:language",{"title":41,"description":42,"icon":43},"Live campaign form preview","Designed and built a live preview for campaign forms, cutting setup mistakes and back-and-forth.","tabler:eye",{"title":45,"description":46,"icon":47},"Client-side caching and state","Built caching and state management with Pinia to avoid duplicate API requests, with support for manual refresh.","tabler:database",{"title":49,"description":50,"icon":51},"Chat refactor","Refactored the chat’s data structures and state flow after changes to staff roles and the API spec, keeping messages correctly linked to users and leaving room to grow.","tabler:messages",{"title":53,"description":54,"icon":55},"Reusable custom components","Wrapped and extended Element Plus components into reusable custom ones.","tabler:components",{"title":57,"description":58,"icon":59},"Stronger typing","Steadily removed any from the codebase and enforced TypeScript types in all new features, improving maintainability and long-term stability.","tabler:shield-check",{"type":61,"title":62,"problem":63,"solution":65},"challenge","Removing any without breaking features",[64],"The legacy code was full of any. It had to pass type checking without destabilizing existing features.",[66],"Started with small, fairly self-contained pages. After each fix I ran lint to confirm it passed and nothing else broke, then moved on to the next.",{"type":61,"title":68,"problem":69,"solution":71},"Improving and extending existing components",[70],"Existing components needed improvements and new capabilities, but many pages relied on them, so nothing could break.",[72],"Constrained component interfaces with types and left existing behavior alone unless fixing a bug. Components hold no side effects. Anything that has to run before or after is handed to the parent through emit.",{"type":61,"title":74,"problem":75,"solution":77},"Extending large features with no handover docs",[76],"Some features were large and hard to change, and no handover documentation had been left behind.",[78],"I first used AI to walk through the files and find the key parts, the problems and a place to start. New features were added without breaking existing code. For rewrites, I agreed on data formats with the backend or converted them into a frontend-friendly shape, keeping the changes as small as possible.",{"left":80,"top":80,"width":81,"height":81,"rotate":80,"vFlip":28,"hFlip":28,"body":82},0,24,"\u003Cg fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\">\u003Cpath d=\"M9 6.371C9 10.789 6.761 13 4 13m0-6.629h7\"\u002F>\u003Cpath d=\"M5 9c0 2.144 2.252 3.908 6 4m1 7l4-9l4 9m-.9-2h-6.2M6.694 3l.793.582\"\u002F>\u003C\u002Fg>",{"left":80,"top":80,"width":81,"height":81,"rotate":80,"vFlip":28,"hFlip":28,"body":84},"\u003Cg fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\">\u003Cpath d=\"M10 12a2 2 0 1 0 4 0a2 2 0 0 0-4 0\"\u002F>\u003Cpath d=\"M21 12q-3.6 6-9 6t-9-6q3.6-6 9-6t9 6\"\u002F>\u003C\u002Fg>",{"left":80,"top":80,"width":81,"height":81,"rotate":80,"vFlip":28,"hFlip":28,"body":86},"\u003Cg fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\">\u003Cpath d=\"M4 6a8 3 0 1 0 16 0A8 3 0 1 0 4 6\"\u002F>\u003Cpath d=\"M4 6v6a8 3 0 0 0 16 0V6\"\u002F>\u003Cpath d=\"M4 12v6a8 3 0 0 0 16 0v-6\"\u002F>\u003C\u002Fg>",{"left":80,"top":80,"width":81,"height":81,"rotate":80,"vFlip":28,"hFlip":28,"body":88},"\u003Cpath fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"m21 14l-3-3h-7a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h9a1 1 0 0 1 1 1zm-7 1v2a1 1 0 0 1-1 1H6l-3 3V11a1 1 0 0 1 1-1h2\"\u002F>",{"left":80,"top":80,"width":81,"height":81,"rotate":80,"vFlip":28,"hFlip":28,"body":90},"\u003Cpath fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"m3 12l3 3l3-3l-3-3zm12 0l3 3l3-3l-3-3zM9 6l3 3l3-3l-3-3zm0 12l3 3l3-3l-3-3z\"\u002F>",{"left":80,"top":80,"width":81,"height":81,"rotate":80,"vFlip":28,"hFlip":28,"body":92},"\u003Cpath fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M11.46 20.846A12 12 0 0 1 3.5 6A12 12 0 0 0 12 3a12 12 0 0 0 8.5 3a12 12 0 0 1-.09 7.06M15 19l2 2l4-4\"\u002F>",1793948871848]