67 lines
2.2 KiB
TypeScript
67 lines
2.2 KiB
TypeScript
import React from 'react';
|
|
|
|
export interface IssueCardProps {
|
|
card: any
|
|
}
|
|
|
|
export class IssueCard extends React.PureComponent<IssueCardProps> {
|
|
render() {
|
|
const { card } = this.props;
|
|
const issueURLInfo = extractInfoFromIssueURL(card.issue.url);
|
|
const projectURL = `${issueURLInfo.baseURL}/${issueURLInfo.owner}/${issueURLInfo.projectName}`;
|
|
const issueURL = `${projectURL}/issues/${card.issue.number}`;
|
|
return (
|
|
<div className="kanboard-card">
|
|
<div className="box">
|
|
<div className="media">
|
|
{
|
|
card.issue.assignee ?
|
|
<div className="media-left">
|
|
<figure className="image is-64x64">
|
|
<img src={card.issue.assignee.avatar_url} alt="Image" />
|
|
</figure>
|
|
<small>{`@${card.issue.assignee.login}`}</small>
|
|
</div>
|
|
: null
|
|
}
|
|
<div className="media-content">
|
|
<div className="content">
|
|
<p>
|
|
<strong>{`#${card.issue.number}`}</strong>
|
|
{ card.issue.milestone ? <small>{`- ${card.issue.milestone.title}`}</small> : null }
|
|
<br />
|
|
<span className="is-size-6">{card.issue.title}</span>
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div className="level is-mobile" style={{marginTop:'1rem'}}>
|
|
<div className="level-left">
|
|
<small className="level-item"><a href={projectURL}>{card.project}</a></small>
|
|
</div>
|
|
<div className="level-right">
|
|
<a className="level-item" target="_blank" href={issueURL}>
|
|
<span className="icon is-small has-text-info">
|
|
<i className="fas fa-search" aria-hidden="true"></i>
|
|
</span>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
}
|
|
|
|
function extractInfoFromIssueURL(issueURL: string): any|void {
|
|
const pattern = /^(https?:\/\/[^\/]+)\/api\/v1\/repos\/([^\/]+)\/([^\/]+)\/.*$/;
|
|
const matches = pattern.exec(issueURL);
|
|
|
|
if (!matches) return;
|
|
|
|
return {
|
|
baseURL: matches[1],
|
|
owner: matches[2],
|
|
projectName: matches[3],
|
|
};
|
|
} |