@opsnow-mcp/opsnow-mcp-common-ui-server 1.0.23 → 1.0.25

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.
@@ -538,11 +538,9 @@ export const PieChartExamples = [
538
538
  const [chartData] = useState([
539
539
  { service: 'EC2', cost: 15600 },
540
540
  { service: 'RDS', cost: 8200 },
541
- { service: 'S3', cost: 4300 },
542
- { service: 'Lambda', cost: 2100 },
543
- { service: 'CloudFront', cost: 1800 }
541
+ { service: 'S3', cost: 4300 }
544
542
  ])
545
- const [chartCategory] = useState(['EC2', 'RDS', 'S3', 'Lambda', 'CloudFront'])
543
+ const [chartCategory] = useState(['EC2', 'RDS', 'S3'])
546
544
 
547
545
  // 다운로드 핸들러
548
546
  const handleDownload = () => {
@@ -6841,3 +6839,428 @@ export const XyMultiChartExamples = [
6841
6839
  />`
6842
6840
  },
6843
6841
  ];
6842
+ // Heatmap Chart 컴포넌트 예제 데이터
6843
+ export const HeatmapChartExamples = [
6844
+ {
6845
+ title: '히트맵 차트 - 기본',
6846
+ description: '인시던트 발생 패턴(시간×요일) 히트맵 차트 기본 예제입니다. heatRule로 최소/최대 색상을 지정하고, 기본 그래디언트 범례를 표시합니다.',
6847
+ code_props_usage: `
6848
+ const chartId = 'heatmap-chart-${generateUniqueGeneralId()}'
6849
+ const chartRef = useRef(null)
6850
+ const [chartProps, setChartProps] = useState({
6851
+ chart: {
6852
+ paddingLeft: 0,
6853
+ paddingRight: 0,
6854
+ paddingBottom: 0,
6855
+ },
6856
+ xAxis: {
6857
+ type: 'category',
6858
+ valueField: 'hour',
6859
+ gridHidden: true,
6860
+ renderer: {
6861
+ opposite: true,
6862
+ minGridDistance: 20,
6863
+ },
6864
+ label: { fontSize: 11 },
6865
+ },
6866
+ yAxis: {
6867
+ type: 'category',
6868
+ valueField: 'day',
6869
+ gridHidden: true,
6870
+ renderer: {
6871
+ inversed: true,
6872
+ minGridDistance: 20,
6873
+ },
6874
+ label: { fontSize: 11, maxWidth: 70, fontWeight: 'bold' },
6875
+ },
6876
+ series: {
6877
+ categoryXField: 'hour',
6878
+ categoryYField: 'day',
6879
+ valueField: 'value',
6880
+ columns: {
6881
+ strokeWidth: 2,
6882
+ hover: { enabled: true },
6883
+ },
6884
+ tooltip: {
6885
+ type: 'CI',
6886
+ header: { name: '{day} {hour}h' },
6887
+ body: { value: '{value} {l.unit}' },
6888
+ },
6889
+ },
6890
+ heatRule: {
6891
+ key: 'fill',
6892
+ minColor: '#fce4ec',
6893
+ maxColor: '#c62828',
6894
+ },
6895
+ legend: {
6896
+ orientation: 'horizontal',
6897
+ legendWidth: 200,
6898
+ },
6899
+ })
6900
+ const [chartData, setChartData] = useState([
6901
+ { hour: '0', day: 'day_0', value: 0 }, { hour: '1', day: 'day_0', value: 2 },
6902
+ { hour: '2', day: 'day_0', value: 0 }, { hour: '3', day: 'day_0', value: 5 },
6903
+ { hour: '4', day: 'day_0', value: 0 }, { hour: '5', day: 'day_0', value: 0 },
6904
+ { hour: '6', day: 'day_0', value: 1 }, { hour: '7', day: 'day_0', value: 3 },
6905
+ { hour: '8', day: 'day_0', value: 0 }, { hour: '9', day: 'day_0', value: 0 },
6906
+ { hour: '10', day: 'day_0', value: 4 }, { hour: '11', day: 'day_0', value: 0 },
6907
+ { hour: '12', day: 'day_0', value: 0 }, { hour: '13', day: 'day_0', value: 2 },
6908
+ { hour: '14', day: 'day_0', value: 0 }, { hour: '15', day: 'day_0', value: 0 },
6909
+ { hour: '16', day: 'day_0', value: 6 }, { hour: '17', day: 'day_0', value: 0 },
6910
+ { hour: '18', day: 'day_0', value: 0 }, { hour: '19', day: 'day_0', value: 1 },
6911
+ { hour: '20', day: 'day_0', value: 0 }, { hour: '21', day: 'day_0', value: 0 },
6912
+ { hour: '22', day: 'day_0', value: 3 }, { hour: '23', day: 'day_0', value: 0 },
6913
+ { hour: '0', day: 'day_1', value: 0 }, { hour: '1', day: 'day_1', value: 0 },
6914
+ { hour: '2', day: 'day_1', value: 1 }, { hour: '3', day: 'day_1', value: 0 },
6915
+ { hour: '4', day: 'day_1', value: 0 }, { hour: '5', day: 'day_1', value: 3 },
6916
+ { hour: '6', day: 'day_1', value: 0 }, { hour: '7', day: 'day_1', value: 0 },
6917
+ { hour: '8', day: 'day_1', value: 7 }, { hour: '9', day: 'day_1', value: 0 },
6918
+ { hour: '10', day: 'day_1', value: 0 }, { hour: '11', day: 'day_1', value: 2 },
6919
+ { hour: '12', day: 'day_1', value: 0 }, { hour: '13', day: 'day_1', value: 0 },
6920
+ { hour: '14', day: 'day_1', value: 4 }, { hour: '15', day: 'day_1', value: 0 },
6921
+ { hour: '16', day: 'day_1', value: 0 }, { hour: '17', day: 'day_1', value: 1 },
6922
+ { hour: '18', day: 'day_1', value: 0 }, { hour: '19', day: 'day_1', value: 5 },
6923
+ { hour: '20', day: 'day_1', value: 0 }, { hour: '21', day: 'day_1', value: 0 },
6924
+ { hour: '22', day: 'day_1', value: 0 }, { hour: '23', day: 'day_1', value: 2 },
6925
+ { hour: '0', day: 'day_2', value: 1 }, { hour: '1', day: 'day_2', value: 0 },
6926
+ { hour: '2', day: 'day_2', value: 0 }, { hour: '3', day: 'day_2', value: 0 },
6927
+ { hour: '4', day: 'day_2', value: 3 }, { hour: '5', day: 'day_2', value: 0 },
6928
+ { hour: '6', day: 'day_2', value: 0 }, { hour: '7', day: 'day_2', value: 6 },
6929
+ { hour: '8', day: 'day_2', value: 0 }, { hour: '9', day: 'day_2', value: 2 },
6930
+ { hour: '10', day: 'day_2', value: 0 }, { hour: '11', day: 'day_2', value: 0 },
6931
+ { hour: '12', day: 'day_2', value: 4 }, { hour: '13', day: 'day_2', value: 0 },
6932
+ { hour: '14', day: 'day_2', value: 0 }, { hour: '15', day: 'day_2', value: 1 },
6933
+ { hour: '16', day: 'day_2', value: 0 }, { hour: '17', day: 'day_2', value: 0 },
6934
+ { hour: '18', day: 'day_2', value: 5 }, { hour: '19', day: 'day_2', value: 0 },
6935
+ { hour: '20', day: 'day_2', value: 0 }, { hour: '21', day: 'day_2', value: 3 },
6936
+ { hour: '22', day: 'day_2', value: 0 }, { hour: '23', day: 'day_2', value: 0 },
6937
+ ])
6938
+ const chartLocaleMessage = [
6939
+ { name: 'day_0', locale: 'Mon 4/14' },
6940
+ { name: 'day_1', locale: 'Tue 4/15' },
6941
+ { name: 'day_2', locale: 'Wed 4/16' },
6942
+ ]
6943
+ const chartLocaleProps = [
6944
+ { name: 'l.unit', locale: 'incidents' },
6945
+ ]
6946
+ `,
6947
+ code: `<OpsnowCommonHeatmapChart
6948
+ chartId={chartId}
6949
+ ref={chartRef}
6950
+ chartProps={chartProps}
6951
+ chartData={chartData}
6952
+ chartLocaleMessage={chartLocaleMessage}
6953
+ chartLocaleProps={chartLocaleProps}
6954
+ />`
6955
+ },
6956
+ {
6957
+ title: '히트맵 차트 - 레전드 라벨 및 스타일',
6958
+ description: '범례에 labelText와 스타일(폰트 크기, 바 높이)을 커스터마이징한 히트맵 차트 예제입니다.',
6959
+ code_props_usage: `
6960
+ const chartId = 'heatmap-chart-${generateUniqueGeneralId()}'
6961
+ const chartRef = useRef(null)
6962
+ const [chartProps, setChartProps] = useState({
6963
+ chart: {
6964
+ paddingLeft: 0,
6965
+ paddingRight: 0,
6966
+ paddingBottom: 0,
6967
+ },
6968
+ xAxis: {
6969
+ type: 'category',
6970
+ valueField: 'hour',
6971
+ gridHidden: true,
6972
+ renderer: {
6973
+ opposite: true,
6974
+ minGridDistance: 20,
6975
+ },
6976
+ label: { fontSize: 11 },
6977
+ },
6978
+ yAxis: {
6979
+ type: 'category',
6980
+ valueField: 'day',
6981
+ gridHidden: true,
6982
+ renderer: {
6983
+ inversed: true,
6984
+ minGridDistance: 20,
6985
+ },
6986
+ label: { fontSize: 11, maxWidth: 70, fontWeight: 'bold' },
6987
+ },
6988
+ series: {
6989
+ categoryXField: 'hour',
6990
+ categoryYField: 'day',
6991
+ valueField: 'value',
6992
+ columns: {
6993
+ strokeWidth: 2,
6994
+ hover: { enabled: true },
6995
+ },
6996
+ tooltip: {
6997
+ type: 'CI',
6998
+ header: { name: '{day} {hour}h' },
6999
+ body: { value: '{value} {l.unit}' },
7000
+ label: { fontSize: 11 },
7001
+ },
7002
+ },
7003
+ heatRule: {
7004
+ key: 'fill',
7005
+ minColor: '#fce4ec',
7006
+ maxColor: '#c62828',
7007
+ },
7008
+ legend: {
7009
+ orientation: 'horizontal',
7010
+ labelText: '{l.legendLabel}',
7011
+ labelFontSize: 12,
7012
+ legendWidth: 200,
7013
+ barHeight: 16,
7014
+ },
7015
+ })
7016
+ const [chartData, setChartData] = useState([
7017
+ { hour: '0', day: 'day_0', value: 0 }, { hour: '1', day: 'day_0', value: 2 },
7018
+ { hour: '2', day: 'day_0', value: 0 }, { hour: '3', day: 'day_0', value: 5 },
7019
+ { hour: '4', day: 'day_0', value: 0 }, { hour: '5', day: 'day_0', value: 0 },
7020
+ { hour: '6', day: 'day_0', value: 1 }, { hour: '7', day: 'day_0', value: 3 },
7021
+ { hour: '8', day: 'day_0', value: 0 }, { hour: '9', day: 'day_0', value: 0 },
7022
+ { hour: '10', day: 'day_0', value: 4 }, { hour: '11', day: 'day_0', value: 0 },
7023
+ { hour: '12', day: 'day_0', value: 0 }, { hour: '13', day: 'day_0', value: 2 },
7024
+ { hour: '14', day: 'day_0', value: 0 }, { hour: '15', day: 'day_0', value: 0 },
7025
+ { hour: '16', day: 'day_0', value: 6 }, { hour: '17', day: 'day_0', value: 0 },
7026
+ { hour: '18', day: 'day_0', value: 0 }, { hour: '19', day: 'day_0', value: 1 },
7027
+ { hour: '20', day: 'day_0', value: 0 }, { hour: '21', day: 'day_0', value: 0 },
7028
+ { hour: '22', day: 'day_0', value: 3 }, { hour: '23', day: 'day_0', value: 0 },
7029
+ { hour: '0', day: 'day_1', value: 0 }, { hour: '1', day: 'day_1', value: 0 },
7030
+ { hour: '2', day: 'day_1', value: 1 }, { hour: '3', day: 'day_1', value: 0 },
7031
+ { hour: '4', day: 'day_1', value: 0 }, { hour: '5', day: 'day_1', value: 3 },
7032
+ { hour: '6', day: 'day_1', value: 0 }, { hour: '7', day: 'day_1', value: 0 },
7033
+ { hour: '8', day: 'day_1', value: 7 }, { hour: '9', day: 'day_1', value: 0 },
7034
+ { hour: '10', day: 'day_1', value: 0 }, { hour: '11', day: 'day_1', value: 2 },
7035
+ { hour: '12', day: 'day_1', value: 0 }, { hour: '13', day: 'day_1', value: 0 },
7036
+ { hour: '14', day: 'day_1', value: 4 }, { hour: '15', day: 'day_1', value: 0 },
7037
+ { hour: '16', day: 'day_1', value: 0 }, { hour: '17', day: 'day_1', value: 1 },
7038
+ { hour: '18', day: 'day_1', value: 0 }, { hour: '19', day: 'day_1', value: 5 },
7039
+ { hour: '20', day: 'day_1', value: 0 }, { hour: '21', day: 'day_1', value: 0 },
7040
+ { hour: '22', day: 'day_1', value: 0 }, { hour: '23', day: 'day_1', value: 2 },
7041
+ ])
7042
+ const chartLocaleMessage = [
7043
+ { name: 'day_0', locale: 'Mon 4/14' },
7044
+ { name: 'day_1', locale: 'Tue 4/15' },
7045
+ ]
7046
+ const chartLocaleProps = [
7047
+ { name: 'l.unit', locale: 'incidents' },
7048
+ { name: 'l.legendLabel', locale: 'Incident Count' },
7049
+ ]
7050
+ `,
7051
+ code: `<OpsnowCommonHeatmapChart
7052
+ chartId={chartId}
7053
+ ref={chartRef}
7054
+ chartProps={chartProps}
7055
+ chartData={chartData}
7056
+ chartLocaleMessage={chartLocaleMessage}
7057
+ chartLocaleProps={chartLocaleProps}
7058
+ />`
7059
+ },
7060
+ {
7061
+ title: '히트맵 차트 - 커스텀 색상 및 클릭 이벤트',
7062
+ description: '커스텀 색상(보라 계열 그래디언트)과 셀 클릭 이벤트를 처리하는 히트맵 차트 예제입니다. onChartClick으로 클릭한 셀 정보를 받을 수 있습니다.',
7063
+ code_props_usage: `
7064
+ const chartId = 'heatmap-chart-${generateUniqueGeneralId()}'
7065
+ const chartRef = useRef(null)
7066
+ const [clickedCell, setClickedCell] = useState(null)
7067
+ const [chartProps, setChartProps] = useState({
7068
+ chart: {
7069
+ paddingLeft: 0,
7070
+ paddingRight: 0,
7071
+ paddingBottom: 0,
7072
+ },
7073
+ xAxis: {
7074
+ type: 'category',
7075
+ valueField: 'hour',
7076
+ gridHidden: true,
7077
+ renderer: {
7078
+ opposite: true,
7079
+ minGridDistance: 20,
7080
+ },
7081
+ label: { fontSize: 11 },
7082
+ },
7083
+ yAxis: {
7084
+ type: 'category',
7085
+ valueField: 'day',
7086
+ gridHidden: true,
7087
+ renderer: {
7088
+ inversed: true,
7089
+ minGridDistance: 20,
7090
+ },
7091
+ label: { fontSize: 11, maxWidth: 70, fontWeight: 'bold' },
7092
+ },
7093
+ series: {
7094
+ categoryXField: 'hour',
7095
+ categoryYField: 'day',
7096
+ valueField: 'value',
7097
+ columns: {
7098
+ strokeWidth: 2,
7099
+ hover: { enabled: true, color: '#d50000' },
7100
+ },
7101
+ tooltip: {
7102
+ type: 'CI',
7103
+ header: { name: '{day} {hour}h' },
7104
+ body: { value: '{value} {l.unit}' },
7105
+ },
7106
+ },
7107
+ heatRule: {
7108
+ key: 'fill',
7109
+ minColor: '#e8eaf6',
7110
+ maxColor: '#4a148c',
7111
+ },
7112
+ legend: {
7113
+ orientation: 'horizontal',
7114
+ labelText: '{l.legendLabel}',
7115
+ labelFontSize: 12,
7116
+ labelFontWeight: 'bold',
7117
+ legendWidth: 200,
7118
+ barHeight: 16,
7119
+ },
7120
+ })
7121
+ const [chartData, setChartData] = useState([
7122
+ { hour: '0', day: 'day_0', value: 0 }, { hour: '1', day: 'day_0', value: 2 },
7123
+ { hour: '2', day: 'day_0', value: 0 }, { hour: '3', day: 'day_0', value: 5 },
7124
+ { hour: '4', day: 'day_0', value: 0 }, { hour: '5', day: 'day_0', value: 0 },
7125
+ { hour: '6', day: 'day_0', value: 1 }, { hour: '7', day: 'day_0', value: 3 },
7126
+ { hour: '8', day: 'day_0', value: 0 }, { hour: '9', day: 'day_0', value: 0 },
7127
+ { hour: '10', day: 'day_0', value: 4 }, { hour: '11', day: 'day_0', value: 0 },
7128
+ { hour: '12', day: 'day_0', value: 0 }, { hour: '13', day: 'day_0', value: 2 },
7129
+ { hour: '14', day: 'day_0', value: 0 }, { hour: '15', day: 'day_0', value: 0 },
7130
+ { hour: '16', day: 'day_0', value: 6 }, { hour: '17', day: 'day_0', value: 0 },
7131
+ { hour: '18', day: 'day_0', value: 0 }, { hour: '19', day: 'day_0', value: 1 },
7132
+ { hour: '20', day: 'day_0', value: 0 }, { hour: '21', day: 'day_0', value: 0 },
7133
+ { hour: '22', day: 'day_0', value: 3 }, { hour: '23', day: 'day_0', value: 0 },
7134
+ { hour: '0', day: 'day_1', value: 0 }, { hour: '1', day: 'day_1', value: 0 },
7135
+ { hour: '2', day: 'day_1', value: 1 }, { hour: '3', day: 'day_1', value: 0 },
7136
+ { hour: '4', day: 'day_1', value: 0 }, { hour: '5', day: 'day_1', value: 3 },
7137
+ { hour: '6', day: 'day_1', value: 0 }, { hour: '7', day: 'day_1', value: 0 },
7138
+ { hour: '8', day: 'day_1', value: 7 }, { hour: '9', day: 'day_1', value: 0 },
7139
+ { hour: '10', day: 'day_1', value: 0 }, { hour: '11', day: 'day_1', value: 2 },
7140
+ { hour: '12', day: 'day_1', value: 0 }, { hour: '13', day: 'day_1', value: 0 },
7141
+ { hour: '14', day: 'day_1', value: 4 }, { hour: '15', day: 'day_1', value: 0 },
7142
+ { hour: '16', day: 'day_1', value: 0 }, { hour: '17', day: 'day_1', value: 1 },
7143
+ { hour: '18', day: 'day_1', value: 0 }, { hour: '19', day: 'day_1', value: 5 },
7144
+ { hour: '20', day: 'day_1', value: 0 }, { hour: '21', day: 'day_1', value: 0 },
7145
+ { hour: '22', day: 'day_1', value: 0 }, { hour: '23', day: 'day_1', value: 2 },
7146
+ ])
7147
+ const chartLocaleMessage = [
7148
+ { name: 'day_0', locale: 'Mon 4/14' },
7149
+ { name: 'day_1', locale: 'Tue 4/15' },
7150
+ ]
7151
+ const chartLocaleProps = [
7152
+ { name: 'l.unit', locale: 'incidents' },
7153
+ { name: 'l.legendLabel', locale: 'Incident Count' },
7154
+ ]
7155
+ const handleChartClick = (data) => {
7156
+ setClickedCell(data)
7157
+ console.log('Clicked cell:', data)
7158
+ }
7159
+ `,
7160
+ code: `<OpsnowCommonHeatmapChart
7161
+ chartId={chartId}
7162
+ ref={chartRef}
7163
+ chartProps={chartProps}
7164
+ chartData={chartData}
7165
+ chartLocaleMessage={chartLocaleMessage}
7166
+ chartLocaleProps={chartLocaleProps}
7167
+ onChartClick={handleChartClick}
7168
+ />`
7169
+ },
7170
+ {
7171
+ title: '히트맵 차트 - downloadFile (차트 다운로드)',
7172
+ description: 'ref를 통해 downloadFile 메서드를 호출하여 히트맵 차트를 이미지(png, jpg) 또는 PDF로 다운로드하는 예제입니다.',
7173
+ code_props_usage: `
7174
+ const chartId = 'heatmap-chart-${generateUniqueGeneralId()}'
7175
+ const chartRef = useRef(null)
7176
+ const [chartProps, setChartProps] = useState({
7177
+ chart: {
7178
+ paddingLeft: 0,
7179
+ paddingRight: 0,
7180
+ paddingBottom: 0,
7181
+ },
7182
+ xAxis: {
7183
+ type: 'category',
7184
+ valueField: 'hour',
7185
+ gridHidden: true,
7186
+ renderer: {
7187
+ opposite: true,
7188
+ minGridDistance: 20,
7189
+ },
7190
+ label: { fontSize: 11 },
7191
+ },
7192
+ yAxis: {
7193
+ type: 'category',
7194
+ valueField: 'day',
7195
+ gridHidden: true,
7196
+ renderer: {
7197
+ inversed: true,
7198
+ minGridDistance: 20,
7199
+ },
7200
+ label: { fontSize: 11, maxWidth: 70, fontWeight: 'bold' },
7201
+ },
7202
+ series: {
7203
+ categoryXField: 'hour',
7204
+ categoryYField: 'day',
7205
+ valueField: 'value',
7206
+ columns: {
7207
+ strokeWidth: 2,
7208
+ hover: { enabled: true },
7209
+ },
7210
+ tooltip: {
7211
+ type: 'CI',
7212
+ header: { name: '{day} {hour}h' },
7213
+ body: { value: '{value} {l.unit}' },
7214
+ },
7215
+ },
7216
+ heatRule: {
7217
+ key: 'fill',
7218
+ minColor: '#e0f2f1',
7219
+ maxColor: '#00695c',
7220
+ },
7221
+ legend: {
7222
+ orientation: 'horizontal',
7223
+ legendWidth: 200,
7224
+ },
7225
+ })
7226
+ const [chartData, setChartData] = useState([
7227
+ { hour: '0', day: 'day_0', value: 0 }, { hour: '1', day: 'day_0', value: 2 },
7228
+ { hour: '2', day: 'day_0', value: 0 }, { hour: '3', day: 'day_0', value: 5 },
7229
+ { hour: '4', day: 'day_0', value: 0 }, { hour: '5', day: 'day_0', value: 0 },
7230
+ { hour: '6', day: 'day_0', value: 1 }, { hour: '7', day: 'day_0', value: 3 },
7231
+ { hour: '8', day: 'day_0', value: 0 }, { hour: '9', day: 'day_0', value: 0 },
7232
+ { hour: '10', day: 'day_0', value: 4 }, { hour: '11', day: 'day_0', value: 0 },
7233
+ { hour: '12', day: 'day_0', value: 0 }, { hour: '13', day: 'day_0', value: 2 },
7234
+ { hour: '14', day: 'day_0', value: 0 }, { hour: '15', day: 'day_0', value: 0 },
7235
+ { hour: '16', day: 'day_0', value: 6 }, { hour: '17', day: 'day_0', value: 0 },
7236
+ { hour: '18', day: 'day_0', value: 0 }, { hour: '19', day: 'day_0', value: 1 },
7237
+ { hour: '20', day: 'day_0', value: 0 }, { hour: '21', day: 'day_0', value: 0 },
7238
+ { hour: '22', day: 'day_0', value: 3 }, { hour: '23', day: 'day_0', value: 0 },
7239
+ ])
7240
+ const chartLocaleProps = [
7241
+ { name: 'l.unit', locale: 'incidents' },
7242
+ ]
7243
+ const chartLocaleMessage = [
7244
+ { name: 'day_0', locale: 'Mon 4/14' },
7245
+ ]
7246
+
7247
+ // 다운로드 함수
7248
+ const handleDownload = (fileType) => {
7249
+ // fileType: 'png' | 'jpg' | 'pdf'
7250
+ // fileOption: { quality?: number } (jpg일 때 품질 0~1)
7251
+ chartRef.current?.downloadFile('heatmap-chart', fileType)
7252
+ }
7253
+ `,
7254
+ code: `<OpsnowCommonHeatmapChart
7255
+ chartId={chartId}
7256
+ ref={chartRef}
7257
+ chartProps={chartProps}
7258
+ chartData={chartData}
7259
+ chartLocaleMessage={chartLocaleMessage}
7260
+ chartLocaleProps={chartLocaleProps}
7261
+ />
7262
+ <button onClick={() => handleDownload('png')}>Download PNG</button>
7263
+ <button onClick={() => handleDownload('jpg')}>Download JPG</button>
7264
+ <button onClick={() => handleDownload('pdf')}>Download PDF</button>`
7265
+ },
7266
+ ];
@@ -1337,6 +1337,172 @@ export const GaugeChartSchema = z.object({
1337
1337
  `),
1338
1338
  chartValue: z.number().describe("차트 값"),
1339
1339
  });
1340
+ // opsnow-common-heatmap-chart
1341
+ export const HeatmapChartSchema = z.object({
1342
+ chartId: z.string().describe("차트의 고유 식별자"),
1343
+ chartProps: z.object({}).describe(`
1344
+ 다음 정의된 형식 예를 보고 차트 속성을 설정하세요.
1345
+ **유효한 JSON 형식이어야합니다.**
1346
+ {
1347
+ // 차트 기본 설정
1348
+ "chart": {
1349
+ "paddingTop": 0, // 차트 상단 패딩 (px)
1350
+ "paddingBottom": 0, // 차트 하단 패딩 (px)
1351
+ "paddingLeft": 0, // 차트 좌측 패딩 (px)
1352
+ "paddingRight": 0, // 차트 우측 패딩 (px)
1353
+ "panX": false, // X축 팬 가능 여부
1354
+ "panY": false, // Y축 팬 가능 여부
1355
+ "wheelStep": 0.25, // 줌 휠 단계
1356
+ "scrollbarX": { // X축 스크롤바 설정
1357
+ "height": 6, // 스크롤바 높이 (px)
1358
+ "gripScale": 0 // 그립 크기 비율
1359
+ },
1360
+ "scrollbarY": { // Y축 스크롤바 설정
1361
+ "gripScale": 0 // 그립 크기 비율
1362
+ },
1363
+ "zoomOutButtonHidden": true // 줌 아웃 버튼 숨김 여부
1364
+ },
1365
+
1366
+ // X축 설정
1367
+ "xAxis": {
1368
+ "type": "category", // 축 타입: 'category' (필수)
1369
+ "valueField": "hour", // X축 값 필드명 (chartData의 필드명과 일치해야 함)
1370
+ "categories": ["0", "1", "2"], // 카테고리 명시적 순서 지정 (선택사항, 생략 시 데이터에서 자동 추출)
1371
+ "gridHidden": true, // 격자선 숨김 여부
1372
+ "labelHidden": false, // 라벨 숨김 여부
1373
+ "label": {
1374
+ "fontSize": 11, // 라벨 폰트 크기
1375
+ "maxWidth": 60, // 라벨 최대 너비 (px)
1376
+ "paddingTop": 5, // 라벨 상단 패딩
1377
+ "fontWeight": "normal" // 라벨 폰트 두께
1378
+ },
1379
+ "renderer": {
1380
+ "opposite": true, // X축을 상단에 배치 (기본값: false)
1381
+ "minGridDistance": 20 // 격자선 최소 간격
1382
+ },
1383
+ "startLocation": 0, // 축 시작 위치 (0~1)
1384
+ "endLocation": 1 // 축 끝 위치 (0~1)
1385
+ },
1386
+
1387
+ // Y축 설정
1388
+ "yAxis": {
1389
+ "type": "category", // 축 타입: 'category' (필수)
1390
+ "valueField": "day", // Y축 값 필드명 (chartData의 필드명과 일치해야 함)
1391
+ "categories": ["day_0", "day_1"], // 카테고리 명시적 순서 지정 (선택사항)
1392
+ "gridHidden": true, // 격자선 숨김 여부
1393
+ "labelHidden": false, // 라벨 숨김 여부
1394
+ "label": {
1395
+ "fontSize": 11, // 라벨 폰트 크기
1396
+ "maxWidth": 70, // 라벨 최대 너비 (px)
1397
+ "fontWeight": "bold" // 라벨 폰트 두께
1398
+ },
1399
+ "renderer": {
1400
+ "inversed": true, // Y축 반전 (기본값: true, 위→아래 순서)
1401
+ "minGridDistance": 20 // 격자선 최소 간격
1402
+ },
1403
+ "startLocation": 0, // 축 시작 위치
1404
+ "endLocation": 1 // 축 끝 위치
1405
+ },
1406
+
1407
+ // 시리즈 설정 (단일 ColumnSeries)
1408
+ "series": {
1409
+ "categoryXField": "hour", // X축 카테고리 필드 (기본값: xAxis.valueField)
1410
+ "categoryYField": "day", // Y축 카테고리 필드 (기본값: yAxis.valueField)
1411
+ "valueField": "value", // 값 필드 (기본값: "value")
1412
+ "columns": {
1413
+ "strokeWidth": 2, // 셀 테두리 두께
1414
+ "cornerRadius": 0, // 셀 모서리 둥글기
1415
+ "hover": {
1416
+ "enabled": true, // 호버 효과 활성화
1417
+ "scale": 1, // 호버 시 확대 비율
1418
+ "color": "#d50000" // 호버 시 테두리 색상
1419
+ }
1420
+ },
1421
+ "tooltip": {
1422
+ "type": "CI", // 'CI' (Individual) 고정 — 각 셀별 툴팁
1423
+ "enabled": true, // 툴팁 활성화
1424
+ "header": { "name": "{day} {hour}h" }, // 헤더 텍스트 ({}로 필드 참조)
1425
+ "body": {
1426
+ "name": "", // 바디 이름 (선택사항)
1427
+ "value": "{value} {l.unit}" // 바디 값 ({l.xxx}로 locale 참조)
1428
+ },
1429
+ "label": { "fontSize": 11 } // 툴팁 폰트 크기
1430
+ },
1431
+ "animation": {
1432
+ "duration": 500, // 애니메이션 지속 시간 (ms)
1433
+ "delay": 100 // 애니메이션 지연 시간 (ms)
1434
+ }
1435
+ },
1436
+
1437
+ // 히트맵 색상 규칙 (필수)
1438
+ "heatRule": {
1439
+ "key": "fill", // 변경할 속성: 'fill' (색상)
1440
+ "dataField": "value", // 값 필드 (기본값: "value")
1441
+ "minColor": "#fce4ec", // 최소값 색상 (연한색)
1442
+ "maxColor": "#c62828", // 최대값 색상 (진한색)
1443
+ "minValue": 0, // 최소값 직접 지정 (선택사항, 생략 시 데이터에서 자동 계산)
1444
+ "maxValue": 10 // 최대값 직접 지정 (선택사항)
1445
+ },
1446
+
1447
+ // 범례 설정 (히트맵 그래디언트 범례)
1448
+ "legend": {
1449
+ "orientation": "horizontal", // 방향: 'horizontal' | 'vertical'
1450
+ "labelText": "{l.legendLabel}", // 범례 라벨 ({l.xxx}로 locale 참조)
1451
+ "labelFontSize": 12, // 라벨 폰트 크기
1452
+ "labelFontWeight": "normal", // 라벨 폰트 두께
1453
+ "valueLabelFontSize": 11, // 값 라벨 폰트 크기
1454
+ "legendWidth": 200, // 범례 너비 (px)
1455
+ "barHeight": 16, // 범례 바 높이 (px)
1456
+ "paddingTop": 10, // 상단 패딩
1457
+ "startText": "Low", // 시작 라벨 텍스트
1458
+ "endText": "High", // 끝 라벨 텍스트
1459
+ "stepCount": 5, // 범례 단계 수
1460
+ "reversed": false // 범례 방향 반전
1461
+ }
1462
+ }
1463
+ `),
1464
+ chartData: z.array(z.record(z.any())).optional().describe(`히트맵에 표시할 데이터 배열입니다. 각 항목은 X축 카테고리, Y축 카테고리, 값 필드를 포함해야 합니다.
1465
+ 예시)
1466
+ [
1467
+ { "hour": "0", "day": "day_0", "value": 3 },
1468
+ { "hour": "1", "day": "day_0", "value": 0 },
1469
+ { "hour": "2", "day": "day_0", "value": 5 },
1470
+ { "hour": "0", "day": "day_1", "value": 1 },
1471
+ { "hour": "1", "day": "day_1", "value": 7 }
1472
+ ]`),
1473
+ chartLocaleProps: z.array(z.object({
1474
+ name: z.string(),
1475
+ locale: z.string()
1476
+ })).optional().describe(`차트에 적용할 다국어 플레이스홀더 목록입니다. {l.xxx} 형태로 chartProps에서 참조합니다.
1477
+ 예시)
1478
+ [
1479
+ { "name": "l.unit", "locale": "건" },
1480
+ { "name": "l.legendLabel", "locale": "인시던트 수" }
1481
+ ]`),
1482
+ chartLocaleData: z.array(z.object({
1483
+ name: z.string().describe("chartData에 정의된 필드 이름"),
1484
+ value: z.string().describe("chartData에 정의된 필드 값 전체 또는 일부"),
1485
+ locale: z.string().describe("chartData에 정의된 필드 다국어 값으로 value에 다국어 값을 적용")
1486
+ })).optional().describe(`차트 데이터에 다국어를 적용합니다.
1487
+ 예시)
1488
+ [
1489
+ { "name": "day", "value": "day_0", "locale": "월요일" }
1490
+ ]`),
1491
+ chartLocaleMessage: z.array(z.object({
1492
+ name: z.string().describe("카테고리 키 (chartData의 카테고리 값과 일치)"),
1493
+ locale: z.string().describe("표시할 다국어 텍스트")
1494
+ })).optional().describe(`카테고리 라벨을 다국어 텍스트로 치환합니다. Y축 또는 X축 카테고리의 키를 표시 텍스트로 변환할 때 사용합니다.
1495
+ 예시)
1496
+ [
1497
+ { "name": "day_0", "locale": "월요일 4/14" },
1498
+ { "name": "day_1", "locale": "화요일 4/15" }
1499
+ ]`),
1500
+ chartTooltipDetailText: z.string().optional().describe("툴팁 하단에 표시할 상세 안내 텍스트"),
1501
+ chartNoRowsText: z.string().optional().describe("데이터 없을 때 표시할 텍스트"),
1502
+ langCd: z.enum(['ko', 'en', 'ja', 'zh', 'ar']).optional().describe("언어 코드"),
1503
+ isDarkMode: z.boolean().optional().describe("다크 모드 활성화 여부"),
1504
+ onChartClick: z.string().optional().describe('셀 클릭 시 호출되는 콜백 함수(stringified). 클릭 시 { xField, yField, valueField, from: "heatmap" } 객체를 전달합니다.')
1505
+ });
1340
1506
  // Chart 컴포넌트 함수 - 배열 반환
1341
1507
  export function createChartComponent() {
1342
1508
  return [
@@ -1639,5 +1805,58 @@ export function createChartComponent() {
1639
1805
  };
1640
1806
  }
1641
1807
  },
1808
+ {
1809
+ name: "createHeatmapChart",
1810
+ description: `히트맵 차트 컴포넌트 - 2차원 매트릭스 형태의 데이터 시각화 지원
1811
+
1812
+ **이 차트 컴포넌트는 amCharts5를 기반으로 구현되었습니다.**
1813
+
1814
+ 히트맵 차트는 X축과 Y축 모두 카테고리(category) 축을 사용하며,
1815
+ 각 셀의 값에 따라 minColor ~ maxColor 사이의 그래디언트로 색상이 결정됩니다.
1816
+ 인시던트 발생 패턴(시간×요일), 리소스 사용량 등 2차원 분포 시각화에 적합합니다.
1817
+
1818
+ **import:**
1819
+ \`\`\`javascript
1820
+ import { useCommonComponents } from '@opsnow-common/opsnow-finops-common-ui-loader';
1821
+ const { OpsnowCommonHeatmapChart } = useCommonComponents();
1822
+ \`\`\``,
1823
+ parameters: HeatmapChartSchema,
1824
+ handler: async (args) => {
1825
+ const props = [];
1826
+ if (args.chartId)
1827
+ props.push(`chartId="${args.chartId}"`);
1828
+ if (args.chartProps)
1829
+ props.push(`chartProps={${args.chartProps}}`);
1830
+ if (args.chartData)
1831
+ props.push(`chartData={${args.chartData}}`);
1832
+ else
1833
+ props.push(`chartData={[]}`);
1834
+ if (args.chartLocaleProps)
1835
+ props.push(`chartLocaleProps={${args.chartLocaleProps}}`);
1836
+ if (args.chartLocaleData)
1837
+ props.push(`chartLocaleData={${args.chartLocaleData}}`);
1838
+ if (args.chartLocaleMessage)
1839
+ props.push(`chartLocaleMessage={${args.chartLocaleMessage}}`);
1840
+ if (args.chartTooltipDetailText)
1841
+ props.push(`chartTooltipDetailText="${args.chartTooltipDetailText}"`);
1842
+ if (args.chartNoRowsText)
1843
+ props.push(`chartNoRowsText="${args.chartNoRowsText}"`);
1844
+ if (args.langCd)
1845
+ props.push(`langCd="${args.langCd}"`);
1846
+ if (args.isDarkMode !== undefined)
1847
+ props.push(`isDarkMode={${args.isDarkMode}}`);
1848
+ if (args.onChartClick)
1849
+ props.push(`onChartClick={${args.onChartClick}}`);
1850
+ const code = `<OpsnowCommonHeatmapChart ${props.join(' ')} />`;
1851
+ return {
1852
+ content: [
1853
+ {
1854
+ type: "text",
1855
+ text: `\`\`\`jsx\n${code}\n\`\`\``
1856
+ }
1857
+ ]
1858
+ };
1859
+ }
1860
+ },
1642
1861
  ];
1643
1862
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opsnow-mcp/opsnow-mcp-common-ui-server",
3
- "version": "1.0.23",
3
+ "version": "1.0.25",
4
4
  "type": "module",
5
5
  "main": "index.js",
6
6
  "bin": {