change(ui): split stuff into components, add store

This commit is contained in:
sylenien 2022-10-20 10:13:01 +02:00 committed by Delirium
parent 2f0b5359b7
commit 61829d8260
15 changed files with 184 additions and 96 deletions

View file

@ -1,17 +1,19 @@
import React from 'react';
import PlayLink from 'Shared/SessionItem/PlayLink';
import { connect } from 'react-redux';
import { countries } from 'App/constants';
import { session as sessionRoute } from 'App/routes';
import Session from './components/Session'
import MetaInfo from './components/MetaInfo'
import Title from './components/Title'
interface Props {
hideModal: () => void;
session: Record<string, any>;
account: Record<string, any>;
width: number;
height: number;
}
function BugReportModal({ hideModal, session, width, height }: Props) {
function BugReportModal({ hideModal, session, width, height, account }: Props) {
const {
userBrowser,
userDevice,
@ -26,7 +28,8 @@ function BugReportModal({ hideModal, session, width, height }: Props) {
sessionId,
} = session;
console.log(session.toJS())
console.log(session.toJS(), account.toJS?.())
const envObject = {
Device: `${userDevice}${userDeviceType !== userDevice ? ` ${userDeviceType}` : ''}`,
Resolution: `${width}x${height}`,
@ -43,88 +46,15 @@ function BugReportModal({ hideModal, session, width, height }: Props) {
className="flex flex-col p-4 gap-4 bg-white"
style={{ maxWidth: '70vw', width: 620, height: '100vh' }}
>
<Title />
<Title userName={account.name} />
<MetaInfo envObject={envObject} metadata={metadata} />
<Session user={userDisplayName} sessionId={sessionId} />
</div>
);
}
function Title() {
return (
<div className="flex items-center py-2 px-3 justify-between bg-gray-lightest rounded">
<div className="flex flex-col gap-2">
<div>Title</div>
<div className="text-gray-medium">By author</div>
</div>
<div>
<div>Severity</div>
<div>select here</div>
</div>
</div>
);
}
interface EnvObj {
Device: string;
Resolution: string;
Browser: string;
OS: string;
Country: string;
Rev?: string;
}
function MetaInfo({ envObject, metadata }: { envObject: EnvObj, metadata: Record<string, any> }) {
return (
<div className="flex gap-8">
<div className="flex flex-col gap-2">
<SectionTitle>Environment</SectionTitle>
{Object.keys(envObject).map((envTag) => (
<div className="flex items-center">
<div className="py-1 px-2">{envTag}</div>
<div className="py-1 px-2 text-gray-medium bg-light-blue-bg rounded">
{/* @ts-ignore */}
{envObject[envTag]}
</div>
</div>
))}
</div>
<div className="flex flex-col gap-2">
<SectionTitle>Metadata</SectionTitle>
{Object.keys(metadata).map((meta) => (
<div className="flex items-center rounded overflow-hidden bg-gray-lightest">
<div className="bg-gray-light-shade py-1 px-2">{meta}</div>
<div className="py-1 px-2 text-gray-medium">{metadata[meta]}</div>
</div>
))}
</div>
</div>
);
}
function Session({ user, sessionId }: { user: string, sessionId: string }) {
return (
<div>
<SectionTitle>Session recording</SectionTitle>
<div className="border rounded flex items-center justify-between p-2">
<div className="flex flex-col">
<div className="text-lg">{user}</div>
<div className="text-disabled-text">
{`${window.location.origin}/${window.location.pathname.split('/')[1]}${sessionRoute(sessionId)}`}
</div>
</div>
<PlayLink isAssist={false} viewed={false} sessionId={sessionId} />
</div>
</div>
);
}
function SectionTitle({ children }: { children: React.ReactNode }) {
return <div className="text-xl font-semibold mb-2">{children}</div>;
}
const WithUIState = connect((state) => ({ session: state.getIn(['sessions', 'current']) }))(
// @ts-ignore
const WithUIState = connect((state) => ({ session: state.getIn(['sessions', 'current']), account: state.getIn(['user', 'account']), }))(
BugReportModal
);

View file

@ -0,0 +1,40 @@
import React from 'react'
import SectionTitle from './SectionTitle'
interface EnvObj {
Device: string;
Resolution: string;
Browser: string;
OS: string;
Country: string;
Rev?: string;
}
export default function MetaInfo({ envObject, metadata }: { envObject: EnvObj, metadata: Record<string, any> }) {
return (
<div className="flex gap-8">
<div className="flex flex-col gap-2">
<SectionTitle>Environment</SectionTitle>
{Object.keys(envObject).map((envTag) => (
<div className="flex items-center">
<div className="py-1 px-2">{envTag}</div>
<div className="py-1 px-2 text-gray-medium bg-light-blue-bg rounded">
{/* @ts-ignore */}
{envObject[envTag]}
</div>
</div>
))}
</div>
<div className="flex flex-col gap-2">
<SectionTitle>Metadata</SectionTitle>
{Object.keys(metadata).map((meta) => (
<div className="flex items-center rounded overflow-hidden bg-gray-lightest">
<div className="bg-gray-light-shade py-1 px-2">{meta}</div>
<div className="py-1 px-2 text-gray-medium">{metadata[meta]}</div>
</div>
))}
</div>
</div>
);
}

View file

@ -0,0 +1,51 @@
import React from 'react';
import { useStore } from 'App/mstore';
import { observer } from 'mobx-react-lite';
import cn from 'classnames';
import { Tooltip } from 'react-tippy';
function ReportTitle() {
const { bugReportStore } = useStore();
const inputRef = React.createRef<HTMLInputElement>();
const toggleEdit = () => {
bugReportStore.toggleTitleEdit(true);
};
React.useEffect(() => {
if (inputRef.current && bugReportStore.isTitleEdit) {
inputRef.current?.focus();
}
}, [bugReportStore.isTitleEdit])
return (
<div>
{bugReportStore.isTitleEdit ? (
<input
ref={inputRef}
name="name"
className="rounded fluid border-0 -mx-2 px-2 h-8 text-2xl"
value={bugReportStore.reportTitle}
onChange={(e) => bugReportStore.setTitle(e.target.value)}
onBlur={() => bugReportStore.toggleTitleEdit(false)}
onFocus={() => bugReportStore.toggleTitleEdit(true)}
/>
) : (
// @ts-ignore
<Tooltip delay={100} arrow title="Double click to rename">
<div
onDoubleClick={toggleEdit}
className={cn(
'text-blue text-2xl h-8 flex items-center border-transparent',
'cursor-pointer select-none border-b border-b-borderColor-transparent hover:border-dotted hover:border-gray-medium'
)}
>
{bugReportStore.reportTitle}
</div>
</Tooltip>
)}
</div>
);
}
export default observer(ReportTitle);

View file

@ -0,0 +1,5 @@
import React from 'react'
export default function SectionTitle({ children }: { children: React.ReactNode }) {
return <div className="text-xl font-semibold mb-2">{children}</div>;
}

View file

@ -0,0 +1,21 @@
import React from 'react'
import SectionTitle from './SectionTitle';
import { session as sessionRoute } from 'App/routes';
import PlayLink from 'Shared/SessionItem/PlayLink';
export default function Session({ user, sessionId }: { user: string, sessionId: string }) {
return (
<div>
<SectionTitle>Session recording</SectionTitle>
<div className="border rounded flex items-center justify-between p-2">
<div className="flex flex-col">
<div className="text-lg">{user}</div>
<div className="text-disabled-text">
{`${window.location.origin}/${window.location.pathname.split('/')[1]}${sessionRoute(sessionId)}`}
</div>
</div>
<PlayLink isAssist={false} viewed={false} sessionId={sessionId} />
</div>
</div>
);
}

View file

@ -0,0 +1,17 @@
import React from 'react'
import ReportTitle from './ReportTitle';
export default function Title({ userName }: { userName: string }) {
return (
<div className="flex items-center py-2 px-3 justify-between bg-gray-lightest rounded">
<div className="flex flex-col gap-2">
<ReportTitle />
<div className="text-gray-medium">By {userName}</div>
</div>
<div>
<div>Severity</div>
<div>select here</div>
</div>
</div>
);
}

View file

@ -69,7 +69,7 @@ class Issues extends React.Component {
const provider = issuesIntegration.provider;
return (
<div className="relative">
<div className="relative h-full w-full p-3">
<div className={stl.buttonWrapper}>
<OutsideClickDetectingDiv onClickOutside={this.closeModal}>
<Tooltip
@ -91,16 +91,10 @@ class Issues extends React.Component {
</div>
}
>
<div
className="flex items-center"
onClick={this.handleOpen}
disabled={
!isModalDisplayed && (metaLoading || fetchIssuesLoading || projectsLoading)
}
>
<Icon name={`integrations/${provider === 'jira' ? 'jira' : 'github'}`} size="16" />
<span className="ml-2">Create Issue</span>
</div>
<div className="flex items-center" onClick={this.handleOpen} disabled={!isModalDisplayed && (metaLoading || fetchIssuesLoading || projectsLoading)}>
<Icon name={ `integrations/${ provider === 'jira' ? 'jira' : 'github'}` } size="16" />
<span className="ml-2">Create Issue</span>
</div>
</Tooltip>
</OutsideClickDetectingDiv>
</div>

View file

@ -80,7 +80,7 @@ function SubHeader(props) {
id={props.sessionId}
showCopyLink={true}
trigger={
<div className="flex items-center">
<div className="flex items-center h-full w-full">
<Icon
className="mr-2"
disabled={props.disabled}

View file

@ -62,7 +62,7 @@ export default class ItemMenu extends React.PureComponent<Props> {
<div
key={item.key}
role="menuitem"
className="w-full h-full p-3 hover:bg-gray-light-shade cursor-pointer flex items-center"
className="hover:bg-gray-light-shade cursor-pointer flex items-center"
>
{item.component}
</div>

View file

@ -28,6 +28,7 @@ function NotePopup({
<div
onClick={toggleNotePopup}
className={cn(
'h-full w-full p-3',
'mr-4 hover:bg-gray-light-shade rounded-md flex items-center',
tooltipActive ? 'cursor-not-allowed' : 'cursor-pointer'
)}

View file

@ -43,7 +43,7 @@ function Bookmark(props : Props ) {
distance={20}
>
{noMargin ? (
<div onClick={ toggleFavorite } className="flex items-center cursor-pointer">
<div onClick={ toggleFavorite } className="flex items-center cursor-pointer h-full w-full p-3">
<Icon name={ isFavorite ? ACTIVE_ICON : INACTIVE_ICON } color={isFavorite ? "teal" : undefined} size="16" />
<span className="ml-2">{isEnterprise ? 'Vault' : 'Bookmark'}</span>
</div>

View file

@ -92,7 +92,8 @@ export default class SharePopup extends React.PureComponent {
arrow
trigger="click"
shown={this.handleOpen}
// beforeHidden={this.handleClose}
className="h-full w-full p-3"
// beforeHidden={this.handleClose}
html={
<div className={styles.wrapper}>
<div className={styles.header}>

View file

@ -0,0 +1,25 @@
import { makeAutoObservable } from "mobx"
enum SeverityLevels {
Low,
Medium,
High
}
export default class BugReportStore {
reportTitle = 'Untitled Report'
isTitleEdit = false
severity = SeverityLevels.High
constructor() {
makeAutoObservable(this)
}
toggleTitleEdit(isEdit: boolean) {
this.isTitleEdit = isEdit
}
setTitle(title: string) {
this.reportTitle = title
}
}

View file

@ -21,6 +21,7 @@ import NotificationStore from './notificationStore';
import ErrorStore from './errorStore';
import SessionStore from './sessionStore';
import NotesStore from './notesStore';
import BugReportStore from './bugReportStore'
export class RootStore {
dashboardStore: DashboardStore;
@ -34,6 +35,7 @@ export class RootStore {
notificationStore: NotificationStore;
sessionStore: SessionStore;
notesStore: NotesStore;
bugReportStore: BugReportStore
constructor() {
this.dashboardStore = new DashboardStore();
@ -47,6 +49,7 @@ export class RootStore {
this.notificationStore = new NotificationStore();
this.sessionStore = new SessionStore();
this.notesStore = new NotesStore();
this.bugReportStore = new BugReportStore();
}
initClient() {

View file

@ -109,7 +109,7 @@ export default class NotesService {
})
}
sendSlackNotification(noteId: number, webhook: string) {
sendSlackNotification(noteId: string, webhook: string) {
return this.client.get(`/notes/${noteId}/slack/${webhook}`)
.then(r => {
if (r.ok) {