@opsnow-mcp/opsnow-mcp-common-ui-server 1.0.36 → 1.0.37

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.
@@ -1610,12 +1610,12 @@ const handleDownload = useCallback(() => {
1610
1610
  export const CurrencySwitcherExamples = [
1611
1611
  {
1612
1612
  title: 'header-currency-switcher',
1613
- code: `// 공통 헤더(header)에서 사용법 — 기본 배선 (opsnow-finops-common-header >= 1.0.20)
1613
+ code: `// 공통 헤더(header)에서 사용법 — 기본 배선
1614
1614
  // 헤더는 통화 상태/환율 데이터를 갖지 않는다 — 앱이 관리해 props로 주입한다
1615
1615
  import OpsnowFinopsCommonHeader from '@opsnow-common/opsnow-finops-common-header'
1616
1616
 
1617
1617
  const [currency, setCurrency] = useState('USD')
1618
- const [historyQuery, setHistoryQuery] = useState({ currency: 'USD', period: 24 })
1618
+ const [historyQuery, setHistoryQuery] = useState({ currency: 'USD', period: 6 })
1619
1619
  const historySeries = useMemo(
1620
1620
  () => buildSeries(historyQuery.currency, historyQuery.period), // 실사용: BE 조회
1621
1621
  [historyQuery]
@@ -1665,13 +1665,11 @@ const isCurrencyEnabledPath = CURRENCY_ENABLED_ROUTES.some(
1665
1665
  onHistoryQueryChange: refetchHistory,
1666
1666
 
1667
1667
  nativeCurrency: 'KRW', // 무환산(수집) 통화
1668
- historyMode: 'popup', // 히스토리 항상 중앙 팝업
1669
- simulationTarget: { currency: 'USD', payer: 'Azure' },
1670
1668
  }}
1671
1669
  // 적용 대상 외 메뉴에서 뜨는 안내 문구를 앱 문구로 덮어쓰기
1672
1670
  currencySwitcherDisabledTooltip={t('header.currencyNotSupported')}
1673
1671
  />`,
1674
- description: "공통 헤더(header)에서 사용법 — 멀티 CSP(수집 통화 혼재) 조합 예제입니다. 'multi-csp-native-currency' 예제와 같은 props(nativeCurrency/historyMode/simulationTarget 포함)를 currencySwitcherProps 안에 그대로 넣으면 되고, 비활성 안내 문구는 currencySwitcherDisabledTooltip으로 앱 번역으로 덮어쓸 수 있습니다. [검색 키워드: 헤더 멀티 CSP, 헤더 통화 전환, 헤더에서 사용법, 수집 통화, currencySwitcherDisabledTooltip]"
1672
+ description: "공통 헤더(header)에서 사용법 — 멀티 CSP(수집 통화 혼재) 조합 예제입니다. 'multi-csp-native-currency' 예제와 같은 props(nativeCurrency 포함)를 currencySwitcherProps 안에 그대로 넣으면 되고, 비활성 안내 문구는 currencySwitcherDisabledTooltip으로 앱 번역으로 덮어쓸 수 있습니다. [검색 키워드: 헤더 멀티 CSP, 헤더 통화 전환, 헤더에서 사용법, 수집 통화, currencySwitcherDisabledTooltip]"
1675
1673
  },
1676
1674
  {
1677
1675
  title: 'standalone-props-reference',
@@ -1688,7 +1686,7 @@ const rateInfo = {
1688
1686
  source: '한국수출입은행', // 출처 (선택)
1689
1687
  rates: { USD: 1, KRW: 1450, JPY: 156.58, VND: 25508, AED: 3.67 }, // 기준 통화(USD) 1단위당 대표 환율
1690
1688
  details: { // (선택) 통화별 Payer/Invoice 상세 환율
1691
- KRW: [ // 2건 이상이면 '다중 환율' — 배지 · 현재 적용 환율 목록 · 히스토리 팝업
1689
+ KRW: [ // 2건 이상이면 '다중 환율' — 'N개 환율' 배지 · 히스토리 다중 시리즈
1692
1690
  { payer: 'Payer 1234', invoice: 'Invoice 0000', rate: 1450 },
1693
1691
  { payer: 'Payer 5678', invoice: 'Invoice 9999', rate: 1462 },
1694
1692
  ],
@@ -1704,18 +1702,15 @@ const rateInfo = {
1704
1702
  rateInfo={rateInfo}
1705
1703
  historySeries={historySeries}
1706
1704
  onHistoryQueryChange={(code, months) => {
1707
- // 히스토리 열림/팝업 통화·기간 셀렉트 변경 시 통지 → BE 재조회 후 주입
1708
- // historySeries: [{ name, label?, current?, points: [{ label, rate }, ...] }, ...]
1705
+ // 팝오버 열림·통화 선택·기간 셀렉트 변경 시 통지 → BE 재조회 후 주입
1706
+ // historySeries: [{ name, label?, currency?, points: [{ label, rate }, ...] }, ...]
1709
1707
  fetchHistory(code, months).then(setHistorySeries)
1710
1708
  }}
1711
- onCustomRateChange={(rate, context) => {
1712
- // what-if 시뮬레이션 값 (저장되지 않음, onChange 미호출)
1713
- }}
1714
1709
  />`,
1715
- description: '통화 전환 드롭다운 + 환율 팝오버 + 환율 히스토리 props 참고용 예제입니다 (⚠️ 실서비스 배치는 header-currency-switcher 예제처럼 공통 헤더로). 환율 데이터는 컴포넌트가 조회하지 않으므로 BE 환율 API 응답을 rateInfo/historySeries props로 주입하고, onHistoryQueryChange(currency, months)로 조회 조건을 통지받아 재조회합니다. rateInfo.details가 2건 이상인 통화는 다중 환율로 취급되어 히스토리가 화면 중앙 팝업(차트/테이블)으로 열리고, 단일 환율·기준 통화는 팝오버 인라인 차트로 열립니다. [검색 키워드: 통화 전환, 환율, 환율 필터, 통화 필터, 통화 변환, 환율 히스토리, 다중 환율, currency, exchange rate]'
1710
+ description: '통화 전환 드롭다운 + 환율 팝오버 + 환율 히스토리 props 참고용 예제입니다 (⚠️ 실서비스 배치는 header-currency-switcher 예제처럼 공통 헤더로). 환율 데이터는 컴포넌트가 조회하지 않으므로 BE 환율 API 응답을 rateInfo/historySeries props로 주입하고, onHistoryQueryChange(currency, months)로 조회 조건을 통지받아 재조회합니다. 히스토리는 팝오버 하단에 상시 인라인으로 노출되며(추가 팝업 없음) 기본은 최근 6개월 테이블(연월=컬럼, Payer/Invoice=행), 차트 뷰로 전환하면 범례 클릭으로 시리즈를 필터링합니다. [검색 키워드: 통화 전환, 환율, 환율 필터, 통화 필터, 통화 변환, 환율 히스토리, 다중 환율, currency, exchange rate]'
1716
1711
  },
1717
1712
  {
1718
- title: 'custom-options-no-detail',
1713
+ title: 'custom-options-no-history',
1719
1714
  code: `// ⚠️ 단독 배치는 props 참고용 — 실서비스 배치는 header-currency-switcher 예제 사용
1720
1715
  <OpsnowCommonCurrencySwitcher
1721
1716
  value={currency}
@@ -1728,10 +1723,9 @@ const rateInfo = {
1728
1723
  ]}
1729
1724
  baseCurrency="USD"
1730
1725
  size="small"
1731
- showDetail={false}
1732
1726
  showHistory={false}
1733
1727
  />`,
1734
- description: '팝오버 통화 목록을 커스텀하고 환율 설정(시뮬레이션)·히스토리 없이 small 크기 트리거로 사용하는 예제입니다.'
1728
+ description: '팝오버 통화 목록을 커스텀하고 하단 히스토리 섹션 없이(showHistory=false, 통화 목록만 노출) small 크기 트리거로 사용하는 예제입니다.'
1735
1729
  },
1736
1730
  {
1737
1731
  title: 'multi-csp-native-currency',
@@ -1755,13 +1749,11 @@ const multiCspRateInfo = {
1755
1749
  value={currency}
1756
1750
  onChange={handleCurrencyChange}
1757
1751
  rateInfo={multiCspRateInfo}
1758
- historySeries={historySeries}
1752
+ historySeries={historySeries} // 시리즈에도 currency: 'KRW' 를 담아 행 라벨에 병기
1759
1753
  onHistoryQueryChange={handleHistoryQueryChange}
1760
1754
  nativeCurrency="KRW" // 무환산(수집) 통화 — 앵커(baseCurrency=USD)와 분리
1761
- historyMode="popup" // 히스토리 항상 중앙 팝업 (USD 뷰 포함)
1762
- simulationTarget={{ currency: 'USD', payer: 'Azure' }} // 시뮬레이션 대상을 실제 환산되는 CSP로 고정
1763
1755
  />`,
1764
- description: "수집 통화 분리 — 멀티 CSP 혼재 예제입니다 (opsnow-common-dropdown >= 1.0.32). 기준 통화(USD)는 환율 앵커로 유지하되 '변환 없음(Default)' 문구는 nativeCurrency='KRW' 행에 붙습니다. USD 행에는 'N개 환율' 배지가 붙고, USD 선택 '현재 적용 환율'에 CSP별 환산 환율이 단위 통화(KRW)로 나열됩니다. historyMode='popup'이면 선택 통화와 무관하게 히스토리가 항상 중앙 팝업으로 열리고, simulationTarget으로 환율 설정(시뮬레이션) 대상이 실제 환산되는 Payer에 고정됩니다. [검색 키워드: 멀티 CSP, 수집 통화, 무환산, nativeCurrency, historyMode, simulationTarget]"
1756
+ description: "수집 통화 분리 — 멀티 CSP 혼재 예제입니다. 기준 통화(USD)는 환율 앵커로 유지하되 '변환 없음(Default)' 문구는 nativeCurrency='KRW' 행에 붙습니다. USD 행에는 'N개 환율' 배지가 붙고, 히스토리 테이블 라벨에는 실제 환산 통화가 'Azure · Invoice 1111 (KRW)'처럼 병기됩니다 USD로 보는 자체가 KRW 환율로 나누는 환산이기 때문입니다. 시리즈에도 currency 필드를 담아 주입하세요. [검색 키워드: 멀티 CSP, 수집 통화, 무환산, nativeCurrency, 단위 통화]"
1765
1757
  },
1766
1758
  ];
1767
1759
  export const ToggleButtonExamples = [
@@ -290,30 +290,32 @@ export const InsightContentSchema = z.object({
290
290
  emptyMessage: z.string().optional().describe("insightText가 없을 때 표시할 빈 메시지"),
291
291
  sx: z.string().optional().describe("MUI sx 스타일 객체(JSX/문자열)"),
292
292
  });
293
- // CurrencySwitcher 컴포넌트 관련 스키마 정의 (통화 전환 드롭다운 + 환율 팝오버 + 환율 히스토리, opsnow-common-dropdown >= 1.0.29 · nativeCurrency/historyMode/simulationTarget은 >= 1.0.32)
293
+ // CurrencySwitcher 컴포넌트 관련 스키마 정의 (통화 전환 드롭다운 + 환율 팝오버 + 환율 히스토리 히스토리는 팝오버 하단 인라인)
294
294
  // 공통 헤더(OpsnowFinopsCommonHeader) 배선은 헤더의 props(showCurrencySwitcher 등)라 이 스키마에 없음 — createCurrencySwitcher description·헤더 예제 참고
295
295
  export const CurrencySwitcherSchema = z.object({
296
296
  value: z.string().describe("현재 표시 통화 코드 상태 변수명 (controlled, 예: currency)"),
297
297
  onChange: z.string().describe("통화 선택 시 호출되는 핸들러 함수명 — (currency, rate) => void 형태, rate는 기준 통화 1단위당 대표 환율 (예: handleCurrencyChange)"),
298
- rateInfo: z.string().optional().describe("기준일 환율 정보 객체 변수명 — { baseDate, source?, rates, details? } 형태로 BE 환율 API 응답을 주입. details는 통화별 Payer/Invoice 상세 환율(Array<{ payer, invoice?, rate, currency? }>)로 2건 이상이면 다중 환율 통화로 취급 (없으면 목록에 환율이 '-'로 표시). 수집 통화가 CSP별로 다른 멀티 CSP 케이스는 details를 기준 통화 키(details.USD)에 담고 행마다 currency 필드로 행 단위 통화를 표기 — nativeCurrency와 함께 사용 (예: rateInfo)"),
299
- historySeries: z.string().optional().describe("환율 히스토리 시리즈 배열 변수명 (controlled) — Array<{ name, label?, current?, points: Array<{ label, rate }> }>, onHistoryQueryChange로 통지받은 조건 기준으로 BE 재조회 후 주입. 인라인 차트는 번째 시리즈(대표 환율)만 사용 (예: historySeries)"),
300
- onHistoryQueryChange: z.string().optional().describe("히스토리 조회 조건 통지 핸들러 함수명 — (currency, months) => void 형태, 히스토리 열림/팝업 통화·기간 셀렉트 변경 시 호출 → 소비 측이 BE 재조회 후 historySeries 갱신 (예: handleHistoryQueryChange)"),
301
- historyPeriodOptions: z.string().optional().describe("히스토리 팝업 기간 옵션 배열 변수명 — number[] (기본값: [6, 12, 24])"),
302
- defaultHistoryPeriod: z.number().optional().describe("히스토리 기본 조회 기간(개월, 기본값: 24)"),
298
+ rateInfo: z.string().optional().describe("기준일 환율 정보 객체 변수명 — { baseDate, source?, rates, details? } 형태로 BE 환율 API 응답을 주입. details는 통화별 Payer/Invoice 상세 환율(Array<{ payer, invoice?, rate, currency? }>)로 2건 이상이면 다중 환율 통화로 취급('N개 환율' 배지, 없으면 목록에 환율이 '-'로 표시). 수집 통화가 CSP별로 다른 멀티 CSP 케이스는 details를 기준 통화 키(details.USD)에 담고 행마다 currency 필드로 행 단위 통화를 표기 — nativeCurrency와 함께 사용 (예: rateInfo)"),
299
+ historySeries: z.string().optional().describe("환율 히스토리 시리즈 배열 변수명 (controlled) — Array<{ name, label?, currency?, points: Array<{ label, rate }> }>, onHistoryQueryChange로 통지받은 조건 기준으로 BE 재조회 후 주입. 테이블 뷰는 전체 시리즈를 행으로, 차트 뷰는 범례에서 선택된 시리즈만 표시. 시리즈 순서가 곧 테이블 행 순서·색상 순서이므로 대표 환율을 번째로 정렬 (예: historySeries)"),
300
+ onHistoryQueryChange: z.string().optional().describe("히스토리 조회 조건 통지 핸들러 함수명 — (currency, months) => void 형태, 팝오버 열림·통화 선택·기간 셀렉트 변경 시 호출 → 소비 측이 BE 재조회 후 historySeries 갱신 (예: handleHistoryQueryChange)"),
301
+ historyPeriodOptions: z.string().optional().describe("히스토리 기간 옵션 배열 변수명 — number[] (기본값: [6, 12, 24])"),
302
+ defaultHistoryPeriod: z.number().optional().describe("히스토리 기본 조회 기간(개월, 기본값: 6)"),
303
303
  currencyOptions: z.string().optional().describe("팝오버 통화 목록 배열 변수명 — Array<{ code, symbol, digits? }> (기본: USD/KRW/JPY/VND/AED)"),
304
304
  baseCurrency: z.string().optional().describe("변환 기준 통화 코드 — 환율이 '1 {base} = X'로 저장되는 앵커. 무환산 표기와는 별개 개념(nativeCurrency 참고) (기본값: 'USD')"),
305
- nativeCurrency: z.string().optional().describe("무환산(수집) 통화 코드 — '변환 없음(Default)' 문구가 붙는 행. 수집 통화가 기준 통화와 다른 멀티 CSP 조합(예: Azure/GCP KRW 수집)에서 baseCurrency(환율 앵커)와 분리 지정 (기본값: baseCurrency, dropdown >= 1.0.32)"),
306
- defaultTargetCurrency: z.string().optional().describe("기준 통화 선택 중일 환율 설정이 다룰 초기 비교 통화 코드"),
307
- simulationTarget: z.string().optional().describe("환율 설정(시뮬레이션) 대상 지정 객체 변수명{ currency, payer?, invoice? } 형태, 기준 통화 뷰에서 실제 환산되는 CSP(Payer) 고정하며 payer/invoice 일치 행이 사전 선택됨 (예: simulationTarget, dropdown >= 1.0.32)"),
308
- customRate: z.string().optional().describe("직접 입력 시뮬레이션 환율 상태 변수명 (number | null, controlled 모드 미지정 내부 관리)"),
309
- onCustomRateChange: z.string().optional().describe("직접 입력 환율 변경 핸들러 함수명 — (rate, context) => void 형태, 저장되지 않는 what-if 값 통지 (onChange는 호출되지 않음)"),
310
- showDetail: z.boolean().optional().describe("환율 설정(직접 입력 시뮬레이션) 섹션 노출 여부 (기본값: true)"),
311
- showHistory: z.boolean().optional().describe("월별 환율 히스토리 링크·인라인 차트·팝업 노출 여부 (기본값: true)"),
312
- historyMode: z.enum(["auto", "popup"]).optional().describe("히스토리 열림 방식 — 'popup'이면 선택 통화·환율 수와 무관하게 항상 화면 중앙 팝업으로 열고 통화 셀렉트에 기준 통화 포함, 'auto'는 다중 환율=팝업/단일 환율·기준 통화=인라인 분기 (기본값: 'auto', dropdown >= 1.0.32)"),
313
- renderChart: z.string().optional().describe("히스토리 팝업 차트 교체 슬롯 함수명 — (series, currency) => ReactNode 형태, 체크된(보이는) 시리즈만 전달 (기본: 내장 SVG 멀티라인 차트)"),
314
- labels: z.string().optional().describe("문구 개별 커스텀 객체 변수명 — Partial<CurrencySwitcherLabels>, i18n(ko/en/ja) 번역 위에 항목별로 덮어씀 ({placeholder} 템플릿 치환 지원)"),
305
+ nativeCurrency: z.string().optional().describe("무환산(수집) 통화 코드 — '변환 없음(Default)' 문구가 붙는 행. 수집 통화가 기준 통화와 다른 멀티 CSP 조합(예: Azure/GCP KRW 수집)에서 baseCurrency(환율 앵커)와 분리 지정 (기본값: baseCurrency)"),
306
+ showHistory: z.boolean().optional().describe("팝오버 하단 히스토리 섹션 노출 여부 (기본값: true)"),
307
+ renderChart: z.string().optional().describe("히스토리 차트 교체 슬롯 함수명(series, currency) => ReactNode 형태, 범례에서 선택된(보이는) 시리즈만 전달 (기본: 내장 SVG 멀티라인 차트)"),
308
+ labels: z.string().optional().describe("문구 개별 커스텀 객체 변수명 — Partial<CurrencySwitcherLabels>, 우선순위 labels > 앱 i18n 리소스(common.currency_switcher.*) > 패키지 내장 ko/en/ja ({placeholder} 템플릿 치환 지원). 기준 통화 행 문구는 baseCurrencyDescription, 히스토리 테이블 컬럼 머리는 payerColumnLabel 키로 교체"),
315
309
  size: z.enum(["small", "medium"]).optional().describe("트리거 크기"),
316
310
  disabled: z.boolean().optional().describe("비활성화 여부"),
311
+ // [다음 버전 재도입 예정] 환율 설정(직접 입력 · 시뮬레이션) 영역 props — dropdown 컴포넌트 쪽
312
+ // '[다음 버전 재도입 예정]' 주석 블록이 되살아나면 아래 주석 해제 (핸들러 쪽 주석 블록도 함께)
313
+ // defaultTargetCurrency: z.string().optional().describe("기준 통화 선택 중일 때 환율 설정이 다룰 초기 비교 통화 코드"),
314
+ // simulationTarget: z.string().optional().describe("환율 설정(시뮬레이션) 대상 지정 객체 변수명 — { currency, payer?, invoice? } 형태, 기준 통화 뷰에서 실제 환산되는 CSP(Payer)로 고정하며 payer/invoice 일치 행이 사전 선택됨 (예: simulationTarget)"),
315
+ // customRate: z.string().optional().describe("직접 입력 시뮬레이션 환율 상태 변수명 (number | null, controlled 모드 — 미지정 시 내부 관리)"),
316
+ // onCustomRateChange: z.string().optional().describe("직접 입력 환율 변경 핸들러 함수명 — (rate, context) => void 형태, 저장되지 않는 what-if 값 통지 (onChange는 호출되지 않음)"),
317
+ // showDetail: z.boolean().optional().describe("환율 설정(직접 입력 시뮬레이션) 섹션 노출 여부 (기본값: true)"),
318
+ // historyMode: z.enum(["auto", "popup"]).optional().describe("히스토리 열림 방식 — 'popup'이면 항상 화면 중앙 팝업, 'auto'는 다중 환율=팝업/단일 환율·기준 통화=인라인 분기 (기본값: 'auto')"),
317
319
  });
318
320
  // Forms 컴포넌트 함수 - 배열 반환
319
321
  export function createFormsComponent() {
@@ -564,7 +566,7 @@ export function createFormsComponent() {
564
566
  props.push(`adornmentIconName=\"${args.adornmentIconName}\"`);
565
567
  if (args.adornmentPosition)
566
568
  props.push(`adornmentPosition=\"${args.adornmentPosition}\"`);
567
- // readOnly는 톱레벨 prop이 아니라 inputProps 경로로만 동작 (MUI 7: rest prop은 FormControl로 가서 input에 미도달)
569
+ // readOnly는 톱레벨 prop이 아니라 inputProps 경로로만 동작 (MUI: rest prop은 FormControl로 가서 input에 미도달)
568
570
  if (args.readOnly && !args.inputProps)
569
571
  props.push(`inputProps={{ readOnly: true }}`);
570
572
  if (args.adornmentLabelColor)
@@ -1121,40 +1123,39 @@ export function createFormsComponent() {
1121
1123
  },
1122
1124
  {
1123
1125
  name: "createCurrencySwitcher",
1124
- description: `CurrencySwitcher 컴포넌트 - 통화 전환 드롭다운 + 환율 팝오버 + 환율 히스토리 — ⚠️ 실서비스 배치는 반드시 공통 헤더(OpsnowFinopsCommonHeader)의 currencySwitcherProps로만 (opsnow-common-dropdown >= 1.0.29 · nativeCurrency/historyMode/simulationTarget은 >= 1.0.32)
1126
+ description: `CurrencySwitcher 컴포넌트 - 통화 전환 드롭다운 + 환율 팝오버 + 환율 히스토리 — ⚠️ 실서비스 배치는 반드시 공통 헤더(OpsnowFinopsCommonHeader)의 currencySwitcherProps로만
1125
1127
 
1126
1128
  ⚠️ **배치 규칙: 실서비스에서는 반드시 OpsnowFinopsCommonHeader의 currencySwitcherProps로만 배치합니다. 단독 \`<OpsnowCommonCurrencySwitcher>\` 사용은 props 참고용 데모에 한합니다.**
1127
1129
 
1128
1130
  트리거에 현재 선택된 통화가 표시되고, 클릭하면 팝오버에서 적용 환율 기준일 안내와
1129
1131
  통화 목록(클릭 시 즉시 적용)을 제공합니다. Payer/Invoice별 환율이 여러 개인 통화는
1130
- 'N개 환율' 배지와 '현재 적용 환율' 목록이 표시되고, 환율 설정에서 Payer를 골라
1131
- what-if 시뮬레이션을 입력할 있습니다.
1132
+ 'N개 환율' 배지가 표시됩니다. 팝오버 하단에 월별 환율 히스토리가 상시 인라인으로
1133
+ 노출되며(추가 팝업 없음), 기본은 최근 6개월 테이블(연월=컬럼, Payer/Invoice=행)이고
1134
+ 차트 뷰로 전환하면 하단 범례 클릭으로 시리즈를 필터링할 수 있습니다.
1132
1135
 
1133
1136
  **데이터 주입 규칙 (중요):**
1134
1137
  - 환율 데이터는 컴포넌트가 조회하지 않습니다 — BE 환율 API 응답을 rateInfo / historySeries props로 주입하고, 조회 조건이 바뀌면 onHistoryQueryChange(currency, months)로 통지받아 재조회하세요.
1135
1138
  - rateInfo: { baseDate: 'YYYY-MM-DD', source?: '출처', rates: { USD: 1, KRW: 1450, ... }, details?: { KRW: [{ payer, invoice?, rate }, ...] } } — 기준 통화 1단위당 환율, details의 통화별 배열이 2건 이상이면 다중 환율 통화로 취급
1136
- - 멀티 CSP(수집 통화 혼재, 예: AWS=USD·Azure/GCP=KRW 수집): details를 기준 통화 키에 담고 행마다 currency 필드로 행 단위 통화 표기 — details: { USD: [{ payer: 'Azure', rate: 1508.8, currency: 'KRW' }, ...] }, nativeCurrency='KRW'와 함께 사용
1137
- - historySeries: [{ name, label?, current?, points: [{ label, rate }, ...] }, ...] — 모든 시리즈는 같은 월 구간으로 정렬, 대표 환율이 번째 시리즈가 되도록 정렬 (인라인 차트는번째 시리즈만 사용)
1139
+ - 멀티 CSP(수집 통화 혼재, 예: AWS=USD·Azure/GCP=KRW 수집): details를 기준 통화 키에 담고 행마다 currency 필드로 행 단위 통화 표기 — details: { USD: [{ payer: 'Azure', rate: 1508.8, currency: 'KRW' }, ...] }, nativeCurrency='KRW'와 함께 사용. 히스토리 시리즈에도 currency: 'KRW'를 담으면 테이블 행 라벨에 'Azure · Invoice 1111 (KRW)'처럼 병기됩니다
1140
+ - historySeries: [{ name, label?, currency?, points: [{ label, rate }, ...] }, ...] — 모든 시리즈는 같은 월 구간으로 정렬(테이블 컬럼은 가장 시리즈의 연월 기준), 시리즈 순서가 테이블 행 순서·색상 순서이므로 대표 환율이 번째가 되도록 정렬
1138
1141
  - BE API 호출 시 axios 직접 import 금지 — getAxios() 공통 axios 사용
1139
1142
 
1140
- **히스토리 동작 분기 규칙:**
1143
+ **히스토리 표기 규칙:**
1141
1144
 
1142
- | 선택 통화 상태 | 히스토리 동작 | 조회 통화 | 표시 시리즈 |
1145
+ | 상황 | 히스토리 표기 | 조회 통화 | 표시 시리즈 |
1143
1146
  |------|------|------|------|
1144
- | historyMode='popup' 지정 (선택 통화·환율 수 무관) | 항상 화면 중앙 팝업통화 셀렉트에 기준 통화 포함 | 선택 통화 (기준 통화 포함) | 주입 시리즈 전체 (멀티 CSP 다중 시리즈) |
1145
- | 'auto' · 다중 환율 통화 선택 (details 2건 이상 — 기준 통화 포함) | 화면 중앙 팝업 (차트/테이블 전환, Payer/Invoice 검색·체크박스 팝오버는 열린 유지, 닫으면 복귀) | 선택 통화 | Payer/Invoice별 전체 시리즈 |
1146
- | 'auto' · 단일 환율 통화 선택 (예: JPY, VND) | 팝오버 안에 인라인 차트 펼침/접힘 (팝업 없음) | 선택 통화 | 번째 시리즈(대표 환율)만 |
1147
- | 'auto' · 기준 통화 선택 + details 없음 (예: USD) | 팝오버 안에 인라인 차트 펼침/접힘 (팝업 없음) | 선택 통화(기준 통화) | 번째 시리즈(대표 환율)만 |
1147
+ | 팝오버 열림 (통화·환율 수 무관) | 하단에 히스토리 섹션 상시 노출 추가 팝업 없음, 기본 뷰는 테이블 | 선택 통화 | 주입 시리즈 전체 |
1148
+ | 테이블 (기본) | 연월이 컬럼 헤더, Payer/Invoice 행. 컬럼은 고정되고 12/24개월은 가로 스크롤 | 선택 통화 | 주입 시리즈 전체 (범례 필터 영향 없음) |
1149
+ | 차트 | 멀티라인 차트 + 하단 범례. 범례 클릭 해당 시리즈가 차트에서 빠짐 (필터링) | 선택 통화 | 범례에서 선택된 시리즈만 |
1150
+ | 통화 변경 · 기간 변경 | onHistoryQueryChange(통화, 개월) 통지 소비자가 historySeries 갱신. 통화가 바뀌면 범례 필터 초기화 | 선택 통화 | 재조회 결과 |
1148
1151
 
1149
1152
  **동작 규칙:**
1150
1153
  - 통화 선택 시 onChange(currency, rate)가 호출됩니다 — 금액 표시 변환은 소비 프로젝트에서 rate로 처리
1151
- - 직접 입력 환율은 저장되지 않는 what-if 시뮬레이션 값으로 onCustomRateChange(rate, context)로만 통지되며, onChange(실제 적용)는 호출되지 않습니다
1152
- - 문구는 i18n 현재 언어(ko/en/ja)를 자동으로 따르고, labels prop으로 항목별 커스텀 가능
1154
+ - 문구는 i18n 현재 언어(ko/en/ja)를 자동으로 따르고, labels prop으로 항목별 커스텀 가능 (우선순위: labels > 앱 i18n 리소스 common.currency_switcher.* > 패키지 내장 문구. 기준 통화 행 문구는 baseCurrencyDescription, 히스토리 테이블 컬럼 머리는 payerColumnLabel 키)
1153
1155
 
1154
- **공통 헤더에서 쓰기 (OpsnowFinopsCommonHeader, opsnow-finops-common-header >= 1.0.20):**
1156
+ **공통 헤더에서 쓰기 (OpsnowFinopsCommonHeader):**
1155
1157
  - 실서비스 배치는 **무조건 공통 헤더를 통해서** 합니다 — 페이지 본문에 단독 배치하지 마세요 (단독 예제는 props 사용법 참고용)
1156
- - 헤더는 AI 버튼~알림 벨 사이 자리만 제공하고 상태·환율 데이터를 갖지 않습니다 — 앱이 관리하는 값을 currencySwitcherProps로 주입하면 내부 OpsnowCommonCurrencySwitcher에 그대로 전달됩니다 (위 props 규칙 동일 적용)
1157
- - **활성/비활성 정책**: 통화 변경은 '개요(OverView)' · '비용 분석(Analytics)' · '청구 내역(Billing Invoice)' · '비용 배분(Cost Allocation)' 4개 메뉴에서만 허용됩니다 — 그 외 메뉴 진입 시 currencySwitcherEnabled={false}로 넘겨 버튼을 disabled 상태로 노출하고, 호버 시 안내 툴팁이 표시됩니다 (문구는 내장 ko/en/ja 기본, currencySwitcherDisabledTooltip으로 덮어쓰기 가능). 적용 라우트: '/overview' · '/cost/analytics/usage-charges' · '/cost/billing-invoice' · '/settings/cost-allocation'(하위 경로 포함) — 현재 라우트가 여기에 속하는지를 앱에서 판단해 주입하세요
1158
+ - 헤더는 AI 버튼~알림 벨 사이 자리만 제공하고 상태·환율 데이터를 갖지 않습니다 — 앱이 관리하는 값을 currencySwitcherProps로 주입하면 내부 OpsnowCommonCurrencySwitcher에 그대로 전달됩니다 (위 props 규칙 동일 적용) - **활성/비활성 정책**: 통화 변경은 '개요(OverView)' · '비용 분석(Analytics)' · '청구 내역(Billing Invoice)' · '비용 배분(Cost Allocation)' 4개 메뉴에서만 허용됩니다 — 그 외 메뉴 진입 시 currencySwitcherEnabled={false}로 넘겨 버튼을 disabled 상태로 노출하고, 호버 시 안내 툴팁이 표시됩니다 (문구는 내장 ko/en/ja 기본, currencySwitcherDisabledTooltip으로 덮어쓰기 가능). 적용 라우트: '/overview' · '/cost/analytics/usage-charges' · '/cost/billing-invoice' · '/settings/cost-allocation'(하위 경로 포함) — 현재 라우트가 여기에 속하는지를 앱에서 판단해 주입하세요
1158
1159
 
1159
1160
  헤더 전용 Props:
1160
1161
 
@@ -1200,20 +1201,15 @@ export function createFormsComponent() {
1200
1201
  entries.push(["baseCurrency", `'${args.baseCurrency}'`]);
1201
1202
  if (args.nativeCurrency)
1202
1203
  entries.push(["nativeCurrency", `'${args.nativeCurrency}'`]);
1203
- if (args.defaultTargetCurrency)
1204
- entries.push(["defaultTargetCurrency", `'${args.defaultTargetCurrency}'`]);
1205
- if (args.customRate)
1206
- entries.push(["customRate", args.customRate]);
1207
- if (args.onCustomRateChange)
1208
- entries.push(["onCustomRateChange", args.onCustomRateChange]);
1209
- if (args.showDetail !== undefined)
1210
- entries.push(["showDetail", String(args.showDetail)]);
1204
+ // [다음 버전 재도입 예정] 환율 설정(직접 입력 · 시뮬레이션) props — 스키마 주석 블록과 함께 주석 해제
1205
+ // if (args.defaultTargetCurrency) entries.push(["defaultTargetCurrency", `'${args.defaultTargetCurrency}'`]);
1206
+ // if (args.simulationTarget) entries.push(["simulationTarget", args.simulationTarget]);
1207
+ // if (args.customRate) entries.push(["customRate", args.customRate]);
1208
+ // if (args.onCustomRateChange) entries.push(["onCustomRateChange", args.onCustomRateChange]);
1209
+ // if (args.showDetail !== undefined) entries.push(["showDetail", String(args.showDetail)]);
1210
+ // if (args.historyMode) entries.push(["historyMode", `'${args.historyMode}'`]);
1211
1211
  if (args.showHistory !== undefined)
1212
1212
  entries.push(["showHistory", String(args.showHistory)]);
1213
- if (args.historyMode)
1214
- entries.push(["historyMode", `'${args.historyMode}'`]);
1215
- if (args.simulationTarget)
1216
- entries.push(["simulationTarget", args.simulationTarget]);
1217
1213
  if (args.renderChart)
1218
1214
  entries.push(["renderChart", args.renderChart]);
1219
1215
  if (args.labels)
package/build/index.js CHANGED
@@ -28,6 +28,22 @@ const server = new McpServer({
28
28
 
29
29
  IMPORTANT: All components require React 18.x or higher environment.
30
30
  Always include required import statements and i18n configuration when needed.
31
+
32
+ **CRITICAL: 공통 패키지 버전 규칙**
33
+
34
+ - @opsnow-common/* 공통 패키지는 반드시 메이저 버전 2.x대를 사용합니다.
35
+ - MUI(@mui/*)는 반드시 9.x대를 사용합니다 — 공통 패키지 2.x가 MUI 9 기반이라 프로젝트의 MUI 버전도 맞춰야 합니다.
36
+ - 이 MCP의 스펙/예제는 모두 2.x + MUI 9 기준이라 그 이전 버전에서는 UI·동작이 다르게 나올 수 있습니다.
37
+ - 작업 전 package.json에서 @opsnow-common/*이 2.x인지, @mui/*가 9.x인지 확인하고, 아니면 업그레이드를 먼저 안내하세요.
38
+
39
+ **CRITICAL: MUI 9 코드 작성 규칙 (옛 MUI 문법 금지)**
40
+
41
+ 1. system props 금지 — Box/Stack/Typography/Grid/Link에 mt/mb/px/color 등을 직접 prop으로 주면 동작하지 않습니다. 반드시 sx로: <Box sx={{ mt: 2 }}> (O), <Box mt={2}> (X)
42
+ 2. Grid는 size prop 사용 — item prop 삭제, xs/sm/md/lg/xl prop 삭제: <Grid size={6}> / <Grid size={{ xs: 12, md: 6 }}> (O), <Grid item xs={6}> (X). 세로 배치(direction="column")도 삭제 — Stack 사용
43
+ 3. slots/slotProps로 통일 — InputProps/inputProps/componentsProps/TransitionComponent 등 legacy prop 삭제: slotProps.input / slotProps.htmlInput / slots.transition 사용
44
+ 4. Dialog/Modal의 disableEscapeKeyDown 삭제 — onClose의 reason 인자('escapeKeyDown' | 'backdropClick')로 분기 처리
45
+
46
+ 위 규칙은 raw MUI 컴포넌트(Box/Stack/Grid 등 직접 사용)에 적용됩니다. OpsnowCommon* 래퍼 컴포넌트는 각 컴포넌트 스펙(getComponentSpec)이 우선입니다 — 예: OpsnowCommonTextField는 inputProps/InputProps를 자체 props로 지원하며 내부에서 slotProps로 매핑하므로 스펙대로 사용하세요.
31
47
  ${toolFlowInstructions}
32
48
 
33
49
  **CRITICAL: API 호출 규칙**
@@ -87,7 +103,7 @@ Before implementing OpsnowCommonDataGrid, you MUST use getUIExamples tool with '
87
103
  **CRITICAL: Spacing Tokens (theme.commonSpacing) — DO NOT hardcode px**
88
104
 
89
105
  Just like color tokens (theme.palette.commonPalette), all layout spacing uses the semantic token theme.commonSpacing
90
- (requires @opsnow-common/opsnow-common-style 1.0.11+, type-augmented → autocompletes, light/dark 공통):
106
+ (@opsnow-common/opsnow-common-style 제공, type-augmented → autocompletes, light/dark 공통):
91
107
 
92
108
  filterGap 24px - gap between filters (filter row gap)
93
109
  filterLabelGap 8px - gap between label and its filter
@@ -122,7 +138,7 @@ When placing multiple filters in one row, the gap values are MANDATORY:
122
138
  - Label and its filter: theme.commonSpacing.filterLabelGap (8px) — wrap label+filter in one Stack
123
139
  - A labeled group counts as ONE filter — still filterGap from the previous filter
124
140
  - Pure CSS (no theme context): use var(--opsnow-spacing-filter-gap) / var(--opsnow-spacing-filter-label-gap)
125
- - Requires @opsnow-common/opsnow-common-style 1.0.11+ (theme.commonSpacing is type-augmented → autocompletes)
141
+ - theme.commonSpacing is provided by @opsnow-common/opsnow-common-style (type-augmented → autocompletes)
126
142
 
127
143
  WARNING: Violating these rules will cause the UI to malfunction.`
128
144
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opsnow-mcp/opsnow-mcp-common-ui-server",
3
- "version": "1.0.36",
3
+ "version": "1.0.37",
4
4
  "type": "module",
5
5
  "main": "index.js",
6
6
  "bin": {