change(ui): add substeps to report steps
This commit is contained in:
parent
d162bb6ae7
commit
40af78bc92
7 changed files with 406 additions and 86 deletions
|
|
@ -11,6 +11,7 @@ import Title from './components/Title';
|
|||
import Comments from './components/Comments';
|
||||
import Steps from './components/Steps';
|
||||
import { mapEvents } from './utils';
|
||||
import { fetchList as fetchMembers } from 'Duck/member';
|
||||
|
||||
interface Props {
|
||||
hideModal: () => void;
|
||||
|
|
@ -25,9 +26,11 @@ interface Props {
|
|||
eventsList: Record<string, any>[];
|
||||
endTime: number;
|
||||
};
|
||||
fetchMembers: () => void
|
||||
members: any;
|
||||
}
|
||||
|
||||
function BugReportModal({ hideModal, session, width, height, account, xrayProps }: Props) {
|
||||
function BugReportModal({ hideModal, session, width, height, account, xrayProps, fetchMembers, members }: Props) {
|
||||
const reportRef = React.createRef<HTMLDivElement>();
|
||||
const [isRendering, setRendering] = React.useState(false);
|
||||
|
||||
|
|
@ -45,10 +48,9 @@ function BugReportModal({ hideModal, session, width, height, account, xrayProps
|
|||
metadata,
|
||||
sessionId,
|
||||
events,
|
||||
notes,
|
||||
} = session;
|
||||
|
||||
console.log(session.toJS());
|
||||
|
||||
const envObject: EnvData = {
|
||||
Device: `${userDevice}${userDeviceType !== userDevice ? ` ${userDeviceType}` : ''}`,
|
||||
Resolution: `${width}x${height}`,
|
||||
|
|
@ -76,9 +78,8 @@ function BugReportModal({ hideModal, session, width, height, account, xrayProps
|
|||
},
|
||||
};
|
||||
|
||||
console.log(bugReportStore)
|
||||
|
||||
React.useEffect(() => {
|
||||
fetchMembers()
|
||||
bugReportStore.updateReportDefaults(defaults);
|
||||
bugReportStore.setDefaultSteps(mapEvents(events));
|
||||
return () => bugReportStore.clearStore();
|
||||
|
|
@ -168,10 +169,10 @@ function BugReportModal({ hideModal, session, width, height, account, xrayProps
|
|||
className="bg-white overflow-y-scroll"
|
||||
style={{ maxWidth: '70vw', width: 620, height: '100vh' }}
|
||||
>
|
||||
<div className="flex flex-col p-4 gap-4 bg-white h-auto relative" ref={reportRef}>
|
||||
<div className="flex flex-col p-4 gap-4 bg-white relative" ref={reportRef}>
|
||||
<Title userName={account.name} />
|
||||
<MetaInfo envObject={envObject} metadata={metadata} />
|
||||
<Steps xrayProps={xrayProps} />
|
||||
<Steps xrayProps={xrayProps} notes={notes} members={members} />
|
||||
<Comments />
|
||||
<Session user={userDisplayName} sessionId={sessionId} sessionUrl={sessionUrl} />
|
||||
<div id="pdf-ignore" className="flex items-center gap-2 mt-4">
|
||||
|
|
@ -201,6 +202,8 @@ const WithUIState = connect((state) => ({
|
|||
session: state.getIn(['sessions', 'current']),
|
||||
// @ts-ignore
|
||||
account: state.getIn(['user', 'account']),
|
||||
}))(BugReportModal);
|
||||
// @ts-ignore
|
||||
members: state.getIn(['members', 'list']),
|
||||
}), { fetchMembers })(BugReportModal);
|
||||
|
||||
export default WithUIState;
|
||||
|
|
|
|||
|
|
@ -5,8 +5,9 @@ import { RADIUS } from '../utils';
|
|||
import SectionTitle from './SectionTitle';
|
||||
import XRay from './StepsComponents/XRay';
|
||||
import StepRenderer from './StepsComponents/StepRenderer';
|
||||
import StepRadius from './StepsComponents/StepRadius'
|
||||
import SubModal from './StepsComponents/SubModal'
|
||||
import StepRadius from './StepsComponents/StepRadius';
|
||||
import SubModal from './StepsComponents/SubModal';
|
||||
import { Note } from 'App/services/NotesService';
|
||||
|
||||
interface Props {
|
||||
xrayProps: {
|
||||
|
|
@ -16,9 +17,11 @@ interface Props {
|
|||
eventsList: Record<string, any>[];
|
||||
endTime: number;
|
||||
};
|
||||
notes: Note[];
|
||||
members: Record<string, any>[];
|
||||
}
|
||||
|
||||
function Steps({ xrayProps }: Props) {
|
||||
function Steps({ xrayProps, notes, members }: Props) {
|
||||
const { bugReportStore } = useStore();
|
||||
const [stepPickRadius, setRadius] = React.useState(RADIUS);
|
||||
const [timePointer, setPointer] = React.useState(0);
|
||||
|
|
@ -57,8 +60,11 @@ function Steps({ xrayProps }: Props) {
|
|||
<div className="flex items-center justify-between">
|
||||
<div className="mt-4 mb-2 text-gray-dark flex items-center gap-4">
|
||||
STEPS
|
||||
|
||||
<div id="pdf-ignore">{timePointer > 0 ? <StepRadius pickRadius={stepPickRadius} setRadius={setRadius} /> : null}</div>
|
||||
<div id="pdf-ignore">
|
||||
{timePointer > 0 ? (
|
||||
<StepRadius pickRadius={stepPickRadius} setRadius={setRadius} />
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-blue cursor-pointer" id="pdf-ignore" onClick={handleStepsSelection}>
|
||||
{!shouldShowEventReset ? (
|
||||
|
|
@ -77,7 +83,12 @@ function Steps({ xrayProps }: Props) {
|
|||
}
|
||||
/>
|
||||
{bugReportStore.isSubStepModalOpen ? (
|
||||
<SubModal type={bugReportStore.subModalType} toggleModal={bugReportStore.toggleSubStepModal} xrayProps={xrayProps}/>
|
||||
<SubModal
|
||||
members={members}
|
||||
type={bugReportStore.subModalType}
|
||||
notes={notes}
|
||||
xrayProps={xrayProps}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,60 +1,119 @@
|
|||
import React from 'react';
|
||||
import { Icon, ItemMenu } from 'UI';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { Step as IStep } from '../../types';
|
||||
const STEP_NAMES = { CLICKRAGE: 'Multiple click', CLICK: 'Clicked', LOCATION: 'Visited' };
|
||||
import { useStore } from 'App/mstore';
|
||||
import cn from 'classnames';
|
||||
import { Duration } from 'luxon';
|
||||
import { ErrorComp, NetworkComp, NoteComp } from './SubModalItems';
|
||||
|
||||
const SUBSTEP = {
|
||||
network: NetworkComp,
|
||||
note: NoteComp,
|
||||
error: ErrorComp,
|
||||
};
|
||||
|
||||
function Step({ step, ind, isDefault }: { step: IStep; ind: number; isDefault?: boolean }) {
|
||||
const { bugReportStore } = useStore();
|
||||
const [menuOpen, setMenu] = React.useState(false);
|
||||
|
||||
const menuItems = [
|
||||
{ icon: 'quotes', text: 'Add Note', onClick: () => bugReportStore.toggleSubStepModal(true, 'note') },
|
||||
{ icon: 'info-circle', text: `Add Error`, onClick: () => bugReportStore.toggleSubStepModal(true, 'error') },
|
||||
{ icon: 'network', text: 'Add Fetch/XHR', onClick: () => bugReportStore.toggleSubStepModal(true, 'network') },
|
||||
{
|
||||
icon: 'quotes',
|
||||
text: 'Add/Remove Note',
|
||||
onClick: () => bugReportStore.toggleSubStepModal(true, 'note', step.key),
|
||||
},
|
||||
{
|
||||
icon: 'info-circle',
|
||||
text: `Add/Remove Error`,
|
||||
onClick: () => bugReportStore.toggleSubStepModal(true, 'error', step.key),
|
||||
},
|
||||
{
|
||||
icon: 'network',
|
||||
text: 'Add/Remove Network Request',
|
||||
onClick: () => bugReportStore.toggleSubStepModal(true, 'network', step.key),
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'py-1 px-2 flex items-center gap-2 w-full rounded',
|
||||
menuOpen
|
||||
? 'bg-figmaColors-secondary-outlined-hover-background'
|
||||
: isDefault
|
||||
? ''
|
||||
: 'hover:bg-figmaColors-secondary-outlined-hover-background group'
|
||||
)}
|
||||
>
|
||||
<div className="rounded-3xl px-4 bg-gray-lightest">{ind + 1}</div>
|
||||
<div className="flex items-center gap-2">
|
||||
{/* @ts-ignore */}
|
||||
<Icon name={step.icon} size={16} color="gray-darkest" />
|
||||
<div className="px-2 text-disabled-text rounded bg-light-blue-bg">
|
||||
{Duration.fromMillis(step.time).toFormat('hh:mm:ss')}
|
||||
</div>
|
||||
{/* @ts-ignore */}
|
||||
<div className="font-semibold">{STEP_NAMES[step.type]}</div>
|
||||
<div className="text-gray-medium">{step.details}</div>
|
||||
</div>
|
||||
<div className="flex flex-col w-full">
|
||||
<div
|
||||
className={cn('group-hover:flex items-center ml-auto gap-4', menuOpen ? 'flex' : 'hidden')}
|
||||
className={cn(
|
||||
'py-1 px-2 flex items-start gap-2 w-full rounded',
|
||||
menuOpen
|
||||
? 'bg-figmaColors-secondary-outlined-hover-background'
|
||||
: isDefault
|
||||
? ''
|
||||
: 'hover:bg-figmaColors-secondary-outlined-hover-background group'
|
||||
)}
|
||||
>
|
||||
<ItemMenu
|
||||
label={
|
||||
<Icon name="plus" size={16} className="cursor-pointer hover:fill-gray-darkest" />
|
||||
}
|
||||
items={menuItems}
|
||||
flat
|
||||
onToggle={(isOpen) => setMenu(isOpen)}
|
||||
/>
|
||||
<div onClick={() => bugReportStore.removeStep(step)}>
|
||||
<Icon name="trash" size={16} className="cursor-pointer hover:fill-gray-darkest" />
|
||||
<div className="rounded-3xl px-4 bg-gray-lightest relative z-10">{ind + 1}</div>
|
||||
<div className="w-full">
|
||||
<div className="flex items-center w-full gap-2">
|
||||
{/* @ts-ignore */}
|
||||
<Icon name={step.icon} size={16} color="gray-darkest" className="relative z-10"/>
|
||||
<div className="px-2 text-disabled-text rounded bg-light-blue-bg">
|
||||
{Duration.fromMillis(step.time).toFormat('hh:mm:ss')}
|
||||
</div>
|
||||
{/* @ts-ignore */}
|
||||
<div className="font-semibold">{STEP_NAMES[step.type]}</div>
|
||||
<div className="text-gray-medium">{step.details}</div>
|
||||
<div
|
||||
className={cn(
|
||||
'group-hover:flex items-center ml-auto gap-4',
|
||||
menuOpen ? 'flex' : 'hidden'
|
||||
)}
|
||||
>
|
||||
<ItemMenu
|
||||
label={
|
||||
<Icon name="plus" size={16} className="cursor-pointer hover:fill-gray-darkest" />
|
||||
}
|
||||
items={menuItems}
|
||||
flat
|
||||
onToggle={(isOpen) => setMenu(isOpen)}
|
||||
/>
|
||||
<div onClick={() => bugReportStore.removeStep(step)}>
|
||||
<Icon name="trash" size={16} className="cursor-pointer hover:fill-gray-darkest" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{step.substeps?.length ? (
|
||||
<div className="flex flex-col gap-2 w-full mt-2 relative">
|
||||
{step.substeps.map((subStep) => {
|
||||
const Component = SUBSTEP[subStep.type];
|
||||
return (
|
||||
<div className="relative">
|
||||
<div
|
||||
key={subStep.key}
|
||||
className="rounded border py-1 px-2 w-full flex flex-col relative z-10"
|
||||
style={{ background: subStep.type === 'note' ? '#FFFEF5' : 'white' }}
|
||||
>
|
||||
{/* @ts-ignore */}
|
||||
<Component item={subStep} />
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
borderBottom: '1px solid #DDDDDD',
|
||||
borderLeft: '1px solid #DDDDDD',
|
||||
borderBottomLeftRadius: 6,
|
||||
position: 'absolute',
|
||||
zIndex: 1,
|
||||
left: -25,
|
||||
bottom: 10,
|
||||
height: '120%',
|
||||
width: 50,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Step;
|
||||
export default observer(Step);
|
||||
|
|
|
|||
|
|
@ -1,20 +1,48 @@
|
|||
import React from 'react';
|
||||
import { Icon, Button } from 'UI';
|
||||
import cn from 'classnames';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { Note } from 'App/services/NotesService';
|
||||
import { NoteItem, ErrorItem, NetworkReq, SubItem } from './SubModalItems';
|
||||
import { filterList, debounce } from 'App/utils';
|
||||
import { useStore } from 'App/mstore';
|
||||
|
||||
const Titles = {
|
||||
note: 'Note',
|
||||
network: 'Fetch/XHR',
|
||||
error: 'Console Error',
|
||||
};
|
||||
const Filters = {
|
||||
note: 'note message or author',
|
||||
network: 'url',
|
||||
error: 'error name or message',
|
||||
};
|
||||
|
||||
interface Props {
|
||||
type: 'note' | 'network' | 'error';
|
||||
toggleModal: (isOpen: boolean) => void;
|
||||
items: SubItem[];
|
||||
}
|
||||
|
||||
let debounceUpdate: any = () => {};
|
||||
|
||||
const SUB_ITEMS = {
|
||||
note: NoteItem,
|
||||
error: ErrorItem,
|
||||
network: NetworkReq,
|
||||
};
|
||||
|
||||
function ModalContent(props: Props) {
|
||||
const [selected, setSelected] = React.useState([]);
|
||||
const [searchStr, setSearch] = React.useState('');
|
||||
const list =
|
||||
searchStr !== ''
|
||||
? filterList(props.items, searchStr, ['url', 'name', 'title', 'message'])
|
||||
: props.items;
|
||||
|
||||
React.useEffect(() => {
|
||||
debounceUpdate = debounce((val: string) => setSearch(val), 250);
|
||||
}, []);
|
||||
|
||||
const SubItem = SUB_ITEMS[props.type];
|
||||
|
||||
return (
|
||||
<div className="flex flex-col p-4 bg-white gap-4 w-full">
|
||||
<div className="flex items-center gap-2">
|
||||
|
|
@ -22,25 +50,63 @@ function ModalContent(props: Props) {
|
|||
<Icon name="quotes" size={18} />
|
||||
</div>
|
||||
<div className="text-2xl font-semibold">{`Select ${Titles[props.type]}`}</div>
|
||||
<div className="ml-auto">
|
||||
<input
|
||||
onChange={(e) => debounceUpdate(e.target.value)}
|
||||
className="bg-white p-2 border border-borderColor-gray-light-shade rounded"
|
||||
placeholder={`Filter by ${Filters[props.type]}`}
|
||||
style={{ width: 250 }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="flex flex-col border rounded w-full"
|
||||
style={{ background: props.type === 'note' ? '#FFFEF5' : 'white' }}
|
||||
className="flex flex-col rounded -mx-4 px-4 py-2 bg-white"
|
||||
style={{ height: '90vh', overflowY: 'scroll', maxWidth: '70vw', width: 620 }}
|
||||
>
|
||||
<div className="p-2 border-b last:border-b-none w-full">item1</div>
|
||||
<div className="p-2 border-b last:border-b-none w-full">item2</div>
|
||||
{list.map((item) => (
|
||||
<React.Fragment key={item.key}>
|
||||
{/* @ts-ignore */}
|
||||
<SubItem item={item} />
|
||||
</React.Fragment>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-2">
|
||||
<Button disabled={selected.length === 0} variant="primary">
|
||||
Add Selected
|
||||
</Button>
|
||||
<Button variant="text-primary" onClick={() => props.toggleModal(false)}>Cancel</Button>
|
||||
</div>
|
||||
<ModalActionsObs />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function ModalActions() {
|
||||
const { bugReportStore } = useStore();
|
||||
|
||||
const removeModal = () => {
|
||||
bugReportStore.toggleSubStepModal(false, bugReportStore.subModalType, undefined)
|
||||
}
|
||||
const saveChoice = () => {
|
||||
bugReportStore.saveSubItems()
|
||||
removeModal()
|
||||
}
|
||||
return (
|
||||
<div className="flex items-center gap-2">
|
||||
<Button
|
||||
disabled={bugReportStore.pickedSubItems.size === 0}
|
||||
variant="primary"
|
||||
onClick={saveChoice}
|
||||
>
|
||||
Add Selected
|
||||
</Button>
|
||||
<Button
|
||||
variant="text-primary"
|
||||
onClick={removeModal}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const ModalActionsObs = observer(ModalActions);
|
||||
|
||||
interface ModalProps {
|
||||
xrayProps: {
|
||||
currentLocation: Record<string, any>[];
|
||||
|
|
@ -50,16 +116,56 @@ interface ModalProps {
|
|||
endTime: number;
|
||||
};
|
||||
type: 'note' | 'network' | 'error';
|
||||
toggleModal: (isOpen: boolean) => void;
|
||||
notes: Note[];
|
||||
members: Record<string, any>[];
|
||||
}
|
||||
|
||||
function SubModal(props: ModalProps) {
|
||||
let items;
|
||||
if (props.type === 'note') {
|
||||
items = props.notes.map((note) => ({
|
||||
type: 'note' as const,
|
||||
title: props.members.find((m) => m.id === note.userId)?.email || note.userId,
|
||||
message: note.message,
|
||||
time: 0,
|
||||
key: note.noteId as unknown as string,
|
||||
}));
|
||||
}
|
||||
if (props.type === 'error') {
|
||||
items = props.xrayProps.exceptionsList.map((error) => ({
|
||||
type: 'error' as const,
|
||||
time: error.time,
|
||||
message: error.message,
|
||||
name: error.name,
|
||||
key: error.key,
|
||||
}));
|
||||
}
|
||||
if (props.type === 'network') {
|
||||
items = props.xrayProps.resourceList.map((fetch) => ({
|
||||
type: 'network' as const,
|
||||
time: fetch.time,
|
||||
url: fetch.url,
|
||||
status: fetch.status,
|
||||
success: fetch.success,
|
||||
message: fetch.name,
|
||||
key: fetch.key,
|
||||
}));
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className="bg-white overflow-y-scroll absolute"
|
||||
style={{ maxWidth: '70vw', width: 620, height: '100vh', top: 0, right: 0, zIndex: 999 }}
|
||||
className="bg-white absolute"
|
||||
style={{
|
||||
maxWidth: '70vw',
|
||||
overflow: 'hidden',
|
||||
width: 620,
|
||||
height: '100vh',
|
||||
top: 0,
|
||||
right: 0,
|
||||
zIndex: 999,
|
||||
}}
|
||||
>
|
||||
<ModalContent type={props.type} toggleModal={props.toggleModal} />
|
||||
<ModalContent type={props.type} items={items} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,123 @@
|
|||
import React from 'react';
|
||||
import { Checkbox } from 'UI';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { useStore } from 'App/mstore';
|
||||
|
||||
interface Item {
|
||||
time: number;
|
||||
message: string;
|
||||
type: 'note' | 'network' | 'error';
|
||||
key: string;
|
||||
}
|
||||
|
||||
export interface INoteItem extends Item {
|
||||
title: string;
|
||||
}
|
||||
|
||||
export interface IError extends Item {
|
||||
name?: string;
|
||||
}
|
||||
|
||||
export interface INetworkReq extends Item {
|
||||
url: string;
|
||||
status: string;
|
||||
success: boolean;
|
||||
}
|
||||
|
||||
export type SubItem = INoteItem | IError | INetworkReq;
|
||||
|
||||
const safeStr = (ogStr: string) => (ogStr.length > 60 ? ogStr.slice(0, 60) + '...' : ogStr);
|
||||
|
||||
export const NetworkComp = ({ item }: { item: INetworkReq }) => (
|
||||
<div className="flex items-start flex-col z-10">
|
||||
<div className="flex items-center gap-2 text-disabled-text">
|
||||
<div>{item.time}</div>
|
||||
<div>{safeStr(item.url)}</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="px-1 bg-light-blue-bg rounded-xl font-mono">{item.status}</div>
|
||||
<div className={item.success ? '' : 'text-red'}>{safeStr(item.message)}</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
export const NetworkReq = observer(({ item }: { item: INetworkReq }) => {
|
||||
const { bugReportStore } = useStore();
|
||||
return (
|
||||
<SubModalItemContainer
|
||||
isChecked={bugReportStore.isSubItemChecked(item)}
|
||||
onChange={(isChecked) => bugReportStore.toggleSubItem(isChecked, item)}
|
||||
>
|
||||
<NetworkComp item={item} />
|
||||
</SubModalItemContainer>
|
||||
);
|
||||
});
|
||||
|
||||
export const NoteComp = ({ item }: { item: INoteItem }) => (
|
||||
<div className="flex items-start flex-col z-10">
|
||||
<div className="font-semibold">{item.title}</div>
|
||||
<div className="text-secondary">{item.message}</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
export const NoteItem = observer(({ item }: { item: INoteItem }) => {
|
||||
const { bugReportStore } = useStore();
|
||||
return (
|
||||
<SubModalItemContainer
|
||||
isChecked={bugReportStore.isSubItemChecked(item)}
|
||||
onChange={(isChecked) => bugReportStore.toggleSubItem(isChecked, item)}
|
||||
isNote
|
||||
>
|
||||
<NoteComp item={item} />
|
||||
</SubModalItemContainer>
|
||||
);
|
||||
});
|
||||
|
||||
export const ErrorComp = ({ item }: { item: IError }) => (
|
||||
<div className="flex items-start flex-col z-10">
|
||||
<div className="text-disabled-text">{item.time}</div>
|
||||
{item.name ? <div className="text-red">{item.name}</div> : null}
|
||||
<div className="text-secondary">{safeStr(item.message)}</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
export const ErrorItem = observer(({ item }: { item: IError }) => {
|
||||
const { bugReportStore } = useStore();
|
||||
return (
|
||||
<SubModalItemContainer
|
||||
isChecked={bugReportStore.isSubItemChecked(item)}
|
||||
onChange={(isChecked) => bugReportStore.toggleSubItem(isChecked, item)}
|
||||
>
|
||||
<ErrorComp item={item} />
|
||||
</SubModalItemContainer>
|
||||
);
|
||||
});
|
||||
|
||||
export function SubModalItemContainer({
|
||||
children,
|
||||
isChecked,
|
||||
onChange,
|
||||
isNote,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
isChecked: boolean;
|
||||
onChange: (arg: any) => void;
|
||||
isNote?: boolean;
|
||||
}) {
|
||||
return (
|
||||
<div
|
||||
className="flex items-start p-2 gap-2 shadow-border-gray hover:shadow-border-main hover:bg-active-blue cursor-pointer"
|
||||
style={{ background: isNote ? '#FFFEF5' : undefined }}
|
||||
onClick={() => onChange(!isChecked)}
|
||||
>
|
||||
<Checkbox
|
||||
name="isIncluded"
|
||||
type="checkbox"
|
||||
checked={isChecked}
|
||||
onChange={(e: any) => onChange(e.target.checked)}
|
||||
className="pt-1"
|
||||
/>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
import { SeverityLevels } from 'App/mstore/bugReportStore';
|
||||
import { SubItem, INoteItem, IError, INetworkReq } from './components/StepsComponents/SubModalItems';
|
||||
|
||||
export interface BugReportPdf extends ReportDefaults {
|
||||
title: string;
|
||||
|
|
@ -6,9 +7,8 @@ export interface BugReportPdf extends ReportDefaults {
|
|||
severity: SeverityLevels;
|
||||
steps: Step[];
|
||||
activity: {
|
||||
network: NetworkError[];
|
||||
console: ConsoleError[];
|
||||
clickRage: ClickRage[];
|
||||
network: INetworkReq[];
|
||||
console: IError[];
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -33,19 +33,6 @@ export interface EnvData {
|
|||
Resolution: string;
|
||||
}
|
||||
|
||||
export interface NetworkError {
|
||||
time: number;
|
||||
}
|
||||
|
||||
export interface ConsoleError {
|
||||
time: number;
|
||||
}
|
||||
|
||||
export interface ClickRage {
|
||||
time: number;
|
||||
}
|
||||
|
||||
export type SubStep = Note | Error | Request;
|
||||
|
||||
export interface Step {
|
||||
key: string;
|
||||
|
|
@ -53,7 +40,7 @@ export interface Step {
|
|||
time: number;
|
||||
details: string;
|
||||
icon: string;
|
||||
substeps?: SubStep[]
|
||||
substeps?: SubItem[]
|
||||
}
|
||||
|
||||
export interface Note {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { makeAutoObservable } from 'mobx';
|
||||
import { BugReportPdf, ReportDefaults, Step } from 'Components/Session_/BugReport/types';
|
||||
import { SubItem } from 'App/components/Session_/BugReport/components/StepsComponents/SubModalItems';
|
||||
|
||||
export enum SeverityLevels {
|
||||
Low,
|
||||
|
|
@ -20,6 +21,8 @@ export default class BugReportStore {
|
|||
sessionEventSteps: Step[] = [];
|
||||
chosenEventSteps: Step[] = [];
|
||||
subModalType: 'note' | 'network' | 'error';
|
||||
targetStep: string
|
||||
pickedSubItems: Map<string, SubItem> = new Map()
|
||||
|
||||
constructor() {
|
||||
makeAutoObservable(this);
|
||||
|
|
@ -38,6 +41,8 @@ export default class BugReportStore {
|
|||
this.chosenEventSteps = [];
|
||||
this.subModalType = undefined;
|
||||
this.isSubStepModalOpen = false;
|
||||
this.targetStep = undefined;
|
||||
this.pickedSubItems = new Map();
|
||||
}
|
||||
|
||||
toggleTitleEdit(isEdit: boolean) {
|
||||
|
|
@ -77,7 +82,8 @@ export default class BugReportStore {
|
|||
}
|
||||
|
||||
setSteps(steps: Step[]) {
|
||||
this.chosenEventSteps = steps;
|
||||
this.chosenEventSteps = steps.map(step => ({ ...step, substeps: undefined }));
|
||||
this.pickedSubItems = undefined;
|
||||
}
|
||||
|
||||
removeStep(step: Step) {
|
||||
|
|
@ -86,9 +92,34 @@ export default class BugReportStore {
|
|||
);
|
||||
}
|
||||
|
||||
toggleSubStepModal(isOpen: boolean, type: 'note' | 'network' | 'error') {
|
||||
toggleSubStepModal(isOpen: boolean, type: 'note' | 'network' | 'error', stepKey?: string) {
|
||||
this.isSubStepModalOpen = isOpen;
|
||||
this.subModalType = type;
|
||||
this.targetStep = stepKey
|
||||
}
|
||||
|
||||
toggleSubItem(isAdded: boolean, item: SubItem) {
|
||||
if (isAdded) {
|
||||
this.pickedSubItems.set(item.key, item)
|
||||
} else {
|
||||
this.pickedSubItems.delete(item.key)
|
||||
}
|
||||
}
|
||||
|
||||
isSubItemChecked(item: SubItem) {
|
||||
return this.pickedSubItems.has(item.key)
|
||||
}
|
||||
|
||||
saveSubItems() {
|
||||
const targetIndex = this.chosenEventSteps.findIndex(step => step.key === this.targetStep)
|
||||
const eventStepsCopy = this.chosenEventSteps
|
||||
const step = this.chosenEventSteps[targetIndex]
|
||||
if (this.pickedSubItems.size > 0) {
|
||||
step.substeps = Array.from(this.pickedSubItems, ([name, value]) => ({ ...value }));
|
||||
}
|
||||
eventStepsCopy[targetIndex] = step
|
||||
|
||||
return this.chosenEventSteps = eventStepsCopy
|
||||
}
|
||||
|
||||
resetSteps() {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue