mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-14 01:15:44 +01:00
implemented pluginsList
This commit is contained in:
@@ -2,11 +2,9 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import injectSheet from "react-jss";
|
import injectSheet from "react-jss";
|
||||||
import type { Repository } from "@scm-manager/ui-types";
|
|
||||||
import { DateFromNow } from "@scm-manager/ui-components";
|
|
||||||
import RepositoryEntryLink from "./RepositoryEntryLink";
|
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import RepositoryAvatar from "./RepositoryAvatar";
|
import type { Plugin } from "@scm-manager/ui-types";
|
||||||
|
import PluginAvatar from "./PluginAvatar";
|
||||||
|
|
||||||
const styles = {
|
const styles = {
|
||||||
inner: {
|
inner: {
|
||||||
@@ -14,82 +12,31 @@ const styles = {
|
|||||||
pointerEvents: "none",
|
pointerEvents: "none",
|
||||||
zIndex: 1
|
zIndex: 1
|
||||||
},
|
},
|
||||||
innerLink: {
|
|
||||||
pointerEvents: "all"
|
|
||||||
},
|
|
||||||
centerImage: {
|
centerImage: {
|
||||||
marginTop: "0.8em",
|
marginTop: "0.8em",
|
||||||
marginLeft: "1em !important"
|
marginLeft: "1em !important"
|
||||||
|
},
|
||||||
|
marginBottom: {
|
||||||
|
marginBottom: "0.75rem !important"
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
repository: Repository,
|
plugin: Plugin,
|
||||||
fullColumnWidth?: boolean,
|
fullColumnWidth?: boolean,
|
||||||
|
|
||||||
// context props
|
// context props
|
||||||
classes: any
|
classes: any
|
||||||
};
|
};
|
||||||
|
|
||||||
class PluginEntry extends React.Component<Props> {
|
class PluginEntry extends React.Component<Props> {
|
||||||
createLink = (repository: Repository) => {
|
|
||||||
return `/repo/${repository.namespace}/${repository.name}`;
|
|
||||||
};
|
|
||||||
|
|
||||||
renderBranchesLink = (repository: Repository, repositoryLink: string) => {
|
|
||||||
if (repository._links["branches"]) {
|
|
||||||
return (
|
|
||||||
<RepositoryEntryLink
|
|
||||||
iconClass="fas fa-code-branch fa-lg"
|
|
||||||
to={repositoryLink + "/branches"}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
renderChangesetsLink = (repository: Repository, repositoryLink: string) => {
|
|
||||||
if (repository._links["changesets"]) {
|
|
||||||
return (
|
|
||||||
<RepositoryEntryLink
|
|
||||||
iconClass="fas fa-exchange-alt fa-lg"
|
|
||||||
to={repositoryLink + "/changesets"}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
renderSourcesLink = (repository: Repository, repositoryLink: string) => {
|
|
||||||
if (repository._links["sources"]) {
|
|
||||||
return (
|
|
||||||
<RepositoryEntryLink
|
|
||||||
iconClass="fa-code fa-lg"
|
|
||||||
to={repositoryLink + "/sources"}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
renderModifyLink = (repository: Repository, repositoryLink: string) => {
|
|
||||||
if (repository._links["update"]) {
|
|
||||||
return (
|
|
||||||
<RepositoryEntryLink
|
|
||||||
iconClass="fa-cog fa-lg"
|
|
||||||
to={repositoryLink + "/settings/general"}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { repository, classes, fullColumnWidth } = this.props;
|
const { plugin, classes, fullColumnWidth } = this.props;
|
||||||
const repositoryLink = this.createLink(repository);
|
|
||||||
const halfColumn = fullColumnWidth ? "is-full" : "is-half";
|
const halfColumn = fullColumnWidth ? "is-full" : "is-half";
|
||||||
const overlayLinkClass = fullColumnWidth
|
const overlayLinkClass = fullColumnWidth
|
||||||
? "overlay-full-column"
|
? "overlay-full-column"
|
||||||
: "overlay-half-column";
|
: "overlay-half-column";
|
||||||
|
// TODO: Add link to plugin page below
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={classNames(
|
className={classNames(
|
||||||
@@ -100,31 +47,35 @@ class PluginEntry extends React.Component<Props> {
|
|||||||
halfColumn
|
halfColumn
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<Link className={classNames(overlayLinkClass)} to={repositoryLink} />
|
<Link
|
||||||
|
className={classNames(overlayLinkClass, "is-plugin-page")}
|
||||||
|
to="#"
|
||||||
|
/>
|
||||||
<article className={classNames("media", classes.inner)}>
|
<article className={classNames("media", classes.inner)}>
|
||||||
<figure className={classNames(classes.centerImage, "media-left")}>
|
<figure className={classNames(classes.centerImage, "media-left")}>
|
||||||
<RepositoryAvatar repository={repository} />
|
<PluginAvatar plugin={plugin} />
|
||||||
</figure>
|
</figure>
|
||||||
<div className={classNames("media-content", "text-box")}>
|
<div className={classNames("media-content", "text-box")}>
|
||||||
<div className="content">
|
<div className="content">
|
||||||
<p className="is-marginless">
|
<nav
|
||||||
<strong>{repository.name}</strong>
|
className={classNames(
|
||||||
</p>
|
"level",
|
||||||
<p className={"shorten-text"}>{repository.description}</p>
|
"is-mobile",
|
||||||
</div>
|
classes.marginBottom
|
||||||
<nav className="level is-mobile">
|
)}
|
||||||
|
>
|
||||||
<div className="level-left">
|
<div className="level-left">
|
||||||
{this.renderBranchesLink(repository, repositoryLink)}
|
<strong>{plugin.name}</strong>
|
||||||
{this.renderChangesetsLink(repository, repositoryLink)}
|
|
||||||
{this.renderSourcesLink(repository, repositoryLink)}
|
|
||||||
{this.renderModifyLink(repository, repositoryLink)}
|
|
||||||
</div>
|
</div>
|
||||||
<div className="level-right is-hidden-mobile">
|
<div className="level-right is-hidden-mobile">
|
||||||
<small className="level-item">
|
{plugin.version}
|
||||||
<DateFromNow date={repository.creationDate} />
|
|
||||||
</small>
|
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
<p className="shorten-text is-marginless">{plugin.description}</p>
|
||||||
|
<p>
|
||||||
|
<small>{plugin.author}</small>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
96
scm-ui/src/admin/plugins/components/PluginGroupEntry.js
Normal file
96
scm-ui/src/admin/plugins/components/PluginGroupEntry.js
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
//@flow
|
||||||
|
import React from "react";
|
||||||
|
import injectSheet from "react-jss";
|
||||||
|
import classNames from "classnames";
|
||||||
|
import type { PluginGroup, Plugin } from "@scm-manager/ui-types";
|
||||||
|
import PluginEntry from "./PluginEntry";
|
||||||
|
|
||||||
|
const styles = {
|
||||||
|
pointer: {
|
||||||
|
cursor: "pointer",
|
||||||
|
fontSize: "1.5rem"
|
||||||
|
},
|
||||||
|
pluginGroup: {
|
||||||
|
marginBottom: "1em"
|
||||||
|
},
|
||||||
|
wrapper: {
|
||||||
|
padding: "0 0.75rem"
|
||||||
|
},
|
||||||
|
clearfix: {
|
||||||
|
clear: "both"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
group: PluginGroup,
|
||||||
|
|
||||||
|
// context props
|
||||||
|
classes: any
|
||||||
|
};
|
||||||
|
|
||||||
|
type State = {
|
||||||
|
collapsed: boolean
|
||||||
|
};
|
||||||
|
|
||||||
|
class PluginGroupEntry extends React.Component<Props, State> {
|
||||||
|
constructor(props: Props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
collapsed: false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleCollapse = () => {
|
||||||
|
this.setState(prevState => ({
|
||||||
|
collapsed: !prevState.collapsed
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
isLastEntry = (array: Plugin[], index: number) => {
|
||||||
|
return index === array.length - 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
isLengthOdd = (array: Plugin[]) => {
|
||||||
|
return array.length % 2 !== 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
isFullSize = (array: Plugin[], index: number) => {
|
||||||
|
return this.isLastEntry(array, index) && this.isLengthOdd(array);
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { group, classes } = this.props;
|
||||||
|
const { collapsed } = this.state;
|
||||||
|
|
||||||
|
const icon = collapsed ? "fa-angle-right" : "fa-angle-down";
|
||||||
|
let content = null;
|
||||||
|
if (!collapsed) {
|
||||||
|
content = group.plugins.map((plugin, index) => {
|
||||||
|
const fullColumnWidth = this.isFullSize(group.plugins, index);
|
||||||
|
return (
|
||||||
|
<PluginEntry
|
||||||
|
plugin={plugin}
|
||||||
|
fullColumnWidth={fullColumnWidth}
|
||||||
|
key={index}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div className={classes.pluginGroup}>
|
||||||
|
<h2>
|
||||||
|
<span className={classes.pointer} onClick={this.toggleCollapse}>
|
||||||
|
<i className={classNames("fa", icon)} /> {group.name}
|
||||||
|
</span>
|
||||||
|
</h2>
|
||||||
|
<hr />
|
||||||
|
<div className={classNames("columns", "is-multiline", classes.wrapper)}>
|
||||||
|
{content}
|
||||||
|
</div>
|
||||||
|
<div className={classes.clearfix} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default injectSheet(styles)(PluginGroupEntry);
|
||||||
@@ -1,24 +1,26 @@
|
|||||||
//@flow
|
//@flow
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import type { Plugins } from "@scm-manager/ui-types";
|
import type { Plugin } from "@scm-manager/ui-types";
|
||||||
|
import PluginGroupEntry from "../components/PluginGroupEntry";
|
||||||
|
import groupByCategory from "./groupByCategory";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
plugins: Plugins[]
|
plugins: Plugin[]
|
||||||
};
|
};
|
||||||
|
|
||||||
class RepositoryList extends React.Component<Props> {
|
class PluginList extends React.Component<Props> {
|
||||||
render() {
|
render() {
|
||||||
const { plugins } = this.props;
|
const { plugins } = this.props;
|
||||||
|
|
||||||
const groups = groupByNamespace(plugins);
|
const groups = groupByCategory(plugins);
|
||||||
return (
|
return (
|
||||||
<div className="content">
|
<div className="content">
|
||||||
{groups.map(group => {
|
{groups.map(group => {
|
||||||
return <PluginEntry group={group} key={group.name} />;
|
return <PluginGroupEntry group={group} key={group.name} />;
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default RepositoryList;
|
export default PluginList;
|
||||||
|
|||||||
39
scm-ui/src/admin/plugins/components/groupByCategory.js
Normal file
39
scm-ui/src/admin/plugins/components/groupByCategory.js
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
// @flow
|
||||||
|
import type { Plugin, PluginGroup } from "@scm-manager/ui-types";
|
||||||
|
|
||||||
|
export default function groupByCategory(
|
||||||
|
plugins: Plugin[]
|
||||||
|
): PluginGroup[] {
|
||||||
|
let groups = {};
|
||||||
|
for (let plugin of plugins) {
|
||||||
|
const groupName = plugin.type;
|
||||||
|
|
||||||
|
let group = groups[groupName];
|
||||||
|
if (!group) {
|
||||||
|
group = {
|
||||||
|
name: groupName,
|
||||||
|
plugins: []
|
||||||
|
};
|
||||||
|
groups[groupName] = group;
|
||||||
|
}
|
||||||
|
group.plugins.push(plugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
let groupArray = [];
|
||||||
|
for (let groupName in groups) {
|
||||||
|
const group = groups[groupName];
|
||||||
|
group.plugins.sort(sortByName);
|
||||||
|
groupArray.push(groups[groupName]);
|
||||||
|
}
|
||||||
|
groupArray.sort(sortByName);
|
||||||
|
return groupArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
function sortByName(a, b) {
|
||||||
|
if (a.name < b.name) {
|
||||||
|
return -1;
|
||||||
|
} else if (a.name > b.name) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -172,6 +172,9 @@ ul.is-separated {
|
|||||||
height: calc(120px - 1.5rem);
|
height: calc(120px - 1.5rem);
|
||||||
width: calc(50% - 3rem);
|
width: calc(50% - 3rem);
|
||||||
}
|
}
|
||||||
|
.overlay-half-column.is-plugin-page {
|
||||||
|
width: calc(37.5% - 1.5rem);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.column.is-full {
|
.column.is-full {
|
||||||
.overlay-full-column {
|
.overlay-full-column {
|
||||||
@@ -179,6 +182,9 @@ ul.is-separated {
|
|||||||
height: calc(120px - 0.5rem);
|
height: calc(120px - 0.5rem);
|
||||||
width: calc(100% - 1.5rem);
|
width: calc(100% - 1.5rem);
|
||||||
}
|
}
|
||||||
|
.overlay-full-column.is-plugin-page {
|
||||||
|
width: calc(75% - 1.5rem);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@media screen and (max-width: 768px) {
|
@media screen and (max-width: 768px) {
|
||||||
.column.is-half {
|
.column.is-half {
|
||||||
@@ -188,12 +194,14 @@ ul.is-separated {
|
|||||||
margin-right: 0;
|
margin-right: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.overlay-half-column {
|
.overlay-half-column,
|
||||||
position: absolute;
|
.overlay-half-column.is-plugin-page {
|
||||||
height: calc(120px - 0.5rem);
|
|
||||||
width: calc(100% - 1.5rem);
|
width: calc(100% - 1.5rem);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.column.is-full .overlay-full-column.is-plugin-page {
|
||||||
|
width: calc(100% - 1.5rem);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user