113 lines
No EOL
3.2 KiB
TypeScript
113 lines
No EOL
3.2 KiB
TypeScript
import React from 'react';
|
||
import { RedmineTypes } from '../redmine-types';
|
||
import { observer } from 'mobx-react-lite';
|
||
import { Instance, types } from 'mobx-state-tree';
|
||
import * as IssueHrefNs from '../misc-components/issue-href';
|
||
import Css from './issue-details-dialog.module.css';
|
||
import * as UnreadedFlagNs from '../misc-components/unreaded-flag';
|
||
import { SetIssueReadingTimestamp } from '../utils/unreaded-provider';
|
||
import axios from 'axios';
|
||
import * as Luxon from 'luxon';
|
||
|
||
export const Store = types.model({
|
||
visible: types.boolean,
|
||
issue: types.frozen<RedmineTypes.ExtendedIssue>(),
|
||
unreadedFlagStore: types.maybe(UnreadedFlagNs.Store)
|
||
}).actions((self) => {
|
||
return {
|
||
hide: () => {
|
||
console.debug(`Issue details dialog hide: issue_id=${self.issue.id}`); // DEBUG
|
||
self.visible = false;
|
||
},
|
||
show: () => {
|
||
console.debug(`Issue details dialog show: issue_id=${self.issue.id}`); // DEBUG
|
||
self.visible = true;
|
||
if (self.unreadedFlagStore) {
|
||
self.unreadedFlagStore.read();
|
||
} else {
|
||
SetIssueReadingTimestamp(self.issue.id);
|
||
}
|
||
}
|
||
};
|
||
}).views((self) => {
|
||
return {
|
||
get displayStyle(): React.CSSProperties {
|
||
return {display: self.visible ? 'block' : 'none'};
|
||
}
|
||
};
|
||
});
|
||
|
||
export type Props = {
|
||
store: Instance<typeof Store>
|
||
};
|
||
|
||
export const IssueDetailsDialog = observer((props: Props): JSX.Element => {
|
||
const onUpdateClick = (e: React.MouseEvent) => {
|
||
const url = `${process.env.REACT_APP_BACKEND}redmine-event-emitter/append-issues`;
|
||
axios.post(url, [props.store.issue.id]);
|
||
};
|
||
const onCloseClick = (e: React.MouseEvent) => {
|
||
if (e.target !== e.currentTarget) return;
|
||
e.stopPropagation();
|
||
props.store.hide();
|
||
};
|
||
return (
|
||
<div className={Css.modal} style={props.store.displayStyle} onClick={onCloseClick}>
|
||
<div className={Css.modalContent}>
|
||
<h1>
|
||
<button onClick={onCloseClick}>close</button>
|
||
<button onClick={onUpdateClick}>force update</button>
|
||
<IssueHrefNs.IssueHref
|
||
id={props.store.issue?.id || -1}
|
||
subject={props.store.issue?.subject || ''}
|
||
tracker={props.store.issue?.tracker?.name || ''}
|
||
url={props.store.issue?.url?.url || ''}
|
||
/>
|
||
</h1>
|
||
<hr/>
|
||
<div>
|
||
<h2>Описание:</h2>
|
||
<pre>
|
||
{props.store.issue.description}
|
||
</pre>
|
||
</div>
|
||
<hr/>
|
||
<div>
|
||
<h2>Комментарии:</h2>
|
||
<Comments details={props.store.issue.journals || []} issue={props.store.issue}/>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
);
|
||
});
|
||
|
||
export const Comments = (props: {details?: RedmineTypes.Journal[], issue: RedmineTypes.ExtendedIssue}): JSX.Element => {
|
||
const comments = props.details?.filter((detail) => {
|
||
return Boolean(detail.notes);
|
||
});
|
||
if (!comments) {
|
||
return <>No comments</>
|
||
}
|
||
const list = comments.map((detail) => {
|
||
const key = `issueid_${props.issue.id}_commentid_${detail.id}`;
|
||
return <Comment data={detail} key={key}/>
|
||
});
|
||
return (
|
||
<>{list}</>
|
||
);
|
||
}
|
||
|
||
export const Comment = (props: {data: RedmineTypes.Journal}): JSX.Element => {
|
||
const date = Luxon.DateTime.fromISO(props.data.created_on).toFormat("dd.MM.yyyy HH:mm");
|
||
return (
|
||
<>
|
||
<h3><span className={Css.dateField}>{date}</span> {props.data.user.name}:</h3>
|
||
<div>
|
||
<pre>
|
||
{props.data.notes || '-'}
|
||
</pre>
|
||
</div>
|
||
<hr/>
|
||
</>
|
||
);
|
||
} |