fix(ui): issue form\
This commit is contained in:
parent
1b3a3dfc21
commit
82621012de
7 changed files with 328 additions and 311 deletions
|
|
@ -1,178 +0,0 @@
|
|||
import { observer } from 'mobx-react-lite';
|
||||
import React from 'react';
|
||||
|
||||
import { useStore } from 'App/mstore';
|
||||
import { Button, CircularLoader, Form, Input, Loader } from 'UI';
|
||||
|
||||
import Select from 'Shared/Select';
|
||||
|
||||
const SelectedValue = ({ icon, text }) => {
|
||||
return (
|
||||
<div className="flex items-center">
|
||||
{icon}
|
||||
<span>{text}</span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
function IssueForm(props) {
|
||||
const { closeHandler } = props;
|
||||
const { issueReportingStore } = useStore();
|
||||
const creating = issueReportingStore.createLoading;
|
||||
const projects = issueReportingStore.projects;
|
||||
const projectsLoading = issueReportingStore.projectsLoading;
|
||||
const users = issueReportingStore.users;
|
||||
const instance = issueReportingStore.instance;
|
||||
const metaLoading = issueReportingStore.metaLoading;
|
||||
const issueTypes = issueReportingStore.issueTypes;
|
||||
const addActivity = issueReportingStore.saveIssue;
|
||||
const init = issueReportingStore.init;
|
||||
const edit = issueReportingStore.editInstance;
|
||||
const fetchMeta = issueReportingStore.fetchMeta;
|
||||
|
||||
React.useEffect(() => {
|
||||
init({
|
||||
projectId: projects[0] ? projects[0].id : '',
|
||||
issueType: issueTypes[0] ? issueTypes[0].id : '',
|
||||
});
|
||||
}, []);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (instance?.projectId) {
|
||||
fetchMeta(instance?.projectId).then(() => {
|
||||
edit({
|
||||
issueType: '',
|
||||
assignee: '',
|
||||
projectId: instance?.projectId,
|
||||
});
|
||||
});
|
||||
}
|
||||
}, [instance?.projectId]);
|
||||
|
||||
const onSubmit = () => {
|
||||
const { sessionId } = props;
|
||||
addActivity(sessionId, instance).then(() => {
|
||||
const { errors } = props;
|
||||
if (!errors || errors.length === 0) {
|
||||
init({ projectId: instance?.projectId });
|
||||
void issueReportingStore.fetchList(sessionId);
|
||||
closeHandler();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const write = (e) => {
|
||||
const {
|
||||
target: { name, value },
|
||||
} = e;
|
||||
edit({ [name]: value });
|
||||
};
|
||||
|
||||
const writeOption = ({ name, value }) => edit({ [name]: value.value });
|
||||
const projectOptions = projects.map(({ name, id }) => ({
|
||||
label: name,
|
||||
value: id,
|
||||
}));
|
||||
const userOptions = users.map(({ name, id }) => ({ label: name, value: id }));
|
||||
|
||||
const issueTypeOptions = issueTypes.map(({ name, id, iconUrl, color }) => {
|
||||
return { label: name, value: id, iconUrl, color };
|
||||
});
|
||||
|
||||
const selectedIssueType = issueTypes.filter(
|
||||
(issue) => issue.id == instance?.issueType
|
||||
)[0];
|
||||
|
||||
return (
|
||||
<Loader loading={projectsLoading} size={40}>
|
||||
<Form onSubmit={onSubmit} className="text-left">
|
||||
<Form.Field className="mb-15-imp">
|
||||
<label htmlFor="issueType" className="flex items-center">
|
||||
<span className="mr-2">Project</span>
|
||||
<CircularLoader loading={metaLoading} />
|
||||
</label>
|
||||
<Select
|
||||
name="projectId"
|
||||
options={projectOptions}
|
||||
defaultValue={instance?.projectId}
|
||||
fluid
|
||||
onChange={writeOption}
|
||||
placeholder="Project"
|
||||
/>
|
||||
</Form.Field>
|
||||
<Form.Field className="mb-15-imp">
|
||||
<label htmlFor="issueType">Issue Type</label>
|
||||
<Select
|
||||
selection
|
||||
name="issueType"
|
||||
labeled
|
||||
options={issueTypeOptions}
|
||||
defaultValue={instance?.issueType}
|
||||
fluid
|
||||
onChange={writeOption}
|
||||
placeholder="Select issue type"
|
||||
text={
|
||||
selectedIssueType ? (
|
||||
<SelectedValue
|
||||
icon={selectedIssueType.iconUrl}
|
||||
text={selectedIssueType.name}
|
||||
/>
|
||||
) : (
|
||||
''
|
||||
)
|
||||
}
|
||||
/>
|
||||
</Form.Field>
|
||||
|
||||
<Form.Field className="mb-15-imp">
|
||||
<label htmlFor="assignee">Assignee</label>
|
||||
<Select
|
||||
selection
|
||||
name="assignee"
|
||||
options={userOptions}
|
||||
fluid
|
||||
onChange={writeOption}
|
||||
placeholder="Select a user"
|
||||
/>
|
||||
</Form.Field>
|
||||
|
||||
<Form.Field className="mb-15-imp">
|
||||
<label htmlFor="title">Summary</label>
|
||||
<Input
|
||||
name="title"
|
||||
value={instance?.title}
|
||||
placeholder="Issue Title / Summary"
|
||||
onChange={write}
|
||||
/>
|
||||
</Form.Field>
|
||||
|
||||
<Form.Field className="mb-15-imp">
|
||||
<label htmlFor="description">Description</label>
|
||||
<textarea
|
||||
name="description"
|
||||
rows="2"
|
||||
value={instance?.description}
|
||||
placeholder="E.g. Found this issue at 3:29secs"
|
||||
onChange={write}
|
||||
className="text-area"
|
||||
/>
|
||||
</Form.Field>
|
||||
|
||||
<Button
|
||||
loading={creating}
|
||||
variant="primary"
|
||||
disabled={!instance?.isValid}
|
||||
className="float-left mr-2"
|
||||
type="submit"
|
||||
>
|
||||
{'Create'}
|
||||
</Button>
|
||||
<Button type="button" onClick={closeHandler}>
|
||||
{'Cancel'}
|
||||
</Button>
|
||||
</Form>
|
||||
</Loader>
|
||||
);
|
||||
}
|
||||
|
||||
export default observer(IssueForm);
|
||||
177
frontend/app/components/Session_/Issues/IssueForm.tsx
Normal file
177
frontend/app/components/Session_/Issues/IssueForm.tsx
Normal file
|
|
@ -0,0 +1,177 @@
|
|||
import { observer } from 'mobx-react-lite';
|
||||
import React, { useEffect } from 'react';
|
||||
import { useStore } from 'App/mstore';
|
||||
import { CircularLoader, Loader } from 'UI';
|
||||
import { Form, Input, Button, Select } from 'antd';
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
interface Props {
|
||||
closeHandler: () => void;
|
||||
sessionId: string;
|
||||
errors: string[];
|
||||
}
|
||||
|
||||
const IssueForm: React.FC<Props> = observer(({ closeHandler, sessionId, errors }) => {
|
||||
const {
|
||||
issueReportingStore: {
|
||||
createLoading: creating,
|
||||
projects,
|
||||
projectsLoading,
|
||||
users,
|
||||
instance,
|
||||
metaLoading,
|
||||
issueTypes,
|
||||
saveIssue,
|
||||
init,
|
||||
editInstance: edit,
|
||||
fetchMeta,
|
||||
fetchList
|
||||
}
|
||||
} = useStore();
|
||||
|
||||
useEffect(() => {
|
||||
init({
|
||||
projectId: projects[0]?.id || '',
|
||||
issueType: issueTypes[0]?.id || ''
|
||||
});
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (instance?.projectId) {
|
||||
fetchMeta(instance.projectId).then(() => {
|
||||
edit({ issueType: '', assignee: '', projectId: instance.projectId });
|
||||
});
|
||||
}
|
||||
}, [instance?.projectId]);
|
||||
|
||||
const onFinish = async () => {
|
||||
await saveIssue(sessionId, instance).then(() => {
|
||||
closeHandler();
|
||||
}).catch(() => {
|
||||
toast('Failed to create issue', { type: 'error' });
|
||||
});
|
||||
|
||||
// if (!errors || errors.length === 0) {
|
||||
// init({ projectId: instance?.projectId });
|
||||
// fetchList(sessionId);
|
||||
// closeHandler();
|
||||
// }
|
||||
};
|
||||
|
||||
const handleChange = (
|
||||
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>
|
||||
) => {
|
||||
const { name, value } = e.target;
|
||||
edit({ [name]: value });
|
||||
};
|
||||
|
||||
const handleSelect = (field: string) => (value: string) => {
|
||||
edit({ [field]: value });
|
||||
};
|
||||
|
||||
const projectOptions = projects.map(
|
||||
({ name, id }: { name: string; id: string }) => ({ label: name, value: id })
|
||||
);
|
||||
const userOptions = users.map(
|
||||
({ name, id }: { name: string; id: string }) => ({ label: name, value: id })
|
||||
);
|
||||
const issueTypeOptions = issueTypes.map((opt: any) => ({
|
||||
label: opt.name,
|
||||
value: opt.id,
|
||||
iconUrl: opt.iconUrl
|
||||
}));
|
||||
|
||||
return (
|
||||
<Loader loading={projectsLoading} size={40}>
|
||||
<Form onFinish={onFinish} layout="vertical" className="text-left">
|
||||
<Form.Item
|
||||
label={
|
||||
<>
|
||||
<span className="mr-2">Project</span>
|
||||
<CircularLoader loading={metaLoading} />
|
||||
</>
|
||||
}
|
||||
className="mb-15-imp"
|
||||
>
|
||||
<Select
|
||||
value={instance?.projectId}
|
||||
onChange={handleSelect('projectId')}
|
||||
placeholder="Project"
|
||||
>
|
||||
{projectOptions.map((option) => (
|
||||
<Select.Option key={option.value} value={option.value}>
|
||||
{option.label}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
<Form.Item label="Issue Type" className="mb-15-imp">
|
||||
<Select
|
||||
value={instance?.issueType}
|
||||
onChange={handleSelect('issueType')}
|
||||
placeholder="Select issue type"
|
||||
optionLabelProp="label"
|
||||
>
|
||||
{issueTypeOptions.map((option) => (
|
||||
<Select.Option
|
||||
key={option.value}
|
||||
value={option.value}
|
||||
label={
|
||||
<div className="flex items-center">
|
||||
{option.iconUrl}
|
||||
<span>{option.label}</span>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
{option.label}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
<Form.Item label="Assignee" className="mb-15-imp">
|
||||
<Select
|
||||
value={instance?.assignee}
|
||||
onChange={handleSelect('assignee')}
|
||||
placeholder="Select a user"
|
||||
>
|
||||
{userOptions.map((option) => (
|
||||
<Select.Option key={option.value} value={option.value}>
|
||||
{option.label}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
<Form.Item label="Summary" className="mb-15-imp">
|
||||
<Input
|
||||
name="title"
|
||||
value={instance?.title}
|
||||
placeholder="Issue Title / Summary"
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item label="Description" className="mb-15-imp">
|
||||
<Input.TextArea
|
||||
name="description"
|
||||
rows={2}
|
||||
value={instance?.description}
|
||||
placeholder="E.g. Found this issue at 3:29secs"
|
||||
onChange={handleChange}
|
||||
className="text-area"
|
||||
/>
|
||||
</Form.Item>
|
||||
<Button
|
||||
loading={creating}
|
||||
disabled={!instance?.isValid}
|
||||
className="float-left mr-2"
|
||||
type="primary"
|
||||
htmlType="submit"
|
||||
>
|
||||
Create
|
||||
</Button>
|
||||
<Button onClick={closeHandler}>Cancel</Button>
|
||||
</Form>
|
||||
</Loader>
|
||||
);
|
||||
});
|
||||
|
||||
export default IssueForm;
|
||||
|
|
@ -1,20 +1,25 @@
|
|||
import React from 'react';
|
||||
import stl from './issuesModal.module.css';
|
||||
import IssueForm from './IssueForm';
|
||||
import cn from 'classnames'
|
||||
import cn from 'classnames';
|
||||
|
||||
interface Props {
|
||||
sessionId: string;
|
||||
closeHandler: () => void;
|
||||
}
|
||||
|
||||
const IssuesModal = ({
|
||||
sessionId,
|
||||
closeHandler,
|
||||
}) => {
|
||||
sessionId,
|
||||
closeHandler
|
||||
}: Props) => {
|
||||
return (
|
||||
<div className={cn(stl.wrapper, 'h-screen')}>
|
||||
<h3 className="text-xl font-semibold">
|
||||
<span>Create Issue</span>
|
||||
</h3>
|
||||
<IssueForm sessionId={ sessionId } closeHandler={ closeHandler } />
|
||||
<IssueForm sessionId={sessionId} closeHandler={closeHandler} errors={[]} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default IssuesModal;
|
||||
|
|
@ -11,6 +11,7 @@ const Key = ({ label }: { label: string }) => (
|
|||
{label}
|
||||
</div>
|
||||
);
|
||||
|
||||
function Cell({ shortcut, text }: any) {
|
||||
return (
|
||||
<div className="flex items-center gap-2 justify-start rounded">
|
||||
|
|
@ -39,28 +40,26 @@ export const PlaybackSpeedShortcut = () => <Key label={'↑ / ↓'} />;
|
|||
|
||||
export function ShortcutGrid() {
|
||||
return (
|
||||
<div className={'p-4 overflow-y-auto h-screen'}>
|
||||
<div className={'mb-4 font-semibold text-xl'}>Keyboard Shortcuts</div>
|
||||
<div className=" grid grid-cols-1 grid-flow-row-dense auto-cols-max gap-4 justify-items-start">
|
||||
<Cell shortcut="⇧ + U" text="Copy Session URL with time" />
|
||||
<Cell shortcut="⇧ + C" text="Launch Console" />
|
||||
<Cell shortcut="⇧ + N" text="Launch Network" />
|
||||
<Cell shortcut="⇧ + P" text="Launch Performance" />
|
||||
<Cell shortcut="⇧ + R" text="Launch State" />
|
||||
<Cell shortcut="⇧ + E" text="Launch Events" />
|
||||
<Cell shortcut="⇧ + F" text="Play Session in Fullscreen" />
|
||||
<Cell shortcut="Space" text="Play/Pause Session" />
|
||||
<Cell shortcut="⇧ + X" text="Launch X-Ray" />
|
||||
<Cell shortcut="⇧ + A" text="Launch User Actions" />
|
||||
<Cell shortcut="⇧ + I" text="Launch More User Info" />
|
||||
<Cell shortcut="⇧ + M" text="Launch Options Menu" />
|
||||
<Cell shortcut="⇧ + >" text="Play Next Session" />
|
||||
<Cell shortcut="⇧ + <" text="Play Previous Session" />
|
||||
<Cell shortcut="→" text="Skip Forward" />
|
||||
<Cell shortcut="←" text="Skip Backward" />
|
||||
<Cell shortcut="↑" text="Playback Speed Up" />
|
||||
<Cell shortcut="↓" text="Playback Speed Down" />
|
||||
</div>
|
||||
|
||||
<div className=" grid grid-cols-1 grid-flow-row-dense auto-cols-max gap-4 justify-items-start">
|
||||
<Cell shortcut="⇧ + U" text="Copy Session URL with time" />
|
||||
<Cell shortcut="⇧ + C" text="Launch Console" />
|
||||
<Cell shortcut="⇧ + N" text="Launch Network" />
|
||||
<Cell shortcut="⇧ + P" text="Launch Performance" />
|
||||
<Cell shortcut="⇧ + R" text="Launch State" />
|
||||
<Cell shortcut="⇧ + E" text="Launch Events" />
|
||||
<Cell shortcut="⇧ + F" text="Play Session in Fullscreen" />
|
||||
<Cell shortcut="Space" text="Play/Pause Session" />
|
||||
<Cell shortcut="⇧ + X" text="Launch X-Ray" />
|
||||
<Cell shortcut="⇧ + A" text="Launch User Actions" />
|
||||
<Cell shortcut="⇧ + I" text="Launch More User Info" />
|
||||
<Cell shortcut="⇧ + M" text="Launch Options Menu" />
|
||||
<Cell shortcut="⇧ + >" text="Play Next Session" />
|
||||
<Cell shortcut="⇧ + <" text="Play Previous Session" />
|
||||
<Cell shortcut="→" text="Skip Forward" />
|
||||
<Cell shortcut="←" text="Skip Backward" />
|
||||
<Cell shortcut="↑" text="Playback Speed Up" />
|
||||
<Cell shortcut="↓" text="Playback Speed Down" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,11 @@
|
|||
import { ShareAltOutlined } from '@ant-design/icons';
|
||||
import { Button as AntButton, Switch, Tooltip, Dropdown } from 'antd';
|
||||
import cn from 'classnames';
|
||||
import { useModal } from "Components/Modal";
|
||||
import IssuesModal from "Components/Session_/Issues/IssuesModal";
|
||||
import IssuesModal from 'Components/Session_/Issues/IssuesModal';
|
||||
import { Link2, Keyboard } from 'lucide-react';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import React, { useMemo } from 'react';
|
||||
import { MoreOutlined } from '@ant-design/icons'
|
||||
import { MoreOutlined } from '@ant-design/icons';
|
||||
import { Icon } from 'UI';
|
||||
import { PlayerContext } from 'App/components/Session/playerContext';
|
||||
import { IFRAME } from 'App/constants/storageKeys';
|
||||
|
|
@ -15,12 +14,14 @@ import { checkParam, truncateStringToFit } from 'App/utils';
|
|||
import SessionTabs from 'Components/Session/Player/SharedComponents/SessionTabs';
|
||||
import { ShortcutGrid } from 'Components/Session_/Player/Controls/components/KeyboardHelp';
|
||||
import WarnBadge from 'Components/Session_/WarnBadge';
|
||||
import { toast } from "react-toastify";
|
||||
import { toast } from 'react-toastify';
|
||||
import HighlightButton from './Highlight/HighlightButton';
|
||||
|
||||
import SharePopup from '../shared/SharePopup/SharePopup';
|
||||
import QueueControls from './QueueControls';
|
||||
import { Bookmark as BookmarkIcn, BookmarkCheck, Vault } from "lucide-react";
|
||||
import { Bookmark as BookmarkIcn, BookmarkCheck, Vault } from 'lucide-react';
|
||||
import { useModal } from 'Components/ModalContext';
|
||||
import IssueForm from 'Components/Session_/Issues/IssueForm';
|
||||
|
||||
const disableDevtools = 'or_devtools_uxt_toggle';
|
||||
|
||||
|
|
@ -31,11 +32,11 @@ function SubHeader(props) {
|
|||
sessionStore,
|
||||
projectsStore,
|
||||
userStore,
|
||||
issueReportingStore,
|
||||
issueReportingStore
|
||||
} = useStore();
|
||||
const favorite = sessionStore.current.favorite;
|
||||
const isEnterprise = userStore.isEnterprise;
|
||||
const currentSession = sessionStore.current
|
||||
const currentSession = sessionStore.current;
|
||||
const projectId = projectsStore.siteId;
|
||||
const integrations = integrationsStore.issues.list;
|
||||
const { store } = React.useContext(PlayerContext);
|
||||
|
|
@ -43,7 +44,7 @@ function SubHeader(props) {
|
|||
const hasIframe = localStorage.getItem(IFRAME) === 'true';
|
||||
const [hideTools, setHideTools] = React.useState(false);
|
||||
const [isFavorite, setIsFavorite] = React.useState(favorite);
|
||||
const { showModal, hideModal } = useModal();
|
||||
const { openModal, closeModal } = useModal();
|
||||
|
||||
React.useEffect(() => {
|
||||
const hideDevtools = checkParam('hideTools');
|
||||
|
|
@ -62,7 +63,7 @@ function SubHeader(props) {
|
|||
|
||||
const issuesIntegrationList = integrationsStore.issues.list;
|
||||
const handleOpenIssueModal = () => {
|
||||
issueReportingStore.init();
|
||||
issueReportingStore.init({});
|
||||
if (!issueReportingStore.projectsFetched) {
|
||||
issueReportingStore.fetchProjects().then((projects) => {
|
||||
if (projects && projects[0]) {
|
||||
|
|
@ -70,13 +71,12 @@ function SubHeader(props) {
|
|||
}
|
||||
});
|
||||
}
|
||||
showModal(
|
||||
<IssuesModal
|
||||
provider={reportingProvider}
|
||||
sessionId={currentSession.sessionId}
|
||||
closeHandler={hideModal}
|
||||
/>
|
||||
)
|
||||
openModal(
|
||||
<IssueForm sessionId={currentSession.sessionId} closeHandler={closeModal} errors={[]} />,
|
||||
{
|
||||
title: 'Create Issue'
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
const reportingProvider = issuesIntegrationList[0]?.provider || '';
|
||||
|
|
@ -90,10 +90,10 @@ function SubHeader(props) {
|
|||
localStorage.setItem(disableDevtools, enabled ? '0' : '1');
|
||||
uxtestingStore.setHideDevtools(!enabled);
|
||||
};
|
||||
|
||||
|
||||
const showKbHelp = () => {
|
||||
showModal(<ShortcutGrid />, { right: true, width: 320 });
|
||||
}
|
||||
openModal(<ShortcutGrid />, { width: 320, title: 'Keyboard Shortcuts' });
|
||||
};
|
||||
|
||||
const vaultIcon = isEnterprise ? (
|
||||
<Vault size={16} strokeWidth={1} />
|
||||
|
|
@ -115,7 +115,7 @@ function SubHeader(props) {
|
|||
toast.success(isFavorite ? REMOVED_MESSAGE : ADDED_MESSAGE);
|
||||
setIsFavorite(!isFavorite);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
@ -126,13 +126,13 @@ function SubHeader(props) {
|
|||
? props.live
|
||||
? '#F6FFED'
|
||||
: '#EBF4F5'
|
||||
: undefined,
|
||||
: undefined
|
||||
}}
|
||||
>
|
||||
<WarnBadge
|
||||
siteId={projectId}
|
||||
siteId={projectId!}
|
||||
currentLocation={currentLocation}
|
||||
version={currentSession?.trackerVersion ?? undefined}
|
||||
version={currentSession?.trackerVersion ?? ""}
|
||||
/>
|
||||
|
||||
<SessionTabs />
|
||||
|
|
@ -164,7 +164,7 @@ function SubHeader(props) {
|
|||
<Dropdown
|
||||
menu={{
|
||||
items: [
|
||||
|
||||
|
||||
{
|
||||
key: '2',
|
||||
label: <div className={'flex items-center gap-2'}>
|
||||
|
|
@ -180,7 +180,7 @@ function SubHeader(props) {
|
|||
<span>Issues</span>
|
||||
</div>,
|
||||
disabled: !enabledIntegration,
|
||||
onClick: handleOpenIssueModal,
|
||||
onClick: handleOpenIssueModal
|
||||
},
|
||||
{
|
||||
key: '1',
|
||||
|
|
@ -1,60 +1,66 @@
|
|||
import { makeAutoObservable } from 'mobx';
|
||||
import ReportedIssue from "../types/session/assignment";
|
||||
import { issueReportsService } from "App/services";
|
||||
import ReportedIssue from '../types/session/assignment';
|
||||
import { issueReportsService } from 'App/services';
|
||||
import { makePersistable } from '.store/mobx-persist-store-virtual-858ce4d906/package';
|
||||
|
||||
export default class issueReportingStore {
|
||||
instance: ReportedIssue
|
||||
issueTypes: any[] = []
|
||||
list: any[] = []
|
||||
issueTypeIcons: {}
|
||||
users: any[] = []
|
||||
projects: any[] = []
|
||||
issuesFetched = false
|
||||
projectsFetched = false
|
||||
projectsLoading = false
|
||||
metaLoading = false
|
||||
createLoading = false
|
||||
export default class IssueReportingStore {
|
||||
instance!: ReportedIssue;
|
||||
issueTypes: any[] = [];
|
||||
list: any[] = [];
|
||||
issueTypeIcons: Record<string, string> = {};
|
||||
users: any[] = [];
|
||||
projects: any[] = [];
|
||||
issuesFetched = false;
|
||||
projectsFetched = false;
|
||||
projectsLoading = false;
|
||||
metaLoading = false;
|
||||
createLoading = false;
|
||||
|
||||
constructor() {
|
||||
makeAutoObservable(this);
|
||||
// void makePersistable(this, {
|
||||
// name: 'IssueReportingStore',
|
||||
// properties: ['issueTypes', 'list', 'projects', 'users', 'projectsFetched', 'issuesFetched', 'issueTypeIcons'],
|
||||
// expireIn: 60000 * 10,
|
||||
// removeOnExpiration: true,
|
||||
// storage: window.localStorage
|
||||
// });
|
||||
}
|
||||
|
||||
init = (instance: any) => {
|
||||
this.instance = new ReportedIssue(instance);
|
||||
init = (instanceData: any) => {
|
||||
this.instance = new ReportedIssue(instanceData);
|
||||
if (this.issueTypes.length > 0) {
|
||||
this.instance.issueType = this.issueTypes[0].id;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
editInstance = (data: any) => {
|
||||
const inst = this.instance
|
||||
this.instance = new ReportedIssue({ ...inst, ...data })
|
||||
}
|
||||
editInstance = (data: Partial<ReportedIssue>) => {
|
||||
Object.assign(this.instance, data);
|
||||
};
|
||||
|
||||
setList = (list: any[]) => {
|
||||
this.list = list;
|
||||
this.issuesFetched = true
|
||||
}
|
||||
this.issuesFetched = true;
|
||||
};
|
||||
|
||||
setProjects = (projects: any[]) => {
|
||||
this.projectsFetched = true;
|
||||
this.projects = projects;
|
||||
}
|
||||
};
|
||||
|
||||
setMeta = (data: any) => {
|
||||
const issueTypes = data.issueTypes || [];
|
||||
const itIcons = {}
|
||||
const itIcons: Record<string, string> = {};
|
||||
issueTypes.forEach((it: any) => {
|
||||
itIcons[it.id] = it.iconUrl
|
||||
})
|
||||
|
||||
itIcons[it.id] = it.iconUrl;
|
||||
});
|
||||
this.issueTypes = issueTypes;
|
||||
this.issueTypeIcons = itIcons;
|
||||
this.users = data.users || [];
|
||||
}
|
||||
};
|
||||
|
||||
fetchProjects = async () => {
|
||||
if (this.projectsLoading) return;
|
||||
if (this.projectsLoading || this.projects.length > 0) return;
|
||||
this.projectsLoading = true;
|
||||
try {
|
||||
const { data } = await issueReportsService.fetchProjects();
|
||||
|
|
@ -62,11 +68,11 @@ export default class issueReportingStore {
|
|||
this.projectsFetched = true;
|
||||
return data;
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
console.error(e);
|
||||
} finally {
|
||||
this.projectsLoading = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
fetchMeta = async (projectId: number) => {
|
||||
if (this.metaLoading) return;
|
||||
|
|
@ -75,32 +81,36 @@ export default class issueReportingStore {
|
|||
const { data } = await issueReportsService.fetchMeta(projectId);
|
||||
this.setMeta(data);
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
console.error(e);
|
||||
} finally {
|
||||
this.metaLoading = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
saveIssue = async (sessionId: string, params: any) => {
|
||||
saveIssue = async (sessionId: string, instance: ReportedIssue) => {
|
||||
if (this.createLoading) return;
|
||||
this.createLoading = true;
|
||||
try {
|
||||
const data = { ...params, assignee: params.assignee, issueType: params.issueType }
|
||||
const { data: issue } = await issueReportsService.saveIssue(sessionId, data);
|
||||
this.init(issue)
|
||||
const payload = { ...instance, assignee: instance.assignee + '', issueType: instance.issueType + '' };
|
||||
const resp = await issueReportsService.saveIssue(sessionId, payload);
|
||||
// const resp = await issueReportsService.saveIssue(sessionId, instance.toCreate());
|
||||
|
||||
// const { data: issue } = await issueReportsService.saveIssue(sessionId, payload);
|
||||
this.init(resp.data.issue);
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
throw e;
|
||||
// console.error(e);
|
||||
} finally {
|
||||
this.createLoading = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
fetchList = async (sessionId: string) => {
|
||||
try {
|
||||
const { data } = await issueReportsService.fetchIssueIntegrations(sessionId);
|
||||
this.setList(data);
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,15 +1,16 @@
|
|||
import { makeAutoObservable } from 'mobx';
|
||||
import Activity, { IActivity } from './activity';
|
||||
import { DateTime } from 'luxon';
|
||||
import { notEmptyString } from 'App/validate';
|
||||
import { notEmptyString } from 'App/validate';
|
||||
|
||||
interface IReportedIssue {
|
||||
export interface IReportedIssue {
|
||||
id: string;
|
||||
title: string;
|
||||
timestamp: number;
|
||||
timestamp: number | null;
|
||||
sessionId: string;
|
||||
projectId: string;
|
||||
siteId: string;
|
||||
activities: [];
|
||||
activities: any[];
|
||||
closed: boolean;
|
||||
assignee: string;
|
||||
commentsCount: number;
|
||||
|
|
@ -17,58 +18,61 @@ interface IReportedIssue {
|
|||
description: string;
|
||||
iconUrl: string;
|
||||
createdAt?: string;
|
||||
comments: IActivity[]
|
||||
users: { id: string }[]
|
||||
comments: IActivity[];
|
||||
users: { id: string }[];
|
||||
}
|
||||
|
||||
export default class ReportedIssue {
|
||||
id: IReportedIssue["id"];
|
||||
title: IReportedIssue["title"] = '';
|
||||
timestamp: IReportedIssue["timestamp"];
|
||||
sessionId: IReportedIssue["sessionId"];
|
||||
projectId: IReportedIssue["projectId"] = '';
|
||||
siteId: IReportedIssue["siteId"];
|
||||
activities: IReportedIssue["activities"];
|
||||
closed: IReportedIssue["closed"];
|
||||
assignee: IReportedIssue["assignee"] = '';
|
||||
issueType: IReportedIssue["issueType"] = '';
|
||||
description: IReportedIssue["description"] = '';
|
||||
iconUrl: IReportedIssue["iconUrl"] = '';
|
||||
id: IReportedIssue['id'] = '';
|
||||
title: IReportedIssue['title'] = '';
|
||||
timestamp: DateTime | null = null;
|
||||
sessionId: IReportedIssue['sessionId'] = '';
|
||||
projectId: IReportedIssue['projectId'] = '';
|
||||
siteId: IReportedIssue['siteId'] = '';
|
||||
activities: any[] = [];
|
||||
closed: IReportedIssue['closed'] = false;
|
||||
assignee: IReportedIssue['assignee'] = '';
|
||||
issueType: IReportedIssue['issueType'] = '';
|
||||
description: IReportedIssue['description'] = '';
|
||||
iconUrl: IReportedIssue['iconUrl'] = '';
|
||||
|
||||
constructor(assignment?: IReportedIssue) {
|
||||
makeAutoObservable(this);
|
||||
if (assignment) {
|
||||
Object.assign(this, {
|
||||
...assignment,
|
||||
timestamp: assignment.createdAt ? DateTime.fromISO(assignment.createdAt) : undefined,
|
||||
activities: assignment.comments ? assignment.comments.map(activity => {
|
||||
Object.assign(this, assignment);
|
||||
this.timestamp = assignment.createdAt ? DateTime.fromISO(assignment.createdAt) : null;
|
||||
this.activities = assignment.comments
|
||||
? assignment.comments.map((activity) => {
|
||||
if (assignment.users) {
|
||||
// @ts-ignore ???
|
||||
activity.user = assignment.users.filter(user => user.id === activity.author)[0];
|
||||
// @ts-ignore
|
||||
activity.user = assignment.users.find(user => user.id === activity.author);
|
||||
}
|
||||
return new Activity(activity)
|
||||
}) : []
|
||||
})
|
||||
return new Activity(activity);
|
||||
})
|
||||
: [];
|
||||
}
|
||||
}
|
||||
|
||||
toJS() {
|
||||
return this
|
||||
}
|
||||
|
||||
validate() {
|
||||
return !!this.projectId && !!this.issueType && notEmptyString(this.title) && notEmptyString(this.description)
|
||||
return (
|
||||
!!this.projectId &&
|
||||
!!this.issueType &&
|
||||
notEmptyString(this.title) &&
|
||||
notEmptyString(this.description)
|
||||
);
|
||||
}
|
||||
|
||||
get isValid() {
|
||||
return !!this.projectId && !!this.issueType && notEmptyString(this.title) && notEmptyString(this.description)
|
||||
return this.validate();
|
||||
}
|
||||
|
||||
toCreate() {
|
||||
return {
|
||||
title: this.title,
|
||||
description: this.description,
|
||||
assignee: this.assignee,
|
||||
issueType: this.issueType
|
||||
}
|
||||
assignee: this.assignee + '',
|
||||
issueType: this.issueType + '',
|
||||
projectId: this.projectId,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue