mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
render list
This commit is contained in:
parent
8009bc3940
commit
e8cc0f3fff
24
public/app/features/plugins/PluginActionBar.tsx
Normal file
24
public/app/features/plugins/PluginActionBar.tsx
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
export default function({ searchQuery, onQueryChange }) {
|
||||||
|
return (
|
||||||
|
<div className="page-action-bar">
|
||||||
|
<div className="gf-form gf-form--grow">
|
||||||
|
<label className="gf-form--has-input-icon">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
className="gf-form-input width-20"
|
||||||
|
value={searchQuery}
|
||||||
|
onChange={onQueryChange}
|
||||||
|
placeholder="Filter by name or type"
|
||||||
|
/>
|
||||||
|
<i className="gf-form-input-icon fa fa-search" />
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div className="page-action-bar__spacer" />
|
||||||
|
<a className="btn btn-success" href="https://grafana.com/plugins?utm_source=grafana_plugin_list" target="_blank">
|
||||||
|
Find more plugins on Grafana.com
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
21
public/app/features/plugins/PluginList.tsx
Normal file
21
public/app/features/plugins/PluginList.tsx
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import classNames from 'classnames/bind';
|
||||||
|
import PluginListItem from './PluginListItem';
|
||||||
|
|
||||||
|
export default function PluginList({ plugins, layout }) {
|
||||||
|
const listStyle = classNames({
|
||||||
|
'card-section': true,
|
||||||
|
'card-list-layout-grid': layout === 'grid',
|
||||||
|
'card-list-layout-list': layout === 'list',
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section className={listStyle}>
|
||||||
|
<ol className="card-list">
|
||||||
|
{plugins.map((plugin, index) => {
|
||||||
|
return <PluginListItem plugin={plugin} key={`${plugin.name}-${index}`} />;
|
||||||
|
})}
|
||||||
|
</ol>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
30
public/app/features/plugins/PluginListItem.tsx
Normal file
30
public/app/features/plugins/PluginListItem.tsx
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
export default function PluginListItem({ plugin }) {
|
||||||
|
return (
|
||||||
|
<li className="card-item-wrapper">
|
||||||
|
<a className="card-item" href={`plugins/${plugin.id}/edit`}>
|
||||||
|
<div className="card-item-header">
|
||||||
|
<div className="card-item-type">
|
||||||
|
<i className={`icon-gf icon-gf-${plugin.type}`} />
|
||||||
|
{plugin.type}
|
||||||
|
</div>
|
||||||
|
{plugin.hasUpdate && (
|
||||||
|
<div className="card-item-notice">
|
||||||
|
<span bs-tooltip="plugin.latestVersion">Update available!</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="card-item-body">
|
||||||
|
<figure className="card-item-figure">
|
||||||
|
<img src={plugin.info.logos.small} />
|
||||||
|
</figure>
|
||||||
|
<div className="card-item-details">
|
||||||
|
<div className="card-item-name">{plugin.name}</div>
|
||||||
|
<div className="card-item-sub-name">{`By ${plugin.info.author.name}`}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
}
|
53
public/app/features/plugins/PluginListPage.tsx
Normal file
53
public/app/features/plugins/PluginListPage.tsx
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
import React, { PureComponent } from 'react';
|
||||||
|
import { hot } from 'react-hot-loader';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import PageHeader from '../../core/components/PageHeader/PageHeader';
|
||||||
|
import PluginActionBar from './PluginActionBar';
|
||||||
|
import PluginList from './PluginList';
|
||||||
|
import { NavModel, Plugin } from '../../types';
|
||||||
|
import { loadPlugins } from './state/actions';
|
||||||
|
import { getNavModel } from '../../core/selectors/navModel';
|
||||||
|
import { getPlugins } from './state/selectors';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
navModel: NavModel;
|
||||||
|
plugins: Plugin[];
|
||||||
|
loadPlugins: typeof loadPlugins;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class PluginListPage extends PureComponent<Props> {
|
||||||
|
componentDidMount() {
|
||||||
|
this.fetchPlugins();
|
||||||
|
}
|
||||||
|
|
||||||
|
async fetchPlugins() {
|
||||||
|
await this.props.loadPlugins();
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { navModel, plugins } = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<PageHeader model={navModel} />
|
||||||
|
<div className="page-container page-body">
|
||||||
|
<PluginActionBar searchQuery="" onQueryChange={() => {}} />
|
||||||
|
{plugins && <PluginList plugins={plugins} layout="grid" />}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapStateToProps(state) {
|
||||||
|
return {
|
||||||
|
navModel: getNavModel(state.navIndex, 'plugins'),
|
||||||
|
plugins: getPlugins(state.plugins),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapDispatchToProps = {
|
||||||
|
loadPlugins,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default hot(module)(connect(mapStateToProps, mapDispatchToProps)(PluginListPage));
|
28
public/app/features/plugins/state/actions.ts
Normal file
28
public/app/features/plugins/state/actions.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import { Plugin, StoreState } from 'app/types';
|
||||||
|
import { ThunkAction } from 'redux-thunk';
|
||||||
|
import { getBackendSrv } from '../../../core/services/backend_srv';
|
||||||
|
|
||||||
|
export enum ActionTypes {
|
||||||
|
LoadPlugins = 'LOAD_PLUGINS',
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LoadPluginsAction {
|
||||||
|
type: ActionTypes.LoadPlugins;
|
||||||
|
payload: Plugin[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export const pluginsLoaded = (plugins: Plugin[]): LoadPluginsAction => ({
|
||||||
|
type: ActionTypes.LoadPlugins,
|
||||||
|
payload: plugins,
|
||||||
|
});
|
||||||
|
|
||||||
|
export type Action = LoadPluginsAction;
|
||||||
|
|
||||||
|
type ThunkResult<R> = ThunkAction<R, StoreState, undefined, Action>;
|
||||||
|
|
||||||
|
export function loadPlugins(): ThunkResult<void> {
|
||||||
|
return async dispatch => {
|
||||||
|
const result = await getBackendSrv().get('api/plugins', { embedded: 0 });
|
||||||
|
dispatch(pluginsLoaded(result));
|
||||||
|
};
|
||||||
|
}
|
16
public/app/features/plugins/state/reducers.ts
Normal file
16
public/app/features/plugins/state/reducers.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import { Action, ActionTypes } from './actions';
|
||||||
|
import { Plugin, PluginsState } from 'app/types';
|
||||||
|
|
||||||
|
export const initialState: PluginsState = { plugins: [] as Plugin[] };
|
||||||
|
|
||||||
|
export const pluginsReducer = (state = initialState, action: Action): PluginsState => {
|
||||||
|
switch (action.type) {
|
||||||
|
case ActionTypes.LoadPlugins:
|
||||||
|
return { ...state, plugins: action.payload };
|
||||||
|
}
|
||||||
|
return state;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default {
|
||||||
|
plugins: pluginsReducer,
|
||||||
|
};
|
1
public/app/features/plugins/state/selectors.ts
Normal file
1
public/app/features/plugins/state/selectors.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export const getPlugins = state => state.plugins;
|
@ -5,6 +5,7 @@ import ServerStats from 'app/features/admin/ServerStats';
|
|||||||
import AlertRuleList from 'app/features/alerting/AlertRuleList';
|
import AlertRuleList from 'app/features/alerting/AlertRuleList';
|
||||||
import TeamPages from 'app/features/teams/TeamPages';
|
import TeamPages from 'app/features/teams/TeamPages';
|
||||||
import TeamList from 'app/features/teams/TeamList';
|
import TeamList from 'app/features/teams/TeamList';
|
||||||
|
import PluginListPage from 'app/features/plugins/PluginListPage';
|
||||||
import FolderSettingsPage from 'app/features/folders/FolderSettingsPage';
|
import FolderSettingsPage from 'app/features/folders/FolderSettingsPage';
|
||||||
import FolderPermissions from 'app/features/folders/FolderPermissions';
|
import FolderPermissions from 'app/features/folders/FolderPermissions';
|
||||||
|
|
||||||
@ -245,9 +246,10 @@ export function setupAngularRoutes($routeProvider, $locationProvider) {
|
|||||||
controllerAs: 'ctrl',
|
controllerAs: 'ctrl',
|
||||||
})
|
})
|
||||||
.when('/plugins', {
|
.when('/plugins', {
|
||||||
templateUrl: 'public/app/features/plugins/partials/plugin_list.html',
|
template: '<react-container />',
|
||||||
controller: 'PluginListCtrl',
|
resolve: {
|
||||||
controllerAs: 'ctrl',
|
component: () => PluginListPage,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
.when('/plugins/:pluginId/edit', {
|
.when('/plugins/:pluginId/edit', {
|
||||||
templateUrl: 'public/app/features/plugins/partials/plugin_edit.html',
|
templateUrl: 'public/app/features/plugins/partials/plugin_edit.html',
|
||||||
|
@ -6,6 +6,7 @@ import alertingReducers from 'app/features/alerting/state/reducers';
|
|||||||
import teamsReducers from 'app/features/teams/state/reducers';
|
import teamsReducers from 'app/features/teams/state/reducers';
|
||||||
import foldersReducers from 'app/features/folders/state/reducers';
|
import foldersReducers from 'app/features/folders/state/reducers';
|
||||||
import dashboardReducers from 'app/features/dashboard/state/reducers';
|
import dashboardReducers from 'app/features/dashboard/state/reducers';
|
||||||
|
import pluginReducers from 'app/features/plugins/state/reducers';
|
||||||
|
|
||||||
const rootReducer = combineReducers({
|
const rootReducer = combineReducers({
|
||||||
...sharedReducers,
|
...sharedReducers,
|
||||||
@ -13,6 +14,7 @@ const rootReducer = combineReducers({
|
|||||||
...teamsReducers,
|
...teamsReducers,
|
||||||
...foldersReducers,
|
...foldersReducers,
|
||||||
...dashboardReducers,
|
...dashboardReducers,
|
||||||
|
...pluginReducers,
|
||||||
});
|
});
|
||||||
|
|
||||||
export let store;
|
export let store;
|
||||||
|
@ -6,7 +6,7 @@ import { FolderDTO, FolderState, FolderInfo } from './folders';
|
|||||||
import { DashboardState } from './dashboard';
|
import { DashboardState } from './dashboard';
|
||||||
import { DashboardAcl, OrgRole, PermissionLevel } from './acl';
|
import { DashboardAcl, OrgRole, PermissionLevel } from './acl';
|
||||||
import { DataSource } from './datasources';
|
import { DataSource } from './datasources';
|
||||||
import { PluginMeta } from './plugins';
|
import { PluginMeta, Plugin, PluginInfo, PluginsState } from './plugins';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
Team,
|
Team,
|
||||||
@ -33,6 +33,9 @@ export {
|
|||||||
PermissionLevel,
|
PermissionLevel,
|
||||||
DataSource,
|
DataSource,
|
||||||
PluginMeta,
|
PluginMeta,
|
||||||
|
PluginInfo,
|
||||||
|
Plugin,
|
||||||
|
PluginsState,
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface StoreState {
|
export interface StoreState {
|
||||||
|
@ -17,3 +17,33 @@ export interface PluginMetaInfo {
|
|||||||
small: string;
|
small: string;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface PluginInfo {
|
||||||
|
author: {
|
||||||
|
name: string;
|
||||||
|
url: string;
|
||||||
|
};
|
||||||
|
description: string;
|
||||||
|
links: string[];
|
||||||
|
logos: { small: string; large: string };
|
||||||
|
screenshots: string;
|
||||||
|
updated: string;
|
||||||
|
version: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Plugin {
|
||||||
|
defaultNavUrl: string;
|
||||||
|
enabled: boolean;
|
||||||
|
hasUpdate: boolean;
|
||||||
|
id: string;
|
||||||
|
info: PluginInfo;
|
||||||
|
latestVersion: string;
|
||||||
|
name: string;
|
||||||
|
pinned: boolean;
|
||||||
|
state: string;
|
||||||
|
type: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PluginsState {
|
||||||
|
plugins: Plugin[];
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user