[{"data":1,"prerenderedAt":71},["ShallowReactive",2],{"project:accuhit-campaign-hub:ja":3,"i-tabler:route":59,"i-tabler:database":63,"i-tabler:shield-check":65,"i-tabler:components":67,"i-tabler:login":69},{"slug":4,"name":5,"summary":6,"role":7,"updateNote":-1,"meta":8,"coverUrl":-1,"lang":23,"isFallback":24,"featuredRank":25,"badge":26,"lastActivity":18,"tapeNumber":27,"blocks":28},"accuhit-campaign-hub","キャンペーンページ統合プラットフォーム","キャンペーンページごとに HTML と JS で作られていた複数のクライアント向けプロジェクトを、Vue と Vuetify の単一アプリに統合。","フロントエンドエンジニア",{"origin":9,"categories":10,"tags":12,"released":18,"company":19,"period":20},"company",[11],"frontend",[13,14,15,16,17],"vue","typescript","vuetify","pinia","liff","2025-11-01","accuhit",{"from":21,"to":22},"2025-11","2026-01","ja",false,3,null,5,[29,52],{"type":30,"items":31},"features",[32,36,40,44,48],{"title":33,"description":34,"icon":35},"パスによるプロジェクト統合","複数のキャンペーンページを一つのアプリにまとめ、パスで切り分けた。集客時は、URL に付与されるパラメータなど、各パスに必要なデータだけを扱えばよい。","tabler:route",{"title":37,"description":38,"icon":39},"設定の単一情報源（SSOT）","プロジェクトごとに異なる UI と設定を一か所に集約し、型は設定そのものから導出。記述ミスと保守コストを削減。","tabler:database",{"title":41,"description":42,"icon":43},"型で守られた設定マップ","各所に散らばっていた switch 文を一つのマップに置き換えた。新しい項目の設定を書き漏らすと型チェックでエラーになり、未知の値はデフォルトにフォールバックする。","tabler:shield-check",{"title":45,"description":46,"icon":47},"UI の統一","Vuetify で各キャンペーンページの画面を統一し、ばらばらだったスタイルを置き換えた。","tabler:components",{"title":49,"description":50,"icon":51},"LINE ログインと例外処理","LIFF で LINE ログインを連携し、読み込みの流れやログイン切れなどの例外を一括で処理。","tabler:login",{"type":53,"title":54,"problem":55,"solution":57},"challenge","ページごとにばらばらで、一つの変更が何か所にも波及",[56],"キャンペーンページはそれぞれ HTML と JS で書かれ、UI も統一されていなかった。同じ種類の設定が各所に散らばり、ルールが食い違いやすく、一か所を直すと何か所も連動して修正が必要だった。",[58],"Vue の単一アプリに統合し、パスでプロジェクトを切り分けた。設定は TypeScript で制約した単一情報源にまとめ、書き漏れや誤りは型チェックで見つかるようにした。",{"left":60,"top":60,"width":61,"height":61,"rotate":60,"vFlip":24,"hFlip":24,"body":62},0,24,"\u003Cpath fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M3 19a2 2 0 1 0 4 0a2 2 0 0 0-4 0M19 7a2 2 0 1 0 0-4a2 2 0 0 0 0 4m-8 12h5.5a3.5 3.5 0 0 0 0-7h-8a3.5 3.5 0 0 1 0-7H13\"\u002F>",{"left":60,"top":60,"width":61,"height":61,"rotate":60,"vFlip":24,"hFlip":24,"body":64},"\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":60,"top":60,"width":61,"height":61,"rotate":60,"vFlip":24,"hFlip":24,"body":66},"\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>",{"left":60,"top":60,"width":61,"height":61,"rotate":60,"vFlip":24,"hFlip":24,"body":68},"\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":60,"top":60,"width":61,"height":61,"rotate":60,"vFlip":24,"hFlip":24,"body":70},"\u003Cg fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\">\u003Cpath d=\"M15 8V6a2 2 0 0 0-2-2H6a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h7a2 2 0 0 0 2-2v-2\"\u002F>\u003Cpath d=\"M21 12H8l3-3m0 6l-3-3\"\u002F>\u003C\u002Fg>",1793955809426]