{"version":3,"file":"AdminFeatureTogglesPage.22157fe31f358f1c4f09.js","mappings":"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","sources":["webpack://grafana/./public/app/features/admin/AdminFeatureTogglesAPI.ts","webpack://grafana/./public/app/features/admin/AdminFeatureTogglesTable.tsx","webpack://grafana/./public/app/features/admin/AdminFeatureTogglesPage.tsx"],"sourcesContent":["import { getBackendSrv } from '@grafana/runtime';\n\nexport type FeatureToggle = {\n name: string;\n description?: string;\n enabled: boolean;\n stage: string;\n readOnly?: boolean;\n hidden?: boolean;\n};\n\nexport type CurrentTogglesState = {\n restartRequired: boolean;\n allowEditing: boolean;\n toggles: FeatureToggle[];\n};\n\ninterface ResolvedToggleState {\n kind: 'ResolvedToggleState';\n restartRequired?: boolean;\n allowEditing?: boolean;\n toggles?: K8sToggleSpec[]; // not used in patch\n enabled: { [key: string]: boolean };\n}\n\ninterface K8sToggleSpec {\n name: string;\n description: string;\n enabled: boolean;\n writeable: boolean;\n source: K8sToggleSource;\n stage: string;\n}\n\ninterface K8sToggleSource {\n namespace: string;\n name: string;\n}\n\ninterface FeatureTogglesAPI {\n getFeatureToggles(): Promise<CurrentTogglesState>;\n updateFeatureToggles(toggles: FeatureToggle[]): Promise<void>;\n}\n\nclass K8sAPI implements FeatureTogglesAPI {\n baseURL = '/apis/featuretoggle.grafana.app/v0alpha1';\n\n async getFeatureToggles(): Promise<CurrentTogglesState> {\n const current = await getBackendSrv().get<ResolvedToggleState>(this.baseURL + '/current');\n return {\n restartRequired: Boolean(current.restartRequired),\n allowEditing: Boolean(current.allowEditing),\n toggles: current.toggles!.map((t) => ({\n name: t.name,\n description: t.description!,\n enabled: t.enabled,\n readOnly: !Boolean(t.writeable),\n stage: t.stage,\n hidden: false, // only return visible things\n })),\n };\n }\n updateFeatureToggles(toggles: FeatureToggle[]): Promise<void> {\n const patchBody: ResolvedToggleState = {\n kind: 'ResolvedToggleState',\n enabled: {},\n };\n toggles.forEach((t) => {\n patchBody.enabled[t.name] = t.enabled;\n });\n return getBackendSrv().patch(this.baseURL + '/current', patchBody);\n }\n}\n\nexport const getTogglesAPI = (): FeatureTogglesAPI => {\n return new K8sAPI();\n};\n","import React, { useState, useRef } from 'react';\n\nimport { Switch, InteractiveTable, Tooltip, type CellProps, Button, ConfirmModal, type SortByFn } from '@grafana/ui';\n\nimport { FeatureToggle, getTogglesAPI } from './AdminFeatureTogglesAPI';\n\ninterface Props {\n featureToggles: FeatureToggle[];\n allowEditing: boolean;\n onUpdateSuccess: () => void;\n}\n\nconst sortByName: SortByFn<FeatureToggle> = (a, b) => {\n return a.original.name.localeCompare(b.original.name);\n};\n\nconst sortByDescription: SortByFn<FeatureToggle> = (a, b) => {\n if (!a.original.description && !b.original.description) {\n return 0;\n } else if (!a.original.description) {\n return 1;\n } else if (!b.original.description) {\n return -1;\n }\n return a.original.description.localeCompare(b.original.description);\n};\n\nconst sortByEnabled: SortByFn<FeatureToggle> = (a, b) => {\n return a.original.enabled === b.original.enabled ? 0 : a.original.enabled ? 1 : -1;\n};\n\nexport function AdminFeatureTogglesTable({ featureToggles, allowEditing, onUpdateSuccess }: Props) {\n // sort manually, doesn't look like it can be automatically done in the table\n featureToggles.sort((a, b) => a.name.localeCompare(b.name));\n const serverToggles = useRef<FeatureToggle[]>(featureToggles);\n const [localToggles, setLocalToggles] = useState<FeatureToggle[]>(featureToggles);\n const [isSaving, setIsSaving] = useState(false);\n const [showSaveModel, setShowSaveModal] = useState(false);\n const togglesApi = getTogglesAPI();\n\n const handleToggleChange = (toggle: FeatureToggle, newValue: boolean) => {\n const updatedToggle = { ...toggle, enabled: newValue };\n\n // Update the local state\n const updatedToggles = localToggles.map((t) => (t.name === toggle.name ? updatedToggle : t));\n setLocalToggles(updatedToggles);\n };\n\n const handleSaveChanges = async () => {\n setIsSaving(true);\n try {\n const modifiedToggles = getModifiedToggles();\n await togglesApi.updateFeatureToggles(modifiedToggles);\n // Pretend the values came from a new request\n serverToggles.current = [...localToggles];\n onUpdateSuccess(); // should trigger a new get\n } finally {\n setIsSaving(false);\n }\n };\n\n const saveButtonRef = useRef<HTMLButtonElement | null>(null);\n const showSaveChangesModal = (show: boolean) => () => {\n setShowSaveModal(show);\n if (!show && saveButtonRef.current) {\n saveButtonRef.current.focus();\n }\n };\n\n const getModifiedToggles = (): FeatureToggle[] => {\n return localToggles.filter((toggle, index) => toggle.enabled !== serverToggles.current[index].enabled);\n };\n\n const hasModifications = () => {\n // Check if there are any differences between the original toggles and the local toggles\n return localToggles.some((toggle, index) => toggle.enabled !== serverToggles.current[index].enabled);\n };\n\n const getToggleTooltipContent = (readOnlyToggle?: boolean) => {\n if (!allowEditing) {\n return 'Feature management is not configured for editing';\n }\n if (readOnlyToggle) {\n return 'This is a non-editable feature';\n }\n return '';\n };\n\n const getStageCell = (stage: string) => {\n switch (stage) {\n case 'GA':\n return (\n <Tooltip content={'General availability'}>\n <div>GA</div>\n </Tooltip>\n );\n case 'privatePreview':\n case 'preview':\n case 'experimental':\n return 'Beta';\n case 'deprecated':\n return 'Deprecated';\n default:\n return stage;\n }\n };\n\n const columns = [\n {\n id: 'name',\n header: 'Name',\n cell: ({ cell: { value } }: CellProps<FeatureToggle, string>) => <div>{value}</div>,\n sortType: sortByName,\n },\n {\n id: 'description',\n header: 'Description',\n cell: ({ cell: { value } }: CellProps<FeatureToggle, string>) => <div>{value}</div>,\n sortType: sortByDescription,\n },\n {\n id: 'stage',\n header: 'Stage',\n cell: ({ cell: { value } }: CellProps<FeatureToggle, string>) => <div>{getStageCell(value)}</div>,\n },\n {\n id: 'enabled',\n header: 'State',\n cell: ({ row }: CellProps<FeatureToggle, boolean>) => {\n const renderStateSwitch = (\n <div>\n <Switch\n value={row.original.enabled}\n disabled={row.original.readOnly}\n onChange={(e) => handleToggleChange(row.original, e.currentTarget.checked)}\n transparent={row.original.readOnly}\n />\n </div>\n );\n\n return row.original.readOnly ? (\n <Tooltip content={getToggleTooltipContent(row.original.readOnly)}>{renderStateSwitch}</Tooltip>\n ) : (\n renderStateSwitch\n );\n },\n sortType: sortByEnabled,\n },\n ];\n\n return (\n <>\n {allowEditing && (\n <div style={{ display: 'flex', justifyContent: 'flex-end', padding: '0 0 5px 0' }}>\n <Button disabled={!hasModifications() || isSaving} onClick={showSaveChangesModal(true)} ref={saveButtonRef}>\n {isSaving ? 'Saving...' : 'Save Changes'}\n </Button>\n <ConfirmModal\n isOpen={showSaveModel}\n title=\"Apply feature toggle changes\"\n body={\n <div>\n <p>\n Some features are stable (GA) and enabled by default, whereas some are currently in their preliminary\n Beta phase, available for early adoption.\n </p>\n <p>We advise understanding the implications of each feature change before making modifications.</p>\n </div>\n }\n confirmText=\"Save changes\"\n onConfirm={async () => {\n showSaveChangesModal(false)();\n handleSaveChanges();\n }}\n onDismiss={showSaveChangesModal(false)}\n />\n </div>\n )}\n <InteractiveTable columns={columns} data={localToggles} getRowId={(featureToggle) => featureToggle.name} />\n </>\n );\n}\n","import { css } from '@emotion/css';\nimport React, { useState } from 'react';\nimport { useAsync } from 'react-use';\n\nimport { GrafanaTheme2 } from '@grafana/data';\nimport { useStyles2, Icon } from '@grafana/ui';\nimport { Page } from 'app/core/components/Page/Page';\n\nimport { getTogglesAPI } from './AdminFeatureTogglesAPI';\nimport { AdminFeatureTogglesTable } from './AdminFeatureTogglesTable';\n\nexport default function AdminFeatureTogglesPage() {\n const [reload, setReload] = useState(1);\n const togglesApi = getTogglesAPI();\n const featureState = useAsync(() => togglesApi.getFeatureToggles(), [reload]);\n const styles = useStyles2(getStyles);\n\n const handleUpdateSuccess = () => {\n setReload(reload + 1);\n };\n\n const EditingAlert = () => {\n return (\n <div className={styles.warning}>\n <div className={styles.icon}>\n <Icon name=\"exclamation-triangle\" />\n </div>\n <span className={styles.message}>\n {featureState.value?.restartRequired\n ? 'A restart is pending for your Grafana instance to apply the latest feature toggle changes'\n : 'Saving feature toggle changes will prompt a restart of the instance, which may take a few minutes'}\n </span>\n </div>\n );\n };\n\n const subTitle = (\n <div>\n View and edit feature toggles. Read more about feature toggles at{' '}\n <a\n className=\"external-link\"\n target=\"_new\"\n href=\"https://grafana.com/docs/grafana/latest/setup-grafana/configure-grafana/feature-toggles/\"\n >\n grafana.com\n </a>\n .\n </div>\n );\n\n return (\n <Page navId=\"feature-toggles\" subTitle={subTitle}>\n <Page.Contents isLoading={featureState.loading}>\n <>\n {featureState.error}\n {featureState.loading && 'Fetching feature toggles'}\n\n <EditingAlert />\n {featureState.value && (\n <AdminFeatureTogglesTable\n featureToggles={featureState.value.toggles}\n allowEditing={featureState.value.allowEditing || false}\n onUpdateSuccess={handleUpdateSuccess}\n />\n )}\n </>\n </Page.Contents>\n </Page>\n );\n}\n\nfunction getStyles(theme: GrafanaTheme2) {\n return {\n warning: css({\n display: 'flex',\n marginTop: theme.spacing(0.25),\n marginBottom: theme.spacing(0.25),\n }),\n icon: css({\n color: theme.colors.warning.main,\n paddingRight: theme.spacing(),\n }),\n message: css({\n color: theme.colors.text.secondary,\n marginTop: theme.spacing(0.25),\n }),\n };\n}\n"],"names":["K8sAPI","current","t","toggles","patchBody","getTogglesAPI","sortByName","a","b","sortByDescription","sortByEnabled","AdminFeatureTogglesTable","featureToggles","allowEditing","onUpdateSuccess","serverToggles","localToggles","setLocalToggles","isSaving","setIsSaving","showSaveModel","setShowSaveModal","togglesApi","handleToggleChange","toggle","newValue","updatedToggle","updatedToggles","handleSaveChanges","modifiedToggles","getModifiedToggles","saveButtonRef","showSaveChangesModal","show","index","hasModifications","getToggleTooltipContent","readOnlyToggle","getStageCell","stage","Tooltip","columns","value","row","renderStateSwitch","Switch","e","Button","ConfirmModal","InteractiveTable","featureToggle","AdminFeatureTogglesPage","reload","setReload","featureState","useAsync","styles","getStyles","handleUpdateSuccess","EditingAlert","Icon","subTitle","Page","theme"],"sourceRoot":""}