mirror of
https://github.com/grafana/grafana.git
synced 2024-11-28 11:44:26 -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 TeamPages from 'app/features/teams/TeamPages';
|
||||
import TeamList from 'app/features/teams/TeamList';
|
||||
import PluginListPage from 'app/features/plugins/PluginListPage';
|
||||
import FolderSettingsPage from 'app/features/folders/FolderSettingsPage';
|
||||
import FolderPermissions from 'app/features/folders/FolderPermissions';
|
||||
|
||||
@ -245,9 +246,10 @@ export function setupAngularRoutes($routeProvider, $locationProvider) {
|
||||
controllerAs: 'ctrl',
|
||||
})
|
||||
.when('/plugins', {
|
||||
templateUrl: 'public/app/features/plugins/partials/plugin_list.html',
|
||||
controller: 'PluginListCtrl',
|
||||
controllerAs: 'ctrl',
|
||||
template: '<react-container />',
|
||||
resolve: {
|
||||
component: () => PluginListPage,
|
||||
},
|
||||
})
|
||||
.when('/plugins/:pluginId/edit', {
|
||||
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 foldersReducers from 'app/features/folders/state/reducers';
|
||||
import dashboardReducers from 'app/features/dashboard/state/reducers';
|
||||
import pluginReducers from 'app/features/plugins/state/reducers';
|
||||
|
||||
const rootReducer = combineReducers({
|
||||
...sharedReducers,
|
||||
@ -13,6 +14,7 @@ const rootReducer = combineReducers({
|
||||
...teamsReducers,
|
||||
...foldersReducers,
|
||||
...dashboardReducers,
|
||||
...pluginReducers,
|
||||
});
|
||||
|
||||
export let store;
|
||||
|
@ -6,7 +6,7 @@ import { FolderDTO, FolderState, FolderInfo } from './folders';
|
||||
import { DashboardState } from './dashboard';
|
||||
import { DashboardAcl, OrgRole, PermissionLevel } from './acl';
|
||||
import { DataSource } from './datasources';
|
||||
import { PluginMeta } from './plugins';
|
||||
import { PluginMeta, Plugin, PluginInfo, PluginsState } from './plugins';
|
||||
|
||||
export {
|
||||
Team,
|
||||
@ -33,6 +33,9 @@ export {
|
||||
PermissionLevel,
|
||||
DataSource,
|
||||
PluginMeta,
|
||||
PluginInfo,
|
||||
Plugin,
|
||||
PluginsState,
|
||||
};
|
||||
|
||||
export interface StoreState {
|
||||
|
@ -17,3 +17,33 @@ export interface PluginMetaInfo {
|
||||
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