{"record":{"id":"a66abb5a2e3d2315","repo":"jlcodes99/cockpit-tools","slug":"announcement","errorCode":null,"errorMessage":"[Announcement] 未支持的命令动作:","messagePattern":"\\[Announcement\\] 未支持的命令动作:","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"src/components/AnnouncementCenter.tsx","lineNumber":298,"sourceCode":"      switch (action.target) {\n        case 'update.check': {\n          window.dispatchEvent(new CustomEvent('update-check-requested', { detail: { source: 'manual' } }));\n          break;\n        }\n        case 'announcement.forceRefresh': {\n          await fetchState(true);\n          break;\n        }\n        case 'page.navigate': {\n          const target = typeof action.arguments?.[0] === 'string' ? action.arguments[0] : '';\n          const targetPage = target ? TAB_TARGET_PAGE_MAP[target] : undefined;\n          if (targetPage) {\n            onNavigate(targetPage);\n          }\n          break;\n        }\n        default:\n          console.warn('[Announcement] 未支持的命令动作:', action.target);\n      }\n      await closeDetail(false);\n    }\n  };\n\n  const currentTypeLabel = (type: string) => {\n    if (type === 'feature') return t('announcement.type.feature', '✨ 新功能');\n    if (type === 'warning') return t('announcement.type.warning', '⚠️ 警告');\n    if (type === 'urgent') return t('announcement.type.urgent', '🚨 紧急');\n    return t('announcement.type.info', 'ℹ️ 信息');\n  };\n\n  const renderInBody = (node: ReactNode) => {\n    if (typeof document === 'undefined') {\n      return node;\n    }\n    return createPortal(node, document.body);\n  };","sourceCodeStart":280,"sourceCodeEnd":316,"githubUrl":"https://github.com/jlcodes99/cockpit-tools/blob/1ed8b77992d62ca81fabf744deb0839ad361d5bf/src/components/AnnouncementCenter.tsx#L280-L316","documentation":"In AnnouncementCenter's runAction switch, each announcement command action has a target that maps to a known branch (navigation, link, etc.). When action.target holds a value with no matching case, the default branch logs this warning. The detail panel still closes via closeDetail(false), but the announced action does nothing.","triggerScenarios":"Clicking a call-to-action on an announcement whose action.target is an unrecognized string: a new target type introduced by a newer backend/app version, a typo in the announcement content, or content authored against a different schema.","commonSituations":"Announcements pushed by a newer backend using a target type this app build doesn't know yet; CMS-managed announcement copy with a misspelled target; stale cached announcements after an app downgrade.","solutions":["Log/inspect the printed action.target value to see the unknown action name.","Update the app — the target type is likely from a newer release than this build.","Fix the announcement content in the management backend if the target is misspelled.","Add a case for the new target in runAction's switch if you own the code."],"exampleFix":"// before\ndefault:\n  console.warn('[Announcement] 未支持的命令动作:', action.target);\n// after\ndefault:\n  console.warn('[Announcement] 未支持的命令动作:', action.target);\n  assertNever(action.target); // compile-time check when new targets are added to the union","handlingStrategy":"type-guard","validationCode":"const KNOWN_ACTION_TARGETS = ['navigate', 'open_url', 'dismiss'] as const;\ntype ActionTarget = typeof KNOWN_ACTION_TARGETS[number];\nif (!KNOWN_ACTION_TARGETS.includes(action.target as ActionTarget)) {\n  console.warn('[Announcement] 未支持的命令动作:', action.target);\n  return;\n}\n","typeGuard":"function isKnownActionTarget(t: string): t is ActionTarget {\n  return (['navigate', 'open_url', 'dismiss'] as const).includes(t as ActionTarget);\n}\n","tryCatchPattern":"switch (action.target) {\n  // ...known cases...\n  default: {\n    console.warn('[Announcement] 未支持的命令动作:', action.target);\n    break; // no-op but still close detail panel\n  }\n}\nawait closeDetail(false);","preventionTips":["Type announcement action.target as a discriminated union, not string","Validate announcement content in the backend before publishing","Keep an allowlist of supported targets shared between backend and frontend","Ignore (don't crash on) unknown targets so old clients tolerate new announcement types"],"tags":["ui","announcements","unhandled-case","switch"],"backgroundTag":"unsupported-action-type","analyzedSha":"1ed8b77992d62ca81fabf744deb0839ad361d5bf","analyzedAt":"2026-09-05T09:51:41.178Z","contentChangedAt":"2026-09-05T09:51:41.178Z","schemaVersion":2},"datasetVersion":"2026-09-12T12:17:11.808Z"}