+
{e.preventDefault(); e.stopPropagation(); detailsStore.show();}}>
+
+
Исп.: {props.store.issue.current_user.name}
+
Прио.: {props.store.issue.priority.name}
+
Версия: {props.store.issue.fixed_version?.name || ''}
+
Прогресс: {props.store.issue.done_ratio}
+
Трудозатраты: {props.store.issue.total_spent_hours} / {props.store.issue.total_estimated_hours}
+ {tagsSection}
);
});
diff --git a/frontend/src/misc-components/issue-details-dialog.module.css b/frontend/src/misc-components/issue-details-dialog.module.css
new file mode 100644
index 0000000..5a40573
--- /dev/null
+++ b/frontend/src/misc-components/issue-details-dialog.module.css
@@ -0,0 +1,21 @@
+.modal {
+ z-index: 1000;
+ position: fixed;
+ display: none;
+ padding-top: 100px;
+ left: 0;
+ top: 0;
+ width: 100%;
+ height: 100%;
+ overflow: auto;
+ background-color: rgb(0, 0, 0);
+ background-color: rgba(0, 0, 0, 0.4);
+}
+
+.modalContent {
+ background-color: #fefefe;
+ margin: auto;
+ padding: 20px;
+ border: 1px solid #888;
+ width: 80%;
+}
\ No newline at end of file
diff --git a/frontend/src/misc-components/issue-details-dialog.tsx b/frontend/src/misc-components/issue-details-dialog.tsx
new file mode 100644
index 0000000..c33be89
--- /dev/null
+++ b/frontend/src/misc-components/issue-details-dialog.tsx
@@ -0,0 +1,99 @@
+import React, { useEffect } 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';
+
+export const Store = types.model({
+ visible: types.boolean,
+ issue: types.frozen
()
+}).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;
+ }
+ };
+}).views((self) => {
+ return {
+ get displayStyle(): React.CSSProperties {
+ return {display: self.visible ? 'block' : 'none'};
+ }
+ };
+});
+
+export type Props = {
+ store: Instance
+};
+
+export const IssueDetailsDialog = observer((props: Props): JSX.Element => {
+ // DEBUG: begin
+ useEffect(() => {
+ console.debug(`Issue detailts dialog: issue_id=${props.store.issue.id}; subject=${props.store.issue?.subject || '-'}; description=${props.store.issue.description}; visible=${props.store.visible}`);
+ });
+ // DEBUG: end
+ return (
+
+
+
+
+
+
+
+
+
Описание:
+
+ {props.store.issue.description}
+
+
+
+
+
Комментарии:
+
+
+
+
+ );
+});
+
+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>
+ }
+ console.debug(`Comments: details=${JSON.stringify(props.details)}`); // DEBUG
+ const list = comments.map((detail) => {
+ const key = `issueid_${props.issue.id}_commentid_${detail.id}`;
+ return
+ });
+ return (
+ <>{list}>
+ );
+}
+
+export const Comment = (props: {data: RedmineTypes.Journal}): JSX.Element => {
+ console.debug(`Comment: data=${JSON.stringify(props.data)}`); // DEBUG
+ return (
+ <>
+ {props.data.user.name}:
+
+
+ {props.data.notes || '-'}
+
+
+
+ >
+ );
+}
\ No newline at end of file