1import React, { PureComponent } from 'react'; 2import { Spinner, HorizontalGroup } from '@grafana/ui'; 3import { DashboardModel } from '../../state/DashboardModel'; 4import { 5 historySrv, 6 RevisionsModel, 7 VersionHistoryTable, 8 VersionHistoryHeader, 9 VersionsHistoryButtons, 10 VersionHistoryComparison, 11} from '../VersionHistory'; 12 13interface Props { 14 dashboard: DashboardModel; 15} 16 17type State = { 18 isLoading: boolean; 19 isAppending: boolean; 20 versions: DecoratedRevisionModel[]; 21 viewMode: 'list' | 'compare'; 22 diffData: { lhs: any; rhs: any }; 23 newInfo?: DecoratedRevisionModel; 24 baseInfo?: DecoratedRevisionModel; 25 isNewLatest: boolean; 26}; 27 28export type DecoratedRevisionModel = RevisionsModel & { 29 createdDateString: string; 30 ageString: string; 31}; 32 33export const VERSIONS_FETCH_LIMIT = 10; 34 35export class VersionsSettings extends PureComponent<Props, State> { 36 limit: number; 37 start: number; 38 39 constructor(props: Props) { 40 super(props); 41 this.limit = VERSIONS_FETCH_LIMIT; 42 this.start = 0; 43 this.state = { 44 isAppending: true, 45 isLoading: true, 46 versions: [], 47 viewMode: 'list', 48 isNewLatest: false, 49 diffData: { 50 lhs: {}, 51 rhs: {}, 52 }, 53 }; 54 } 55 56 componentDidMount() { 57 this.getVersions(); 58 } 59 60 getVersions = (append = false) => { 61 this.setState({ isAppending: append }); 62 historySrv 63 .getHistoryList(this.props.dashboard, { limit: this.limit, start: this.start }) 64 .then((res) => { 65 this.setState({ 66 isLoading: false, 67 versions: [...this.state.versions, ...this.decorateVersions(res)], 68 }); 69 this.start += this.limit; 70 }) 71 .catch((err) => console.log(err)) 72 .finally(() => this.setState({ isAppending: false })); 73 }; 74 75 getDiff = async () => { 76 const selectedVersions = this.state.versions.filter((version) => version.checked); 77 const [newInfo, baseInfo] = selectedVersions; 78 const isNewLatest = newInfo.version === this.props.dashboard.version; 79 80 this.setState({ 81 isLoading: true, 82 }); 83 84 const lhs = await historySrv.getDashboardVersion(this.props.dashboard.id, baseInfo.version); 85 const rhs = await historySrv.getDashboardVersion(this.props.dashboard.id, newInfo.version); 86 87 this.setState({ 88 baseInfo, 89 isLoading: false, 90 isNewLatest, 91 newInfo, 92 viewMode: 'compare', 93 diffData: { 94 lhs: lhs.data, 95 rhs: rhs.data, 96 }, 97 }); 98 }; 99 100 decorateVersions = (versions: RevisionsModel[]) => 101 versions.map((version) => ({ 102 ...version, 103 createdDateString: this.props.dashboard.formatDate(version.created), 104 ageString: this.props.dashboard.getRelativeTime(version.created), 105 checked: false, 106 })); 107 108 isLastPage() { 109 return this.state.versions.find((rev) => rev.version === 1); 110 } 111 112 onCheck = (ev: React.FormEvent<HTMLInputElement>, versionId: number) => { 113 this.setState({ 114 versions: this.state.versions.map((version) => 115 version.id === versionId ? { ...version, checked: ev.currentTarget.checked } : version 116 ), 117 }); 118 }; 119 120 reset = () => { 121 this.setState({ 122 baseInfo: undefined, 123 diffData: { 124 lhs: {}, 125 rhs: {}, 126 }, 127 isNewLatest: false, 128 newInfo: undefined, 129 versions: this.state.versions.map((version) => ({ ...version, checked: false })), 130 viewMode: 'list', 131 }); 132 }; 133 134 render() { 135 const { versions, viewMode, baseInfo, newInfo, isNewLatest, isLoading, diffData } = this.state; 136 const canCompare = versions.filter((version) => version.checked).length !== 2; 137 const showButtons = versions.length > 1; 138 const hasMore = versions.length >= this.limit; 139 140 if (viewMode === 'compare') { 141 return ( 142 <div> 143 <VersionHistoryHeader 144 isComparing 145 onClick={this.reset} 146 baseVersion={baseInfo?.version} 147 newVersion={newInfo?.version} 148 isNewLatest={isNewLatest} 149 /> 150 {isLoading ? ( 151 <VersionsHistorySpinner msg="Fetching changes…" /> 152 ) : ( 153 <VersionHistoryComparison 154 newInfo={newInfo!} 155 baseInfo={baseInfo!} 156 isNewLatest={isNewLatest} 157 diffData={diffData} 158 /> 159 )} 160 </div> 161 ); 162 } 163 164 return ( 165 <div> 166 <VersionHistoryHeader /> 167 {isLoading ? ( 168 <VersionsHistorySpinner msg="Fetching history list…" /> 169 ) : ( 170 <VersionHistoryTable versions={versions} onCheck={this.onCheck} /> 171 )} 172 {this.state.isAppending && <VersionsHistorySpinner msg="Fetching more entries…" />} 173 {showButtons && ( 174 <VersionsHistoryButtons 175 hasMore={hasMore} 176 canCompare={canCompare} 177 getVersions={this.getVersions} 178 getDiff={this.getDiff} 179 isLastPage={!!this.isLastPage()} 180 /> 181 )} 182 </div> 183 ); 184 } 185} 186 187const VersionsHistorySpinner = ({ msg }: { msg: string }) => ( 188 <HorizontalGroup> 189 <Spinner /> 190 <em>{msg}</em> 191 </HorizontalGroup> 192); 193