@dssp/dkpi 1.0.0-alpha.86 → 1.0.0-alpha.88
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/_index.html +4 -4
- package/assets/helps/index.md +1 -1
- package/assets/manifest.json +2 -2
- package/dist-client/components/kpi-boxplot-chart.d.ts +7 -0
- package/dist-client/components/kpi-boxplot-chart.js +20 -5
- package/dist-client/components/kpi-boxplot-chart.js.map +1 -1
- package/dist-client/components/kpi-lookup-chart.js +1 -17
- package/dist-client/components/kpi-lookup-chart.js.map +1 -1
- package/dist-client/components/kpi-radar-chart.js +2 -2
- package/dist-client/components/kpi-radar-chart.js.map +1 -1
- package/dist-client/components/kpi-single-boxplot-chart.d.ts +7 -0
- package/dist-client/components/kpi-single-boxplot-chart.js +27 -26
- package/dist-client/components/kpi-single-boxplot-chart.js.map +1 -1
- package/dist-client/components/kpi-step-lookup-chart.d.ts +33 -0
- package/dist-client/components/kpi-step-lookup-chart.js +181 -0
- package/dist-client/components/kpi-step-lookup-chart.js.map +1 -0
- package/dist-client/components/kpi-trend-chart.js +2 -12
- package/dist-client/components/kpi-trend-chart.js.map +1 -1
- package/dist-client/google-map/common-google-map.js +5 -1
- package/dist-client/google-map/common-google-map.js.map +1 -1
- package/dist-client/pages/kpi-admin/dssp-kpi-overview.d.ts +2 -1
- package/dist-client/pages/kpi-admin/dssp-kpi-overview.js +24 -9
- package/dist-client/pages/kpi-admin/dssp-kpi-overview.js.map +1 -1
- package/dist-client/pages/kpi-admin/kpi-system-guide.js +311 -158
- package/dist-client/pages/kpi-admin/kpi-system-guide.js.map +1 -1
- package/dist-client/pages/kpi-dashboard/components/kpi-left-panel.d.ts +6 -0
- package/dist-client/pages/kpi-dashboard/components/kpi-left-panel.js +110 -50
- package/dist-client/pages/kpi-dashboard/components/kpi-left-panel.js.map +1 -1
- package/dist-client/pages/kpi-dashboard/components/kpi-map-panel.d.ts +0 -9
- package/dist-client/pages/kpi-dashboard/components/kpi-map-panel.js +6 -91
- package/dist-client/pages/kpi-dashboard/components/kpi-map-panel.js.map +1 -1
- package/dist-client/pages/kpi-dashboard/components/kpi-region-popup.d.ts +2 -0
- package/dist-client/pages/kpi-dashboard/components/kpi-region-popup.js +148 -68
- package/dist-client/pages/kpi-dashboard/components/kpi-region-popup.js.map +1 -1
- package/dist-client/pages/kpi-dashboard/kpi-dashboard-map.js +85 -41
- package/dist-client/pages/kpi-dashboard/kpi-dashboard-map.js.map +1 -1
- package/dist-client/pages/kpi-dashboard/kpi-dashboard.js +15 -7
- package/dist-client/pages/kpi-dashboard/kpi-dashboard.js.map +1 -1
- package/dist-client/pages/project-complete-tabs/pc-tab1-plan.js +16 -25
- package/dist-client/pages/project-complete-tabs/pc-tab1-plan.js.map +1 -1
- package/dist-client/pages/project-complete-tabs/pc-tab2-rating.js +9 -13
- package/dist-client/pages/project-complete-tabs/pc-tab2-rating.js.map +1 -1
- package/dist-client/pages/project-complete-tabs/pc-tab4-monthly.js +9 -15
- package/dist-client/pages/project-complete-tabs/pc-tab4-monthly.js.map +1 -1
- package/dist-client/pages/sv-project-complete.js +7 -11
- package/dist-client/pages/sv-project-complete.js.map +1 -1
- package/dist-client/pages/sv-project-completed-list.js +4 -4
- package/dist-client/pages/sv-project-completed-list.js.map +1 -1
- package/dist-client/pages/sv-project-detail.d.ts +1 -0
- package/dist-client/pages/sv-project-detail.js +94 -82
- package/dist-client/pages/sv-project-detail.js.map +1 -1
- package/dist-client/pages/sv-project-list.js +15 -12
- package/dist-client/pages/sv-project-list.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-client/viewparts/menu-tools.js +17 -3
- package/dist-client/viewparts/menu-tools.js.map +1 -1
- package/dist-server/service/kpi-stat/kpi-stat-query.js +20 -19
- package/dist-server/service/kpi-stat/kpi-stat-query.js.map +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/openapi/unstable.yaml +2 -2
- package/package.json +3 -3
- package/schema.graphql +20 -0
- package/translations/ko.json +2 -2
- package/views/auth-page.html +4 -4
- package/views/public/home.html +5 -5
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"kpi-system-guide.js","sourceRoot":"","sources":["../../../client/pages/kpi-admin/kpi-system-guide.ts"],"names":[],"mappings":";AAAA,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,KAAK,CAAA;AACxC,OAAO,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAA;AACxD,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AACzC,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AACjD,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AACzC,OAAO,GAAG,MAAM,aAAa,CAAA;AAE7B;;;;;GAKG;AAEH,MAAM,cAAc,GAAG,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkCzB,CAAA;AAED,MAAM,iBAAiB,GAAqD;IAC1E,MAAM,EAAE,EAAE,KAAK,EAAE,gBAAgB,EAAE,KAAK,EAAE,MAAM,EAAE;IAClD,OAAO,EAAE,EAAE,KAAK,EAAE,iBAAiB,EAAE,KAAK,EAAE,SAAS,EAAE;IACvD,MAAM,EAAE,EAAE,KAAK,EAAE,cAAc,EAAE,KAAK,EAAE,SAAS,EAAE;IACnD,MAAM,EAAE,EAAE,KAAK,EAAE,gBAAgB,EAAE,KAAK,EAAE,SAAS,EAAE;CACtD,CAAA;AAED,oBAAoB;AACpB,MAAM,oBAAoB,GAA2B;IACnD,WAAW,EAAE,0BAA0B;IACvC,aAAa,EAAE,wBAAwB;IACvC,cAAc,EAAE,2BAA2B;IAC3C,OAAO,EAAE,sBAAsB;CAChC,CAAA;AAED,MAAM,iBAAiB,GAAmE;IACxF,QAAQ,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE;IAC1D,QAAQ,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE;IAC3D,UAAU,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE;IAC5D,SAAS,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE;CAC5D,CAAA;AAGM,IAAM,cAAc,GAApB,MAAM,cAAe,SAAQ,QAAQ;IAArC;;QA8LI,YAAO,GAAU,EAAE,CAAA;QACnB,YAAO,GAAU,EAAE,CAAA;QACnB,YAAO,GAAG,IAAI,CAAA;QACd,mBAAc,GAAQ,IAAI,CAAA;IA2QrC,CAAC;IAzQC,IAAI,OAAO;QACT,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,CAAA;IACjC,CAAC;IAED,KAAK,CAAC,YAAY;;QAChB,IAAI,CAAC;YACH,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,MAAM,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,cAAc,EAAE,CAAC,CAAA;YAC9D,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,IAAI,EAAE,CAAA;YACjC,IAAI,CAAC,OAAO,GAAG,CAAA,MAAA,IAAI,CAAC,UAAU,0CAAE,KAAK,KAAI,EAAE,CAAA;QAC7C,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,OAAO,CAAC,KAAK,CAAC,iCAAiC,EAAE,CAAC,CAAC,CAAA;QACrD,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,OAAO,GAAG,KAAK,CAAA;QACtB,CAAC;IACH,CAAC;IAED,yCAAyC;IACjC,cAAc,CAAC,UAAkB;;QACvC,MAAM,IAAI,GAAa,EAAE,CAAA;QACzB,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YAC7B,IAAI,CAAC,CAAC,MAAM,KAAK,KAAK;gBAAE,SAAQ;YAChC,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC,EAAE,CAAC;gBAC/B,IAAI,CAAC,CAAC,MAAM,KAAK,KAAK;oBAAE,SAAQ;gBAChC,IAAI,MAAA,CAAC,CAAC,OAAO,0CAAE,QAAQ,CAAC,IAAI,UAAU,GAAG,CAAC,EAAE,CAAC;oBAC3C,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAA;gBACnB,CAAC;YACH,CAAC;QACH,CAAC;QACD,OAAO,IAAI,CAAA;IACb,CAAC;IAEO,UAAU,CAAC,IAAS,EAAE,IAAsB;QAClD,IAAI,CAAC,cAAc,mCAAQ,IAAI,KAAE,KAAK,EAAE,IAAI,GAAE,CAAA;IAChD,CAAC;IAEO,WAAW;QACjB,IAAI,CAAC,cAAc,GAAG,IAAI,CAAA;IAC5B,CAAC;IAED,MAAM;QACJ,IAAI,IAAI,CAAC,OAAO;YAAE,OAAO,IAAI,CAAA,uEAAuE,CAAA;QAEpG,gBAAgB;QAChB,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,KAAK,CAAC,CAAC,CAAA;QAEpG,OAAO,IAAI,CAAA;;;;;;;;;;UAUL,MAAM,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAChD,IAAI,CAAA,8DAA8D,CAAC,CAAC,KAAK,gBAAgB,CAC1F;;;UAGC,MAAM,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAChD,IAAI,CAAA,8BAA8B,CAAC,CAAC,IAAI,oCAAoC,CAAC,CAAC,KAAK,gBAAgB,CACpG;;;;;;;;qCAQ4B,GAAG,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,WAAW,EAAE,WAAW,EAAE,OAAO,EAAE,6BAA6B,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAE,YAAY,EAAE,EAAE,KAAK,CAAC;;qCAElK,IAAI,CAAC,OAAO;aACpC,MAAM,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,KAAK,CAAC;aACtC,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC;aACtD,IAAI,CAAC,KAAK,CAAC;;;;UAId,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE;YACnE,MAAM,UAAU,GAAG,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,KAAK,CAAC,CAAA;YACxE,OAAO,IAAI,CAAA;;6CAEwB,GAAG,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,EAAE,KAAK,CAAC;uCACrC,CAAC,CAAC,IAAI;8CACC,CAAC,CAAC,MAAM;;;kBAGpC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAA,0BAA0B,CAAC,CAAC,OAAO,QAAQ,CAAC,CAAC,CAAC,EAAE;;oBAE9D,UAAU,CAAC,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE;gBAC1B,MAAM,EAAE,GAAG,iBAAiB,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,SAAS,IAAI,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,CAAA;gBAC3F,MAAM,EAAE,GAAG,iBAAiB,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,SAAS,IAAI,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,CAAA;gBACtG,OAAO,IAAI,CAAA;mDACoB,GAAG,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,EAAE,KAAK,CAAC;8CACpC,CAAC,CAAC,IAAI;iDACH,CAAC,CAAC,OAAO,IAAI,GAAG;;4DAEL,EAAE,CAAC,KAAK;4DACR,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,KAAK;;;qBAG1D,CAAA;YACH,CAAC,CAAC;;;;WAIT,CAAA;QACH,CAAC,CAAC;;;;QAIF,CAAC,GAAG,EAAE;YACN,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO;iBAC7B,MAAM,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC;iBAC5B,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;iBAC5D,MAAM,CAAC,CAAC,EAAE,KAAK,EAAO,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAA;YAC/C,OAAO,IAAI,CAAA;;oDAEiC,WAAW,CAAC,MAAM;;gBAEtD,WAAW,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAO,EAAE,EAAE,CAAC,IAAI,CAAA;kDACT,GAAG,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,EAAE,QAAQ,CAAC;6CACvC,CAAC,CAAC,IAAI;;sBAE7B,CAAC,CAAC,IAAI,IAAI,GAAG;sBACb,CAAC,CAAC,WAAW,KAAK,UAAU,IAAI,CAAC,CAAC,MAAM;gBACxC,CAAC,CAAC,oBAAoB,CAAC,CAAC,CAAC,MAAM,CAAC;oBAC9B,CAAC,CAAC,IAAI,CAAA,YAAY,oBAAoB,CAAC,CAAC,CAAC,MAAM,CAAC;iFACS,CAAC,CAAQ,EAAE,EAAE,CAAC,CAAC,CAAC,eAAe,EAAE;6BACrF,CAAC,CAAC,MAAM,QAAQ;oBACrB,CAAC,CAAC,CAAC,CAAC,MAAM;gBACZ,CAAC,CAAC,CAAC,CAAC,WAAW,IAAI,QAAQ;;+CAEF,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC;;eAEhD,CAAC;;;SAGP,CAAA;QACH,CAAC,CAAC,EAAE;;;;;;;yEAO+D,IAAI,CAAC,MAAM;;;;yEAIX,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,KAAK,CAAC,CAAC,MAAM;;;;yEAI1D,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,IAAI,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,MAAM;;;;yEAI1F,IAAI,CAAC,MAAM,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,KAAK,UAAU,CAAC,CAAC,MAAM;;;;;;;QAO3H,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,kBAAkB,EAAE,CAAC,CAAC,CAAC,OAAO;KAC5D,CAAA;IACH,CAAC;IAEO,kBAAkB;QACxB,MAAM,CAAC,GAAG,IAAI,CAAC,cAAc,CAAA;QAC7B,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,KAAK,KAAK,CAAA;QAE/B,OAAO,IAAI,CAAA;0CAC2B,GAAG,EAAE,CAAC,IAAI,CAAC,WAAW,EAAE;wCAC1B,CAAC,CAAQ,EAAE,EAAE,CAAC,CAAC,CAAC,eAAe,EAAE;;oBAErD,CAAC,CAAC,IAAI;iDACuB,GAAG,EAAE,CAAC,IAAI,CAAC,WAAW,EAAE;;;YAG7D,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAA;;;0CAGU,CAAC,CAAC,WAAW;;WAE5C,CAAC,CAAC,CAAC,EAAE;;YAEJ,KAAK,CAAC,CAAC,CAAC,IAAI,CAAA;cACV,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAA;;2CAEa,CAAC,CAAC,OAAO;aACvC,CAAC,CAAC,CAAC,EAAE;;cAEJ,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAA;;;yDAGyB,CAAC,iBAAiB,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,IAAI,MAAM;oBAC3F,CAAC,iBAAiB,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,SAAS;;;aAGlE,CAAC,CAAC,CAAC,EAAE;;cAEJ,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAA;;;yDAGyB,CAAC,iBAAiB,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,IAAI,MAAM;oBAC3F,CAAC,iBAAiB,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,IAAI,EAAE,IAAI,CAAC,iBAAiB,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,SAAS;;;aAGvH,CAAC,CAAC,CAAC,EAAE;;cAEJ,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAA;;;4CAGe,CAAC,CAAC,MAAM;;aAEvC,CAAC,CAAC,CAAC,EAAE;;;gDAG8B,GAAG,EAAE,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAA,CAAC,CAAC;;;;gDAItD,GAAG,EAAE,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAA,CAAC,CAAC;;;;WAIxF,CAAC,CAAC,CAAC,IAAI,CAAA;;;0CAGwB,CAAC,CAAC,IAAI,IAAI,GAAG;;;;0CAIb,CAAC,CAAC,WAAW,IAAI,QAAQ;;;;;kBAKjD,CAAC,CAAC,MAAM,IAAI,oBAAoB,CAAC,CAAC,CAAC,MAAM,CAAC;YAC1C,CAAC,CAAC,IAAI,CAAA,YAAY,oBAAoB,CAAC,CAAC,CAAC,MAAM,CAAC,2DAA2D,CAAC,CAAC,MAAM,QAAQ;YAC3H,CAAC,CAAC,CAAC,CAAC,MAAM,IAAI,KAAK;;;;;;gBAMrB,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CACtC,IAAI,CAAA,qEAAqE,GAAG,QAAQ,CACrF;gBACC,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA,sDAAsD,CAAC,CAAC,CAAC,EAAE;;;;gDAI1E,GAAG,EAAE,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,QAAQ,CAAC,uBAAuB,CAAC,CAAA,CAAC,CAAC;;;;WAIpG;;;KAGN,CAAA;IACH,CAAC;;AA1cM,qBAAM,GAAG;IACd,eAAe;IACf,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAwLF;CACF,AA3LY,CA2LZ;AAEQ;IAAR,KAAK,EAAE;;+CAAoB;AACnB;IAAR,KAAK,EAAE;;+CAAoB;AACnB;IAAR,KAAK,EAAE;;+CAAe;AACd;IAAR,KAAK,EAAE;;sDAA2B;AAjMxB,cAAc;IAD1B,aAAa,CAAC,kBAAkB,CAAC;GACrB,cAAc,CA4c1B","sourcesContent":["import { html, css, nothing } from 'lit'\nimport { customElement, state } from 'lit/decorators.js'\nimport { PageView } from '@operato/shell'\nimport { ScrollbarStyles } from '@operato/styles'\nimport { client } from '@operato/graphql'\nimport { navigate } from '@operato/shell'\nimport gql from 'graphql-tag'\n\n/**\n * KPI 시스템 종합 안내 페이지\n *\n * DB에서 실시간으로 KPI 계층, 산식, 메트릭, scoreType/valueType 등을 조회하여\n * 시스템 전체 구조를 한눈에 파악할 수 있는 매뉴얼 겸 라이브 대시보드.\n */\n\nconst GET_KPI_SYSTEM = gql`\n query {\n kpiRoot: kpisLevel1 {\n id\n name\n description\n formula\n weight\n scoreType\n valueType\n active\n kpis: children {\n id\n name\n description\n formula\n weight\n scoreType\n valueType\n active\n grades\n }\n }\n kpiMetrics {\n items {\n id\n name\n unit\n source\n collectType\n active\n }\n }\n }\n`\n\nconst SCORE_TYPE_LABELS: Record<string, { label: string; color: string }> = {\n DIRECT: { label: 'DIRECT (변환 없음)', color: '#888' },\n FORMULA: { label: 'FORMULA (수식 변환)', color: '#9c27b0' },\n LOOKUP: { label: 'LOOKUP (등급표)', color: '#1976d2' },\n CUSTOM: { label: 'CUSTOM (2D 룩업)', color: '#e65100' }\n}\n\n/** 외부 시스템 URL 매핑 */\nconst EXTERNAL_SYSTEM_URLS: Record<string, string> = {\n '세움터(EAIS)': 'https://cloud.eais.go.kr',\n '키스콘(KISCON)': 'https://www.kiscon.net',\n '올바로(Allbaro)': 'https://www.allbaro.or.kr',\n '전자카드제': 'https://www.cw.or.kr'\n}\n\nconst VALUE_TYPE_LABELS: Record<string, { label: string; color: string; icon: string }> = {\n MEASURED: { label: '외부 수집', color: '#1976d2', icon: '📡' },\n ASSESSED: { label: '감리자 평가', color: '#2e7d32', icon: '✍️' },\n CALCULATED: { label: '산식 계산', color: '#9c27b0', icon: '🔢' },\n COMPOSITE: { label: '복합 입력', color: '#e65100', icon: '🔀' }\n}\n\n@customElement('kpi-system-guide')\nexport class KpiSystemGuide extends PageView {\n static styles = [\n ScrollbarStyles,\n css`\n :host {\n display: block;\n overflow-y: auto;\n background: #f5f6fa;\n padding: 24px;\n }\n\n .page-title {\n font-size: 1.5rem;\n font-weight: 800;\n color: #1a237e;\n margin-bottom: 4px;\n }\n .page-subtitle {\n font-size: 0.9rem;\n color: #666;\n margin-bottom: 24px;\n }\n\n /* 계층 트리 */\n .tree-section {\n background: #fff;\n border-radius: 12px;\n padding: 24px;\n margin-bottom: 24px;\n box-shadow: 0 2px 8px rgba(0,0,0,0.06);\n }\n .section-title {\n font-size: 1.1rem;\n font-weight: 700;\n color: #333;\n margin-bottom: 16px;\n display: flex;\n align-items: center;\n gap: 8px;\n }\n\n /* Z 루트 */\n .z-node {\n background: linear-gradient(135deg, #1a237e, #283593);\n color: #fff;\n border-radius: 10px;\n padding: 16px 20px;\n margin-bottom: 16px;\n cursor: pointer;\n }\n .z-node:hover { opacity: 0.95; }\n .z-name { font-size: 1.1rem; font-weight: 700; }\n .z-formula { font-size: 0.8rem; opacity: 0.8; margin-top: 4px; font-family: monospace; }\n\n /* Y 그룹 */\n .y-group {\n margin-bottom: 16px;\n border: 1px solid #e0e0e0;\n border-radius: 10px;\n overflow: hidden;\n }\n .y-header {\n background: linear-gradient(135deg, #0c4da2, #1565c0);\n color: #fff;\n padding: 12px 16px;\n display: flex;\n justify-content: space-between;\n align-items: center;\n cursor: pointer;\n }\n .y-header:hover { opacity: 0.95; }\n .y-name { font-weight: 700; font-size: 1rem; }\n .y-weight {\n background: rgba(255,255,255,0.2);\n padding: 2px 10px;\n border-radius: 12px;\n font-size: 0.8rem;\n }\n .y-body { padding: 12px; }\n .y-formula {\n font-size: 0.8rem;\n color: #555;\n background: #f8f9fa;\n padding: 8px 12px;\n border-radius: 6px;\n font-family: monospace;\n margin-bottom: 10px;\n word-break: break-all;\n }\n\n /* X 카드 */\n .x-cards { display: flex; flex-wrap: wrap; gap: 8px; }\n .x-card {\n flex: 1 1 220px;\n background: #fff;\n border: 1px solid #e8e8e8;\n border-radius: 8px;\n padding: 10px 14px;\n cursor: pointer;\n transition: all 0.2s;\n min-width: 200px;\n }\n .x-card:hover {\n border-color: #1976d2;\n box-shadow: 0 2px 8px rgba(25,118,210,0.15);\n }\n .x-name { font-weight: 600; font-size: 0.85rem; color: #333; margin-bottom: 4px; }\n .x-formula {\n font-size: 0.75rem; color: #888; font-family: monospace;\n white-space: nowrap; overflow: hidden; text-overflow: ellipsis;\n margin-bottom: 6px;\n }\n .x-badges { display: flex; gap: 4px; flex-wrap: wrap; }\n .badge {\n font-size: 0.65rem;\n padding: 2px 6px;\n border-radius: 4px;\n font-weight: 600;\n }\n .badge-score { background: #e3f2fd; color: #1565c0; }\n .badge-value { background: #e8f5e9; color: #2e7d32; }\n\n /* 메트릭 섹션 */\n .metrics-grid {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));\n gap: 10px;\n }\n .metric-card {\n background: #fff;\n border: 1px solid #e8e8e8;\n border-radius: 8px;\n padding: 10px 14px;\n transition: all 0.2s;\n }\n .metric-card:hover { border-color: #4caf50; }\n .metric-name { font-weight: 600; font-size: 0.85rem; color: #333; }\n .metric-unit { font-size: 0.75rem; color: #888; }\n .metric-used {\n font-size: 0.7rem; color: #1976d2; margin-top: 4px;\n }\n\n /* 팝업 */\n .popup-overlay {\n position: fixed; top: 0; left: 0; right: 0; bottom: 0;\n background: rgba(0,0,0,0.3); z-index: 1000;\n display: flex; align-items: center; justify-content: center;\n }\n .popup-box {\n background: #fff; border-radius: 12px; padding: 24px;\n min-width: 400px; max-width: 600px; max-height: 80vh;\n overflow-y: auto; box-shadow: 0 8px 32px rgba(0,0,0,0.2);\n }\n .popup-title {\n font-size: 1.1rem; font-weight: 700; color: #1a237e;\n margin-bottom: 16px; display: flex; justify-content: space-between;\n }\n .popup-close {\n background: none; border: none; font-size: 1.2rem;\n cursor: pointer; color: #999;\n }\n .popup-row {\n display: flex; gap: 8px; margin-bottom: 8px;\n font-size: 0.85rem;\n }\n .popup-label { color: #888; min-width: 100px; flex-shrink: 0; }\n .popup-value { color: #333; font-weight: 500; }\n .popup-formula {\n background: #f5f5f5; padding: 10px; border-radius: 6px;\n font-family: monospace; font-size: 0.85rem; margin: 8px 0;\n word-break: break-all;\n }\n .popup-link {\n color: #1976d2; cursor: pointer; text-decoration: underline;\n font-size: 0.85rem;\n }\n .popup-link:hover { color: #0d47a1; }\n\n /* 범례 */\n .legend {\n display: flex; gap: 16px; flex-wrap: wrap;\n margin-bottom: 20px; padding: 12px 16px;\n background: #fff; border-radius: 8px;\n box-shadow: 0 1px 4px rgba(0,0,0,0.04);\n }\n .legend-group { display: flex; align-items: center; gap: 4px; font-size: 0.8rem; }\n .legend-title { font-weight: 700; color: #555; margin-right: 4px; }\n `\n ]\n\n @state() kpiRoot: any[] = []\n @state() metrics: any[] = []\n @state() loading = true\n @state() selectedDetail: any = null\n\n get context() {\n return { title: 'KPI 시스템 가이드' }\n }\n\n async firstUpdated() {\n try {\n const { data } = await client.query({ query: GET_KPI_SYSTEM })\n this.kpiRoot = data.kpiRoot || []\n this.metrics = data.kpiMetrics?.items || []\n } catch (e) {\n console.error('Failed to load KPI system data:', e)\n } finally {\n this.loading = false\n }\n }\n\n /** 메트릭이 사용되는 KPI 목록 — 비활성 KPI/Y 그룹 제외 */\n private getMetricUsage(metricName: string): string[] {\n const used: string[] = []\n for (const y of this.kpiRoot) {\n if (y.active === false) continue\n for (const x of (y.kpis || [])) {\n if (x.active === false) continue\n if (x.formula?.includes(`[${metricName}]`)) {\n used.push(x.name)\n }\n }\n }\n return used\n }\n\n private showDetail(item: any, type: 'kpi' | 'metric') {\n this.selectedDetail = { ...item, _type: type }\n }\n\n private closeDetail() {\n this.selectedDetail = null\n }\n\n render() {\n if (this.loading) return html`<div style=\"padding:40px;text-align:center;color:#888;\">로딩 중...</div>`\n\n // Z 노드 (루트의 부모)\n const allX = this.kpiRoot.flatMap((y: any) => (y.kpis || []).filter((x: any) => x.active !== false))\n\n return html`\n <div class=\"page-title\">KPI 시스템 가이드</div>\n <div class=\"page-subtitle\">\n 건축현장 성과평가 KPI 체계의 전체 구조, 산식, 메트릭 연관관계를 확인합니다.\n 모든 정보는 DB에서 실시간 조회됩니다.\n </div>\n\n <!-- 범례 -->\n <div class=\"legend\">\n <span class=\"legend-title\">Score 산정:</span>\n ${Object.entries(SCORE_TYPE_LABELS).map(([, v]) =>\n html`<span class=\"legend-group\"><span class=\"badge badge-score\">${v.label}</span></span>`\n )}\n <span style=\"width:16px\"></span>\n <span class=\"legend-title\">Value 획득:</span>\n ${Object.entries(VALUE_TYPE_LABELS).map(([, v]) =>\n html`<span class=\"legend-group\">${v.icon} <span class=\"badge badge-value\">${v.label}</span></span>`\n )}\n </div>\n\n <!-- 1. KPI 계층 구조 -->\n <div class=\"tree-section\">\n <div class=\"section-title\">📊 KPI 계층 구조 (Z → Y → X)</div>\n\n <!-- Z 루트 -->\n <div class=\"z-node\" @click=${() => this.showDetail({ name: 'Z. 전체스코어', description: 'Y1~Y6 가중합', formula: 'Y1×w1 + Y2×w2 + ... + Y6×w6', scoreType: 'DIRECT', valueType: 'CALCULATED' }, 'kpi')}>\n <div class=\"z-name\">Z. 전체스코어</div>\n <div class=\"z-formula\">= ${this.kpiRoot\n .filter((y: any) => y.active !== false)\n .map((y: any) => `${y.name.split('.')[0]}×${y.weight}`)\n .join(' + ')}</div>\n </div>\n\n <!-- Y 그룹들 (비활성 Y 제외) -->\n ${this.kpiRoot.filter((y: any) => y.active !== false).map((y: any) => {\n const activeKpis = (y.kpis || []).filter((x: any) => x.active !== false)\n return html`\n <div class=\"y-group\">\n <div class=\"y-header\" @click=${() => this.showDetail(y, 'kpi')}>\n <span class=\"y-name\">${y.name}</span>\n <span class=\"y-weight\">가중치: ${y.weight}</span>\n </div>\n <div class=\"y-body\">\n ${y.formula ? html`<div class=\"y-formula\">${y.formula}</div>` : ''}\n <div class=\"x-cards\">\n ${activeKpis.map((x: any) => {\n const st = SCORE_TYPE_LABELS[x.scoreType] || { label: x.scoreType || '미설정', color: '#999' }\n const vt = VALUE_TYPE_LABELS[x.valueType] || { label: x.valueType || '미설정', color: '#999', icon: '?' }\n return html`\n <div class=\"x-card\" @click=${() => this.showDetail(x, 'kpi')}>\n <div class=\"x-name\">${x.name}</div>\n <div class=\"x-formula\">${x.formula || '—'}</div>\n <div class=\"x-badges\">\n <span class=\"badge badge-score\">${st.label}</span>\n <span class=\"badge badge-value\">${vt.icon} ${vt.label}</span>\n </div>\n </div>\n `\n })}\n </div>\n </div>\n </div>\n `\n })}\n </div>\n\n <!-- 2. 원천 메트릭 — active=true 이면서 활성 KPI formula 에 실제 참조되는 것만 -->\n ${(() => {\n const usedMetrics = this.metrics\n .filter((m: any) => m.active)\n .map((m: any) => ({ m, usage: this.getMetricUsage(m.name) }))\n .filter(({ usage }: any) => usage.length > 0)\n return html`\n <div class=\"tree-section\">\n <div class=\"section-title\">📋 원천 메트릭 (${usedMetrics.length}개 사용)</div>\n <div class=\"metrics-grid\">\n ${usedMetrics.map(({ m, usage }: any) => html`\n <div class=\"metric-card\" @click=${() => this.showDetail(m, 'metric')}>\n <div class=\"metric-name\">${m.name}</div>\n <div class=\"metric-unit\">\n ${m.unit || '—'} ·\n ${m.collectType === 'EXTERNAL' && m.source\n ? EXTERNAL_SYSTEM_URLS[m.source]\n ? html`<a href=\"${EXTERNAL_SYSTEM_URLS[m.source]}\" target=\"_blank\" rel=\"noopener\"\n style=\"color:#1976d2;text-decoration:none;\" @click=${(e: Event) => e.stopPropagation()}\n >${m.source} ↗</a>`\n : m.source\n : m.collectType || 'MANUAL'}\n </div>\n <div class=\"metric-used\">→ ${usage.join(', ')}</div>\n </div>\n `)}\n </div>\n </div>\n `\n })()}\n\n <!-- 3. 시스템 요약 -->\n <div class=\"tree-section\">\n <div class=\"section-title\">📈 시스템 요약</div>\n <div style=\"display:flex;gap:16px;flex-wrap:wrap;\">\n <div style=\"flex:1;min-width:200px;background:#e3f2fd;padding:16px;border-radius:8px;text-align:center;\">\n <div style=\"font-size:2rem;font-weight:800;color:#1565c0;\">${allX.length}</div>\n <div style=\"font-size:0.85rem;color:#555;\">X-level 지표</div>\n </div>\n <div style=\"flex:1;min-width:200px;background:#e8f5e9;padding:16px;border-radius:8px;text-align:center;\">\n <div style=\"font-size:2rem;font-weight:800;color:#2e7d32;\">${this.kpiRoot.filter((y: any) => y.active !== false).length}</div>\n <div style=\"font-size:0.85rem;color:#555;\">Y-level 영역</div>\n </div>\n <div style=\"flex:1;min-width:200px;background:#fff3e0;padding:16px;border-radius:8px;text-align:center;\">\n <div style=\"font-size:2rem;font-weight:800;color:#e65100;\">${this.metrics.filter((m: any) => m.active && this.getMetricUsage(m.name).length > 0).length}</div>\n <div style=\"font-size:0.85rem;color:#555;\">원천 메트릭</div>\n </div>\n <div style=\"flex:1;min-width:200px;background:#fce4ec;padding:16px;border-radius:8px;text-align:center;\">\n <div style=\"font-size:2rem;font-weight:800;color:#c62828;\">${allX.filter((x: any) => x.valueType === 'ASSESSED').length}</div>\n <div style=\"font-size:0.85rem;color:#555;\">감리자 평가 항목</div>\n </div>\n </div>\n </div>\n\n <!-- 팝업 -->\n ${this.selectedDetail ? this._renderDetailPopup() : nothing}\n `\n }\n\n private _renderDetailPopup() {\n const d = this.selectedDetail\n const isKpi = d._type === 'kpi'\n\n return html`\n <div class=\"popup-overlay\" @click=${() => this.closeDetail()}>\n <div class=\"popup-box\" @click=${(e: Event) => e.stopPropagation()}>\n <div class=\"popup-title\">\n <span>${d.name}</span>\n <button class=\"popup-close\" @click=${() => this.closeDetail()}>×</button>\n </div>\n\n ${d.description ? html`\n <div class=\"popup-row\">\n <span class=\"popup-label\">설명</span>\n <span class=\"popup-value\">${d.description}</span>\n </div>\n ` : ''}\n\n ${isKpi ? html`\n ${d.formula ? html`\n <div class=\"popup-row\"><span class=\"popup-label\">산식</span></div>\n <div class=\"popup-formula\">${d.formula}</div>\n ` : ''}\n\n ${d.scoreType ? html`\n <div class=\"popup-row\">\n <span class=\"popup-label\">Score 산정</span>\n <span class=\"popup-value\" style=\"color:${(SCORE_TYPE_LABELS[d.scoreType] || {}).color || '#333'}\">\n ${(SCORE_TYPE_LABELS[d.scoreType] || {}).label || d.scoreType}\n </span>\n </div>\n ` : ''}\n\n ${d.valueType ? html`\n <div class=\"popup-row\">\n <span class=\"popup-label\">Value 획득</span>\n <span class=\"popup-value\" style=\"color:${(VALUE_TYPE_LABELS[d.valueType] || {}).color || '#333'}\">\n ${(VALUE_TYPE_LABELS[d.valueType] || {}).icon || ''} ${(VALUE_TYPE_LABELS[d.valueType] || {}).label || d.valueType}\n </span>\n </div>\n ` : ''}\n\n ${d.weight ? html`\n <div class=\"popup-row\">\n <span class=\"popup-label\">가중치</span>\n <span class=\"popup-value\">${d.weight}</span>\n </div>\n ` : ''}\n\n <div style=\"margin-top:16px;padding-top:12px;border-top:1px solid #eee;\">\n <span class=\"popup-link\" @click=${() => { this.closeDetail(); navigate('kpi-overview') }}>\n → KPI 개요에서 보기\n </span>\n \n <span class=\"popup-link\" @click=${() => { this.closeDetail(); navigate('kpi-admin') }}>\n → KPI 관리에서 편집\n </span>\n </div>\n ` : html`\n <div class=\"popup-row\">\n <span class=\"popup-label\">단위</span>\n <span class=\"popup-value\">${d.unit || '—'}</span>\n </div>\n <div class=\"popup-row\">\n <span class=\"popup-label\">수집 방식</span>\n <span class=\"popup-value\">${d.collectType || 'MANUAL'}</span>\n </div>\n <div class=\"popup-row\">\n <span class=\"popup-label\">데이터 소스</span>\n <span class=\"popup-value\">\n ${d.source && EXTERNAL_SYSTEM_URLS[d.source]\n ? html`<a href=\"${EXTERNAL_SYSTEM_URLS[d.source]}\" target=\"_blank\" rel=\"noopener\" style=\"color:#1976d2;\">${d.source} ↗</a>`\n : d.source || '미설정'}\n </span>\n </div>\n\n <div style=\"margin-top:12px;\">\n <div class=\"popup-label\" style=\"margin-bottom:6px;\">사용처:</div>\n ${this.getMetricUsage(d.name).map(kpi =>\n html`<div style=\"font-size:0.85rem;color:#1976d2;margin-bottom:2px;\">· ${kpi}</div>`\n )}\n ${this.getMetricUsage(d.name).length === 0 ? html`<div style=\"color:#ccc;font-size:0.85rem;\">미사용</div>` : ''}\n </div>\n\n <div style=\"margin-top:16px;padding-top:12px;border-top:1px solid #eee;\">\n <span class=\"popup-link\" @click=${() => { this.closeDetail(); navigate('kpi-metric-value-list') }}>\n → 메트릭 값 관리\n </span>\n </div>\n `}\n </div>\n </div>\n `\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"kpi-system-guide.js","sourceRoot":"","sources":["../../../client/pages/kpi-admin/kpi-system-guide.ts"],"names":[],"mappings":"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","sourcesContent":["import { html, css, nothing } from 'lit'\nimport { customElement, state } from 'lit/decorators.js'\nimport { PageView } from '@operato/shell'\nimport { ScrollbarStyles } from '@operato/styles'\nimport { client } from '@operato/graphql'\nimport { navigate } from '@operato/shell'\nimport gql from 'graphql-tag'\n\n/**\n * KPI 시스템 종합 안내 페이지\n *\n * DB에서 실시간으로 KPI 계층, 산식, 메트릭, scoreType/valueType 등을 조회하여\n * 시스템 전체 구조를 한눈에 파악할 수 있는 매뉴얼 겸 라이브 대시보드.\n */\n\nconst GET_KPI_SYSTEM = gql`\n query {\n kpiRoot: kpisLevel1 {\n id\n name\n description\n formula\n weight\n scoreType\n valueType\n active\n kpis: children {\n id\n name\n description\n formula\n weight\n scoreType\n valueType\n active\n grades\n }\n }\n kpiMetrics {\n items {\n id\n name\n unit\n source\n collectType\n active\n }\n }\n }\n`\n\nconst SCORE_TYPE_LABELS: Record<string, { label: string; color: string }> = {\n DIRECT: { label: 'DIRECT (변환 없음)', color: '#888' },\n FORMULA: { label: 'FORMULA (수식 변환)', color: '#9c27b0' },\n LOOKUP: { label: 'LOOKUP (등급표)', color: '#1976d2' },\n CUSTOM: { label: 'CUSTOM (2D 룩업)', color: '#e65100' }\n}\n\n/** 외부 시스템 URL 매핑 */\nconst EXTERNAL_SYSTEM_URLS: Record<string, string> = {\n '세움터(EAIS)': 'https://cloud.eais.go.kr',\n '키스콘(KISCON)': 'https://www.kiscon.net',\n '올바로(Allbaro)': 'https://www.allbaro.or.kr',\n 전자카드제: 'https://www.cw.or.kr'\n}\n\nconst VALUE_TYPE_LABELS: Record<string, { label: string; color: string; icon: string }> = {\n MEASURED: { label: '외부 수집', color: '#1976d2', icon: '📡' },\n ASSESSED: { label: '감리자 평가', color: '#2e7d32', icon: '✍️' },\n CALCULATED: { label: '산식 계산', color: '#9c27b0', icon: '🔢' },\n COMPOSITE: { label: '복합 입력', color: '#e65100', icon: '🔀' }\n}\n\n@customElement('kpi-system-guide')\nexport class KpiSystemGuide extends PageView {\n static styles = [\n ScrollbarStyles,\n css`\n :host {\n display: block;\n overflow-y: auto;\n background: #f5f6fa;\n padding: 24px;\n }\n\n .page-title {\n font-size: 1.5rem;\n font-weight: 800;\n color: #1a237e;\n margin-bottom: 4px;\n }\n .page-subtitle {\n font-size: 0.9rem;\n color: #666;\n margin-bottom: 24px;\n }\n\n /* 계층 트리 */\n .tree-section {\n background: #fff;\n border-radius: 12px;\n padding: 24px;\n margin-bottom: 24px;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);\n }\n .section-title {\n font-size: 1.1rem;\n font-weight: 700;\n color: #333;\n margin-bottom: 16px;\n display: flex;\n align-items: center;\n gap: 8px;\n }\n\n /* Z 루트 */\n .z-node {\n background: linear-gradient(135deg, #1a237e, #283593);\n color: #fff;\n border-radius: 10px;\n padding: 16px 20px;\n margin-bottom: 16px;\n cursor: pointer;\n }\n .z-node:hover {\n opacity: 0.95;\n }\n .z-name {\n font-size: 1.1rem;\n font-weight: 700;\n }\n .z-formula {\n font-size: 0.8rem;\n opacity: 0.8;\n margin-top: 4px;\n font-family: monospace;\n }\n\n /* Y 그룹 */\n .y-group {\n margin-bottom: 16px;\n border: 1px solid #e0e0e0;\n border-radius: 10px;\n overflow: hidden;\n }\n .y-header {\n background: linear-gradient(135deg, #0c4da2, #1565c0);\n color: #fff;\n padding: 12px 16px;\n display: flex;\n justify-content: space-between;\n align-items: center;\n cursor: pointer;\n }\n .y-header:hover {\n opacity: 0.95;\n }\n .y-name {\n font-weight: 700;\n font-size: 1rem;\n }\n .y-weight {\n background: rgba(255, 255, 255, 0.2);\n padding: 2px 10px;\n border-radius: 12px;\n font-size: 0.8rem;\n }\n .y-body {\n padding: 12px;\n }\n .y-formula {\n font-size: 0.8rem;\n color: #555;\n background: #f8f9fa;\n padding: 8px 12px;\n border-radius: 6px;\n font-family: monospace;\n margin-bottom: 10px;\n word-break: break-all;\n }\n\n /* X 카드 */\n .x-cards {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n }\n .x-card {\n flex: 1 1 220px;\n background: #fff;\n border: 1px solid #e8e8e8;\n border-radius: 8px;\n padding: 10px 14px;\n cursor: pointer;\n transition: all 0.2s;\n min-width: 200px;\n }\n .x-card:hover {\n border-color: #1976d2;\n box-shadow: 0 2px 8px rgba(25, 118, 210, 0.15);\n }\n .x-name {\n font-weight: 600;\n font-size: 0.85rem;\n color: #333;\n margin-bottom: 4px;\n }\n .x-formula {\n font-size: 0.75rem;\n color: #888;\n font-family: monospace;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n margin-bottom: 6px;\n }\n .x-badges {\n display: flex;\n gap: 4px;\n flex-wrap: wrap;\n }\n .badge {\n font-size: 0.65rem;\n padding: 2px 6px;\n border-radius: 4px;\n font-weight: 600;\n }\n .badge-score {\n background: #e3f2fd;\n color: #1565c0;\n }\n .badge-value {\n background: #e8f5e9;\n color: #2e7d32;\n }\n\n /* 메트릭 섹션 */\n .metrics-grid {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));\n gap: 10px;\n }\n .metric-card {\n background: #fff;\n border: 1px solid #e8e8e8;\n border-radius: 8px;\n padding: 10px 14px;\n transition: all 0.2s;\n }\n .metric-card:hover {\n border-color: #4caf50;\n }\n .metric-name {\n font-weight: 600;\n font-size: 0.85rem;\n color: #333;\n }\n .metric-unit {\n font-size: 0.75rem;\n color: #888;\n }\n .metric-used {\n font-size: 0.7rem;\n color: #1976d2;\n margin-top: 4px;\n }\n\n /* 팝업 */\n .popup-overlay {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.3);\n z-index: 1000;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n .popup-box {\n background: #fff;\n border-radius: 12px;\n padding: 24px;\n min-width: 400px;\n max-width: 600px;\n max-height: 80vh;\n overflow-y: auto;\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);\n }\n .popup-title {\n font-size: 1.1rem;\n font-weight: 700;\n color: #1a237e;\n margin-bottom: 16px;\n display: flex;\n justify-content: space-between;\n }\n .popup-close {\n background: none;\n border: none;\n font-size: 1.2rem;\n cursor: pointer;\n color: #999;\n }\n .popup-row {\n display: flex;\n gap: 8px;\n margin-bottom: 8px;\n font-size: 0.85rem;\n }\n .popup-label {\n color: #888;\n min-width: 100px;\n flex-shrink: 0;\n }\n .popup-value {\n color: #333;\n font-weight: 500;\n }\n .popup-formula {\n background: #f5f5f5;\n padding: 10px;\n border-radius: 6px;\n font-family: monospace;\n font-size: 0.85rem;\n margin: 8px 0;\n word-break: break-all;\n }\n .popup-link {\n color: #1976d2;\n cursor: pointer;\n text-decoration: underline;\n font-size: 0.85rem;\n }\n .popup-link:hover {\n color: #0d47a1;\n }\n\n /* 범례 */\n .legend {\n display: flex;\n gap: 16px;\n flex-wrap: wrap;\n margin-bottom: 20px;\n padding: 12px 16px;\n background: #fff;\n border-radius: 8px;\n box-shadow: 0 1px 4px rgba(0, 0, 0, 0.04);\n }\n .legend-group {\n display: flex;\n align-items: center;\n gap: 4px;\n font-size: 0.8rem;\n }\n .legend-title {\n font-weight: 700;\n color: #555;\n margin-right: 4px;\n }\n `\n ]\n\n @state() kpiRoot: any[] = []\n @state() metrics: any[] = []\n @state() loading = true\n @state() selectedDetail: any = null\n\n get context() {\n return { title: 'KPI 시스템 가이드' }\n }\n\n async firstUpdated() {\n try {\n const { data } = await client.query({ query: GET_KPI_SYSTEM })\n this.kpiRoot = data.kpiRoot || []\n this.metrics = data.kpiMetrics?.items || []\n } catch (e) {\n console.error('Failed to load KPI system data:', e)\n } finally {\n this.loading = false\n }\n }\n\n /** 메트릭이 사용되는 KPI 목록 — 비활성 KPI/Y 그룹 제외 */\n private getMetricUsage(metricName: string): string[] {\n const used: string[] = []\n for (const y of this.kpiRoot) {\n if (y.active === false) continue\n for (const x of y.kpis || []) {\n if (x.active === false) continue\n if (x.formula?.includes(`[${metricName}]`)) {\n used.push(x.name)\n }\n }\n }\n return used\n }\n\n private showDetail(item: any, type: 'kpi' | 'metric') {\n this.selectedDetail = { ...item, _type: type }\n }\n\n private closeDetail() {\n this.selectedDetail = null\n }\n\n render() {\n if (this.loading) return html`<div style=\"padding:40px;text-align:center;color:#888;\">로딩 중...</div>`\n\n // Z 노드 (루트의 부모)\n const allX = this.kpiRoot.flatMap((y: any) => (y.kpis || []).filter((x: any) => x.active !== false))\n\n return html`\n <div class=\"page-title\">KPI 시스템 가이드</div>\n <div class=\"page-subtitle\">\n 건축프로젝트 성과평가 KPI 체계의 전체 구조, 산식, 메트릭 연관관계를 확인합니다. 모든 정보는 DB에서 실시간 조회됩니다.\n </div>\n\n <!-- 범례 -->\n <div class=\"legend\">\n <span class=\"legend-title\">Score 산정:</span>\n ${Object.entries(SCORE_TYPE_LABELS).map(\n ([, v]) => html`<span class=\"legend-group\"><span class=\"badge badge-score\">${v.label}</span></span>`\n )}\n <span style=\"width:16px\"></span>\n <span class=\"legend-title\">Value 획득:</span>\n ${Object.entries(VALUE_TYPE_LABELS).map(\n ([, v]) => html`<span class=\"legend-group\">${v.icon} <span class=\"badge badge-value\">${v.label}</span></span>`\n )}\n </div>\n\n <!-- 1. KPI 계층 구조 -->\n <div class=\"tree-section\">\n <div class=\"section-title\">📊 KPI 계층 구조 (Z → Y → X)</div>\n\n <!-- Z 루트 -->\n <div\n class=\"z-node\"\n @click=${() =>\n this.showDetail(\n {\n name: 'Z. 전체스코어',\n description: 'Y1~Y6 가중합',\n formula: 'Y1×w1 + Y2×w2 + ... + Y6×w6',\n scoreType: 'DIRECT',\n valueType: 'CALCULATED'\n },\n 'kpi'\n )}\n >\n <div class=\"z-name\">Z. 전체스코어</div>\n <div class=\"z-formula\">\n =\n ${this.kpiRoot\n .filter((y: any) => y.active !== false)\n .map((y: any) => `${y.name.split('.')[0]}×${y.weight}`)\n .join(' + ')}\n </div>\n </div>\n\n <!-- Y 그룹들 (비활성 Y 제외) -->\n ${this.kpiRoot\n .filter((y: any) => y.active !== false)\n .map((y: any) => {\n const activeKpis = (y.kpis || []).filter((x: any) => x.active !== false)\n return html`\n <div class=\"y-group\">\n <div class=\"y-header\" @click=${() => this.showDetail(y, 'kpi')}>\n <span class=\"y-name\">${y.name}</span>\n <span class=\"y-weight\">가중치: ${y.weight}</span>\n </div>\n <div class=\"y-body\">\n ${y.formula ? html`<div class=\"y-formula\">${y.formula}</div>` : ''}\n <div class=\"x-cards\">\n ${activeKpis.map((x: any) => {\n const st = SCORE_TYPE_LABELS[x.scoreType] || { label: x.scoreType || '미설정', color: '#999' }\n const vt = VALUE_TYPE_LABELS[x.valueType] || { label: x.valueType || '미설정', color: '#999', icon: '?' }\n return html`\n <div class=\"x-card\" @click=${() => this.showDetail(x, 'kpi')}>\n <div class=\"x-name\">${x.name}</div>\n <div class=\"x-formula\">${x.formula || '—'}</div>\n <div class=\"x-badges\">\n <span class=\"badge badge-score\">${st.label}</span>\n <span class=\"badge badge-value\">${vt.icon} ${vt.label}</span>\n </div>\n </div>\n `\n })}\n </div>\n </div>\n </div>\n `\n })}\n </div>\n\n <!-- 2. 원천 메트릭 — active=true 이면서 활성 KPI formula 에 실제 참조되는 것만 -->\n ${(() => {\n const usedMetrics = this.metrics\n .filter((m: any) => m.active)\n .map((m: any) => ({ m, usage: this.getMetricUsage(m.name) }))\n .filter(({ usage }: any) => usage.length > 0)\n return html`\n <div class=\"tree-section\">\n <div class=\"section-title\">📋 원천 메트릭 (${usedMetrics.length}개 사용)</div>\n <div class=\"metrics-grid\">\n ${usedMetrics.map(\n ({ m, usage }: any) => html`\n <div class=\"metric-card\" @click=${() => this.showDetail(m, 'metric')}>\n <div class=\"metric-name\">${m.name}</div>\n <div class=\"metric-unit\">\n ${m.unit || '—'} ·\n ${m.collectType === 'EXTERNAL' && m.source\n ? EXTERNAL_SYSTEM_URLS[m.source]\n ? html`<a\n href=\"${EXTERNAL_SYSTEM_URLS[m.source]}\"\n target=\"_blank\"\n rel=\"noopener\"\n style=\"color:#1976d2;text-decoration:none;\"\n @click=${(e: Event) => e.stopPropagation()}\n >${m.source} ↗</a\n >`\n : m.source\n : m.collectType || 'MANUAL'}\n </div>\n <div class=\"metric-used\">→ ${usage.join(', ')}</div>\n </div>\n `\n )}\n </div>\n </div>\n `\n })()}\n\n <!-- 3. 시스템 요약 -->\n <div class=\"tree-section\">\n <div class=\"section-title\">📈 시스템 요약</div>\n <div style=\"display:flex;gap:16px;flex-wrap:wrap;\">\n <div style=\"flex:1;min-width:200px;background:#e3f2fd;padding:16px;border-radius:8px;text-align:center;\">\n <div style=\"font-size:2rem;font-weight:800;color:#1565c0;\">${allX.length}</div>\n <div style=\"font-size:0.85rem;color:#555;\">X-level 지표</div>\n </div>\n <div style=\"flex:1;min-width:200px;background:#e8f5e9;padding:16px;border-radius:8px;text-align:center;\">\n <div style=\"font-size:2rem;font-weight:800;color:#2e7d32;\">\n ${this.kpiRoot.filter((y: any) => y.active !== false).length}\n </div>\n <div style=\"font-size:0.85rem;color:#555;\">Y-level 영역</div>\n </div>\n <div style=\"flex:1;min-width:200px;background:#fff3e0;padding:16px;border-radius:8px;text-align:center;\">\n <div style=\"font-size:2rem;font-weight:800;color:#e65100;\">\n ${this.metrics.filter((m: any) => m.active && this.getMetricUsage(m.name).length > 0).length}\n </div>\n <div style=\"font-size:0.85rem;color:#555;\">원천 메트릭</div>\n </div>\n <div style=\"flex:1;min-width:200px;background:#fce4ec;padding:16px;border-radius:8px;text-align:center;\">\n <div style=\"font-size:2rem;font-weight:800;color:#c62828;\">\n ${allX.filter((x: any) => x.valueType === 'ASSESSED').length}\n </div>\n <div style=\"font-size:0.85rem;color:#555;\">감리자 평가 항목</div>\n </div>\n </div>\n </div>\n\n <!-- 팝업 -->\n ${this.selectedDetail ? this._renderDetailPopup() : nothing}\n `\n }\n\n private _renderDetailPopup() {\n const d = this.selectedDetail\n const isKpi = d._type === 'kpi'\n\n return html`\n <div class=\"popup-overlay\" @click=${() => this.closeDetail()}>\n <div class=\"popup-box\" @click=${(e: Event) => e.stopPropagation()}>\n <div class=\"popup-title\">\n <span>${d.name}</span>\n <button class=\"popup-close\" @click=${() => this.closeDetail()}>×</button>\n </div>\n\n ${d.description\n ? html`\n <div class=\"popup-row\">\n <span class=\"popup-label\">설명</span>\n <span class=\"popup-value\">${d.description}</span>\n </div>\n `\n : ''}\n ${isKpi\n ? html`\n ${d.formula\n ? html`\n <div class=\"popup-row\"><span class=\"popup-label\">산식</span></div>\n <div class=\"popup-formula\">${d.formula}</div>\n `\n : ''}\n ${d.scoreType\n ? html`\n <div class=\"popup-row\">\n <span class=\"popup-label\">Score 산정</span>\n <span class=\"popup-value\" style=\"color:${(SCORE_TYPE_LABELS[d.scoreType] || {}).color || '#333'}\">\n ${(SCORE_TYPE_LABELS[d.scoreType] || {}).label || d.scoreType}\n </span>\n </div>\n `\n : ''}\n ${d.valueType\n ? html`\n <div class=\"popup-row\">\n <span class=\"popup-label\">Value 획득</span>\n <span class=\"popup-value\" style=\"color:${(VALUE_TYPE_LABELS[d.valueType] || {}).color || '#333'}\">\n ${(VALUE_TYPE_LABELS[d.valueType] || {}).icon || ''}\n ${(VALUE_TYPE_LABELS[d.valueType] || {}).label || d.valueType}\n </span>\n </div>\n `\n : ''}\n ${d.weight\n ? html`\n <div class=\"popup-row\">\n <span class=\"popup-label\">가중치</span>\n <span class=\"popup-value\">${d.weight}</span>\n </div>\n `\n : ''}\n\n <div style=\"margin-top:16px;padding-top:12px;border-top:1px solid #eee;\">\n <span\n class=\"popup-link\"\n @click=${() => {\n this.closeDetail()\n navigate('kpi-overview')\n }}\n >\n → KPI 개요에서 보기\n </span>\n \n <span\n class=\"popup-link\"\n @click=${() => {\n this.closeDetail()\n navigate('kpi-admin')\n }}\n >\n → KPI 관리에서 편집\n </span>\n </div>\n `\n : html`\n <div class=\"popup-row\">\n <span class=\"popup-label\">단위</span>\n <span class=\"popup-value\">${d.unit || '—'}</span>\n </div>\n <div class=\"popup-row\">\n <span class=\"popup-label\">수집 방식</span>\n <span class=\"popup-value\">${d.collectType || 'MANUAL'}</span>\n </div>\n <div class=\"popup-row\">\n <span class=\"popup-label\">데이터 소스</span>\n <span class=\"popup-value\">\n ${d.source && EXTERNAL_SYSTEM_URLS[d.source]\n ? html`<a href=\"${EXTERNAL_SYSTEM_URLS[d.source]}\" target=\"_blank\" rel=\"noopener\" style=\"color:#1976d2;\"\n >${d.source} ↗</a\n >`\n : d.source || '미설정'}\n </span>\n </div>\n\n <div style=\"margin-top:12px;\">\n <div class=\"popup-label\" style=\"margin-bottom:6px;\">사용처:</div>\n ${this.getMetricUsage(d.name).map(\n kpi => html`<div style=\"font-size:0.85rem;color:#1976d2;margin-bottom:2px;\">· ${kpi}</div>`\n )}\n ${this.getMetricUsage(d.name).length === 0 ? html`<div style=\"color:#ccc;font-size:0.85rem;\">미사용</div>` : ''}\n </div>\n\n <div style=\"margin-top:16px;padding-top:12px;border-top:1px solid #eee;\">\n <span\n class=\"popup-link\"\n @click=${() => {\n this.closeDetail()\n navigate('kpi-metric-value-list')\n }}\n >\n → 메트릭 값 관리\n </span>\n </div>\n `}\n </div>\n </div>\n `\n }\n}\n"]}
|
|
@@ -27,6 +27,12 @@ export declare class KpiLeftPanel extends LitElement {
|
|
|
27
27
|
private calculateChangeRate;
|
|
28
28
|
private generateChartData;
|
|
29
29
|
private _onAllPeriodChange;
|
|
30
|
+
/** 시작월 설정 — 시작 > 종료(역전)면 종료를 시작에 맞춰 보정. (YYYY-MM 문자열 비교 = 시간순) */
|
|
31
|
+
private _setStartYearMonth;
|
|
32
|
+
/** 종료월 설정 — 종료 < 시작(역전)이면 시작을 종료에 맞춰 보정. */
|
|
33
|
+
private _setEndYearMonth;
|
|
34
|
+
/** 기간 변경을 부모로 전파 → 지역 팝업 등 다른 패널이 동일 기간을 쓰도록 동기화 */
|
|
35
|
+
private _emitPeriodChange;
|
|
30
36
|
private onCategoryChange;
|
|
31
37
|
private onChartTypeChange;
|
|
32
38
|
private onRegionClick;
|
|
@@ -128,12 +128,12 @@ let KpiLeftPanel = class KpiLeftPanel extends LitElement {
|
|
|
128
128
|
// selectedCategory에 따라 kpiName 결정
|
|
129
129
|
const categoryToKpiName = {
|
|
130
130
|
'전체 KPI': null,
|
|
131
|
-
'일정 성과': 'Y1
|
|
132
|
-
'비용 성과': 'Y2
|
|
133
|
-
'품질 성과': 'Y3
|
|
131
|
+
'일정 성과': '일정성과(Y1)',
|
|
132
|
+
'비용 성과': '비용성과(Y2)',
|
|
133
|
+
'품질 성과': '품질성과(Y3)',
|
|
134
134
|
'안전 성과': 'Y4. 안전성과',
|
|
135
|
-
'환경 성과': 'Y5
|
|
136
|
-
'생산성 성과': 'Y6
|
|
135
|
+
'환경 성과': '환경성과(Y5)',
|
|
136
|
+
'생산성 성과': '생산성성과(Y6)'
|
|
137
137
|
};
|
|
138
138
|
const kpiName = categoryToKpiName[this.selectedCategory];
|
|
139
139
|
const response = await client.query({
|
|
@@ -186,12 +186,12 @@ let KpiLeftPanel = class KpiLeftPanel extends LitElement {
|
|
|
186
186
|
// selectedCategory에 따라 kpiName 결정
|
|
187
187
|
const categoryToKpiName = {
|
|
188
188
|
'전체 KPI': null,
|
|
189
|
-
'일정 성과': 'Y1
|
|
190
|
-
'비용 성과': 'Y2
|
|
191
|
-
'품질 성과': 'Y3
|
|
189
|
+
'일정 성과': '일정성과(Y1)',
|
|
190
|
+
'비용 성과': '비용성과(Y2)',
|
|
191
|
+
'품질 성과': '품질성과(Y3)',
|
|
192
192
|
'안전 성과': 'Y4. 안전성과',
|
|
193
|
-
'환경 성과': 'Y5
|
|
194
|
-
'생산성 성과': 'Y6
|
|
193
|
+
'환경 성과': '환경성과(Y5)',
|
|
194
|
+
'생산성 성과': '생산성성과(Y6)'
|
|
195
195
|
};
|
|
196
196
|
const kpiName = categoryToKpiName[this.selectedCategory];
|
|
197
197
|
const response = await client.query({
|
|
@@ -263,29 +263,31 @@ let KpiLeftPanel = class KpiLeftPanel extends LitElement {
|
|
|
263
263
|
return ((latestValue - oldestValue) / oldestValue) * 100;
|
|
264
264
|
}
|
|
265
265
|
generateChartData() {
|
|
266
|
-
//
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
266
|
+
// kpiName 표기가 'Y1. 일정성과' / '일정성과(Y1)' 등으로 환경마다 달라 전체 문자열 매칭은 깨짐.
|
|
267
|
+
// Y1~Y6 코드만 추출해 짧은 라벨로 매핑.
|
|
268
|
+
const yLabelByCode = {
|
|
269
|
+
Y1: '일정',
|
|
270
|
+
Y2: '비용',
|
|
271
|
+
Y3: '품질',
|
|
272
|
+
Y4: '안전',
|
|
273
|
+
Y5: '환경',
|
|
274
|
+
Y6: '생산성'
|
|
274
275
|
};
|
|
276
|
+
const toCategory = (kpiName) => { var _a, _b; return yLabelByCode[(_b = (_a = (kpiName || '').match(/Y[1-6]/)) === null || _a === void 0 ? void 0 : _a[0]) !== null && _b !== void 0 ? _b : ''] || kpiName; };
|
|
275
277
|
if (this.selectedChartType === 'radar') {
|
|
276
278
|
// 레이더 차트용 데이터 생성 (전체 평균만 표시)
|
|
277
279
|
const data = [];
|
|
278
280
|
const categories = [];
|
|
279
281
|
this.kpiYComprehensiveStats.forEach(stat => {
|
|
280
|
-
const category =
|
|
282
|
+
const category = toCategory(stat.kpiName);
|
|
281
283
|
if (!categories.includes(category)) {
|
|
282
284
|
categories.push(category);
|
|
283
285
|
}
|
|
284
|
-
// 전체 평균 데이터만 표시 (비교 시리즈 없음)
|
|
286
|
+
// 전체 평균 데이터만 표시 (비교 시리즈 없음) — 박스플롯과 동일 정밀도(Math.round 제거)
|
|
285
287
|
data.push({
|
|
286
288
|
org: '전체평균',
|
|
287
289
|
category,
|
|
288
|
-
value:
|
|
290
|
+
value: stat.avgVal * 100
|
|
289
291
|
});
|
|
290
292
|
});
|
|
291
293
|
this.chartCategories = categories.length > 0 ? categories : ['일정', '비용', '품질', '안전', '환경', '생산성'];
|
|
@@ -296,7 +298,7 @@ let KpiLeftPanel = class KpiLeftPanel extends LitElement {
|
|
|
296
298
|
const data = [];
|
|
297
299
|
const categories = [];
|
|
298
300
|
this.kpiYComprehensiveStats.forEach(stat => {
|
|
299
|
-
const category =
|
|
301
|
+
const category = toCategory(stat.kpiName);
|
|
300
302
|
if (!categories.includes(category)) {
|
|
301
303
|
categories.push(category);
|
|
302
304
|
}
|
|
@@ -318,9 +320,37 @@ let KpiLeftPanel = class KpiLeftPanel extends LitElement {
|
|
|
318
320
|
_onAllPeriodChange(e) {
|
|
319
321
|
this.isAllPeriod = e.target.checked;
|
|
320
322
|
if (this.isAllPeriod) {
|
|
321
|
-
|
|
322
|
-
|
|
323
|
+
// 전체: 시작 = 선택 가능한 가장 이른 월(2022-01), 종료 = 현재월 → 모든 데이터 포함하며 표시도 명확
|
|
324
|
+
// (빈 문자열로 두면 드롭다운이 2022-01~2022-01 로 잘못 보이던 문제 방지)
|
|
325
|
+
const now = new Date();
|
|
326
|
+
this.startYearMonth = '2022-01';
|
|
327
|
+
this.endYearMonth = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}`;
|
|
328
|
+
}
|
|
329
|
+
this._emitPeriodChange();
|
|
330
|
+
}
|
|
331
|
+
/** 시작월 설정 — 시작 > 종료(역전)면 종료를 시작에 맞춰 보정. (YYYY-MM 문자열 비교 = 시간순) */
|
|
332
|
+
_setStartYearMonth(ym) {
|
|
333
|
+
this.startYearMonth = ym;
|
|
334
|
+
if (this.endYearMonth && this.startYearMonth > this.endYearMonth) {
|
|
335
|
+
this.endYearMonth = this.startYearMonth;
|
|
323
336
|
}
|
|
337
|
+
this._emitPeriodChange();
|
|
338
|
+
}
|
|
339
|
+
/** 종료월 설정 — 종료 < 시작(역전)이면 시작을 종료에 맞춰 보정. */
|
|
340
|
+
_setEndYearMonth(ym) {
|
|
341
|
+
this.endYearMonth = ym;
|
|
342
|
+
if (this.startYearMonth && this.endYearMonth < this.startYearMonth) {
|
|
343
|
+
this.startYearMonth = this.endYearMonth;
|
|
344
|
+
}
|
|
345
|
+
this._emitPeriodChange();
|
|
346
|
+
}
|
|
347
|
+
/** 기간 변경을 부모로 전파 → 지역 팝업 등 다른 패널이 동일 기간을 쓰도록 동기화 */
|
|
348
|
+
_emitPeriodChange() {
|
|
349
|
+
this.dispatchEvent(new CustomEvent('period-change', {
|
|
350
|
+
detail: { startYearMonth: this.startYearMonth, endYearMonth: this.endYearMonth, isAllPeriod: this.isAllPeriod },
|
|
351
|
+
bubbles: true,
|
|
352
|
+
composed: true
|
|
353
|
+
}));
|
|
324
354
|
}
|
|
325
355
|
onCategoryChange(event) {
|
|
326
356
|
const target = event.target;
|
|
@@ -368,7 +398,10 @@ let KpiLeftPanel = class KpiLeftPanel extends LitElement {
|
|
|
368
398
|
const condData = [
|
|
369
399
|
{ 항목: 'KPI 카테고리', 값: this.selectedCategory },
|
|
370
400
|
{ 항목: '사업 유형', 값: this.sectorType === 'PUBLIC' ? '공공' : this.sectorType === 'PRIVATE' ? '민간' : '전체' },
|
|
371
|
-
{
|
|
401
|
+
{
|
|
402
|
+
항목: '건물 용도',
|
|
403
|
+
값: this.buildingUsage === 'RESIDENTIAL' ? '주거' : this.buildingUsage === 'NON_RESIDENTIAL' ? '비주거' : '전체'
|
|
404
|
+
},
|
|
372
405
|
{ 항목: '기간', 값: this.isAllPeriod ? '전체 기간' : `${this.startYearMonth} ~ ${this.endYearMonth}` },
|
|
373
406
|
{ 항목: '다운로드 일시', 값: new Date().toLocaleString('ko-KR') }
|
|
374
407
|
];
|
|
@@ -445,7 +478,7 @@ let KpiLeftPanel = class KpiLeftPanel extends LitElement {
|
|
|
445
478
|
return '▲';
|
|
446
479
|
if (change < 0)
|
|
447
480
|
return '▼';
|
|
448
|
-
return '
|
|
481
|
+
return ''; // 변동 없음(0)이면 화살표 미표시
|
|
449
482
|
}
|
|
450
483
|
render() {
|
|
451
484
|
return html `
|
|
@@ -495,33 +528,49 @@ let KpiLeftPanel = class KpiLeftPanel extends LitElement {
|
|
|
495
528
|
<!-- 기간 필터 -->
|
|
496
529
|
<div class="filter-row">
|
|
497
530
|
<span class="filter-label">기간</span>
|
|
498
|
-
<select
|
|
531
|
+
<select
|
|
532
|
+
class="filter-select"
|
|
533
|
+
?disabled=${this.isAllPeriod}
|
|
534
|
+
@change=${(e) => {
|
|
499
535
|
const y = e.target.value;
|
|
500
536
|
const m = this.startYearMonth.split('-')[1] || '01';
|
|
501
|
-
this.
|
|
502
|
-
}}
|
|
537
|
+
this._setStartYearMonth(`${y}-${m}`);
|
|
538
|
+
}}
|
|
539
|
+
>
|
|
503
540
|
${[2022, 2023, 2024, 2025, 2026].map(y => html `<option value="${y}" ?selected=${this.startYearMonth.startsWith(String(y))}>${y}</option>`)}
|
|
504
541
|
</select>
|
|
505
|
-
<select
|
|
542
|
+
<select
|
|
543
|
+
class="filter-select"
|
|
544
|
+
?disabled=${this.isAllPeriod}
|
|
545
|
+
@change=${(e) => {
|
|
506
546
|
const y = this.startYearMonth.split('-')[0] || '2025';
|
|
507
547
|
const m = e.target.value.padStart(2, '0');
|
|
508
|
-
this.
|
|
509
|
-
}}
|
|
548
|
+
this._setStartYearMonth(`${y}-${m}`);
|
|
549
|
+
}}
|
|
550
|
+
>
|
|
510
551
|
${[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12].map(m => html `<option value="${m}" ?selected=${parseInt(this.startYearMonth.split('-')[1]) === m}>${m}월</option>`)}
|
|
511
552
|
</select>
|
|
512
553
|
<span style="color:#6c757d;font-size:0.8rem;">~</span>
|
|
513
|
-
<select
|
|
554
|
+
<select
|
|
555
|
+
class="filter-select"
|
|
556
|
+
?disabled=${this.isAllPeriod}
|
|
557
|
+
@change=${(e) => {
|
|
514
558
|
const y = e.target.value;
|
|
515
559
|
const m = this.endYearMonth.split('-')[1] || '12';
|
|
516
|
-
this.
|
|
517
|
-
}}
|
|
560
|
+
this._setEndYearMonth(`${y}-${m}`);
|
|
561
|
+
}}
|
|
562
|
+
>
|
|
518
563
|
${[2022, 2023, 2024, 2025, 2026].map(y => html `<option value="${y}" ?selected=${this.endYearMonth.startsWith(String(y))}>${y}</option>`)}
|
|
519
564
|
</select>
|
|
520
|
-
<select
|
|
565
|
+
<select
|
|
566
|
+
class="filter-select"
|
|
567
|
+
?disabled=${this.isAllPeriod}
|
|
568
|
+
@change=${(e) => {
|
|
521
569
|
const y = this.endYearMonth.split('-')[0] || '2026';
|
|
522
570
|
const m = e.target.value.padStart(2, '0');
|
|
523
|
-
this.
|
|
524
|
-
}}
|
|
571
|
+
this._setEndYearMonth(`${y}-${m}`);
|
|
572
|
+
}}
|
|
573
|
+
>
|
|
525
574
|
${[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12].map(m => html `<option value="${m}" ?selected=${parseInt(this.endYearMonth.split('-')[1]) === m}>${m}월</option>`)}
|
|
526
575
|
</select>
|
|
527
576
|
<label style="display:flex;align-items:center;gap:3px;cursor:pointer;">
|
|
@@ -532,7 +581,7 @@ let KpiLeftPanel = class KpiLeftPanel extends LitElement {
|
|
|
532
581
|
|
|
533
582
|
<!-- 종합 성과 -->
|
|
534
583
|
<div class="chart-section">
|
|
535
|
-
<div class="sub-title"
|
|
584
|
+
<div class="sub-title">성과영역별 KPI</div>
|
|
536
585
|
<div class="chart-toggle">
|
|
537
586
|
<button
|
|
538
587
|
class="toggle-button ${this.selectedChartType === 'boxplot' ? 'active' : ''}"
|
|
@@ -548,21 +597,32 @@ let KpiLeftPanel = class KpiLeftPanel extends LitElement {
|
|
|
548
597
|
</button>
|
|
549
598
|
</div>
|
|
550
599
|
<div class="chart-container">
|
|
551
|
-
${this.
|
|
552
|
-
? html
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
600
|
+
${this.chartData.length === 0
|
|
601
|
+
? html `<div style="display:flex;align-items:center;justify-content:center;height:100%;color:#888;font-size:0.9rem;">
|
|
602
|
+
선택한 기간에 데이터가 없습니다.
|
|
603
|
+
</div>`
|
|
604
|
+
: this.selectedChartType === 'boxplot'
|
|
605
|
+
? html `
|
|
606
|
+
<sv-kpi-boxplot-chart
|
|
607
|
+
.data=${this.chartData}
|
|
608
|
+
.valueKey=${'value'}
|
|
609
|
+
.valueLabel=${'전국'}
|
|
610
|
+
.showStatus=${false}
|
|
611
|
+
></sv-kpi-boxplot-chart>
|
|
612
|
+
`
|
|
613
|
+
: html `
|
|
614
|
+
<sv-kpi-radar-chart
|
|
615
|
+
.data=${this.chartData}
|
|
616
|
+
.categories=${this.chartCategories}
|
|
617
|
+
.valueKey=${'value'}
|
|
618
|
+
></sv-kpi-radar-chart>
|
|
619
|
+
`}
|
|
560
620
|
</div>
|
|
561
621
|
</div>
|
|
562
622
|
|
|
563
623
|
<!-- 시도별 성과 -->
|
|
564
624
|
<div class="chart-section">
|
|
565
|
-
<div class="sub-title">시도별
|
|
625
|
+
<div class="sub-title">시도별 종합 KPI</div>
|
|
566
626
|
<table class="performance-table">
|
|
567
627
|
<thead>
|
|
568
628
|
<tr>
|
|
@@ -732,7 +792,7 @@ KpiLeftPanel.styles = [
|
|
|
732
792
|
color: #dc3545;
|
|
733
793
|
}
|
|
734
794
|
.change-down {
|
|
735
|
-
color: #
|
|
795
|
+
color: #2196f3;
|
|
736
796
|
}
|
|
737
797
|
.change-neutral {
|
|
738
798
|
color: #6c757d;
|