fix(ui): some issue/assignment refactoring
This commit is contained in:
parent
df12385e5f
commit
f6a62d835a
17 changed files with 188 additions and 179 deletions
|
|
@ -36,7 +36,7 @@ function SessionList(props: Props) {
|
||||||
</div>
|
</div>
|
||||||
<Loader loading={props.loading}>
|
<Loader loading={props.loading}>
|
||||||
<NoContent
|
<NoContent
|
||||||
show={!props.loading && props.list.size === 0}
|
show={!props.loading && props.list.length === 0}
|
||||||
title={
|
title={
|
||||||
<div className="flex items-center justify-center flex-col">
|
<div className="flex items-center justify-center flex-col">
|
||||||
<AnimatedSVG name={ICONS.NO_LIVE_SESSIONS} size={170} />
|
<AnimatedSVG name={ICONS.NO_LIVE_SESSIONS} size={170} />
|
||||||
|
|
|
||||||
|
|
@ -53,5 +53,5 @@ export default connect(state => ({
|
||||||
users: state.getIn(['assignments', 'users']),
|
users: state.getIn(['assignments', 'users']),
|
||||||
loading: state.getIn(['assignments', 'fetchAssignment', 'loading']),
|
loading: state.getIn(['assignments', 'fetchAssignment', 'loading']),
|
||||||
issueTypeIcons: state.getIn(['assignments', 'issueTypeIcons']),
|
issueTypeIcons: state.getIn(['assignments', 'issueTypeIcons']),
|
||||||
issuesIntegration: state.getIn([ 'issues', 'list']).first() || {},
|
issuesIntegration: state.getIn([ 'issues', 'list'])[0] || {},
|
||||||
}))(IssueDetails);
|
}))(IssueDetails);
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { Form, Input, Button, CircularLoader, Loader } from 'UI';
|
import { Form, Input, Button, CircularLoader, Loader } from 'UI';
|
||||||
//import { } from 'Duck/issues';
|
|
||||||
import { addActivity, init, edit, fetchAssignments, fetchMeta } from 'Duck/assignments';
|
import { addActivity, init, edit, fetchAssignments, fetchMeta } from 'Duck/assignments';
|
||||||
import Select from 'Shared/Select';
|
import Select from 'Shared/Select';
|
||||||
|
|
||||||
|
|
@ -119,7 +118,6 @@ class IssueForm extends React.PureComponent {
|
||||||
selection
|
selection
|
||||||
name="assignee"
|
name="assignee"
|
||||||
options={userOptions}
|
options={userOptions}
|
||||||
// value={ instance.assignee }
|
|
||||||
fluid
|
fluid
|
||||||
onChange={this.writeOption}
|
onChange={this.writeOption}
|
||||||
placeholder="Select a user"
|
placeholder="Select a user"
|
||||||
|
|
@ -153,7 +151,7 @@ class IssueForm extends React.PureComponent {
|
||||||
<Button
|
<Button
|
||||||
loading={creating}
|
loading={creating}
|
||||||
variant="primary"
|
variant="primary"
|
||||||
disabled={!instance.validate()}
|
disabled={!instance.isValid}
|
||||||
className="float-left mr-2"
|
className="float-left mr-2"
|
||||||
type="submit"
|
type="submit"
|
||||||
>
|
>
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,8 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { Icon, Popover, Button } from 'UI';
|
import { Popover, Button } from 'UI';
|
||||||
import IssuesModal from './IssuesModal';
|
import IssuesModal from './IssuesModal';
|
||||||
import { fetchProjects, fetchMeta } from 'Duck/assignments';
|
import { fetchProjects, fetchMeta } from 'Duck/assignments';
|
||||||
import stl from './issues.module.css';
|
|
||||||
|
|
||||||
@connect(
|
@connect(
|
||||||
(state) => ({
|
(state) => ({
|
||||||
|
|
@ -15,9 +14,7 @@ import stl from './issues.module.css';
|
||||||
fetchIssueLoading: state.getIn(['assignments', 'fetchAssignment', 'loading']),
|
fetchIssueLoading: state.getIn(['assignments', 'fetchAssignment', 'loading']),
|
||||||
fetchIssuesLoading: state.getIn(['assignments', 'fetchAssignments', 'loading']),
|
fetchIssuesLoading: state.getIn(['assignments', 'fetchAssignments', 'loading']),
|
||||||
projectsLoading: state.getIn(['assignments', 'fetchProjects', 'loading']),
|
projectsLoading: state.getIn(['assignments', 'fetchProjects', 'loading']),
|
||||||
issuesIntegration: state.getIn(['issues', 'list']).first() || {},
|
issuesIntegration: state.getIn(['issues', 'list']) || {},
|
||||||
|
|
||||||
jiraConfig: state.getIn(['issues', 'list']).first(),
|
|
||||||
issuesFetched: state.getIn(['issues', 'issuesFetched']),
|
issuesFetched: state.getIn(['issues', 'issuesFetched']),
|
||||||
}),
|
}),
|
||||||
{ fetchMeta, fetchProjects }
|
{ fetchMeta, fetchProjects }
|
||||||
|
|
@ -58,13 +55,9 @@ class Issues extends React.Component {
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
sessionId,
|
sessionId,
|
||||||
isModalDisplayed,
|
|
||||||
projectsLoading,
|
|
||||||
metaLoading,
|
|
||||||
fetchIssuesLoading,
|
|
||||||
issuesIntegration,
|
issuesIntegration,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const provider = issuesIntegration.provider;
|
const provider = issuesIntegration.first()?.provider || '';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Popover
|
<Popover
|
||||||
|
|
@ -80,13 +73,6 @@ class Issues extends React.Component {
|
||||||
Create Issue
|
Create Issue
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
{/* <div
|
|
||||||
className="flex items-center cursor-pointer"
|
|
||||||
disabled={!isModalDisplayed && (metaLoading || fetchIssuesLoading || projectsLoading)}
|
|
||||||
>
|
|
||||||
<Icon name={`integrations/${provider === 'jira' ? 'jira' : 'github'}`} size="16" />
|
|
||||||
<span className="ml-2 whitespace-nowrap">Create Issue</span>
|
|
||||||
</div> */}
|
|
||||||
</Popover>
|
</Popover>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ import styles from './playerBlock.module.css';
|
||||||
fullscreen: state.getIn(['components', 'player', 'fullscreen']),
|
fullscreen: state.getIn(['components', 'player', 'fullscreen']),
|
||||||
sessionId: state.getIn(['sessions', 'current']).sessionId,
|
sessionId: state.getIn(['sessions', 'current']).sessionId,
|
||||||
disabled: state.getIn(['components', 'targetDefiner', 'inspectorMode']),
|
disabled: state.getIn(['components', 'targetDefiner', 'inspectorMode']),
|
||||||
jiraConfig: state.getIn(['issues', 'list']).first(),
|
jiraConfig: state.getIn(['issues', 'list'])[0],
|
||||||
}))
|
}))
|
||||||
export default class PlayerBlock extends React.PureComponent {
|
export default class PlayerBlock extends React.PureComponent {
|
||||||
render() {
|
render() {
|
||||||
|
|
|
||||||
|
|
@ -169,7 +169,7 @@ function SessionList(props: Props) {
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
show={!loading && list.size === 0}
|
show={!loading && list.length === 0}
|
||||||
>
|
>
|
||||||
{list.map((session: any) => (
|
{list.map((session: any) => (
|
||||||
<div key={session.sessionId} className="border-b">
|
<div key={session.sessionId} className="border-b">
|
||||||
|
|
@ -188,7 +188,7 @@ function SessionList(props: Props) {
|
||||||
<div className="flex items-center justify-between p-5">
|
<div className="flex items-center justify-between p-5">
|
||||||
<div>
|
<div>
|
||||||
Showing <span className="font-medium">{(currentPage - 1) * pageSize + 1}</span> to{' '}
|
Showing <span className="font-medium">{(currentPage - 1) * pageSize + 1}</span> to{' '}
|
||||||
<span className="font-medium">{(currentPage - 1) * pageSize + list.size}</span> of{' '}
|
<span className="font-medium">{(currentPage - 1) * pageSize + list.length}</span> of{' '}
|
||||||
<span className="font-medium">{numberWithCommas(total)}</span> sessions.
|
<span className="font-medium">{numberWithCommas(total)}</span> sessions.
|
||||||
</div>
|
</div>
|
||||||
<Pagination
|
<Pagination
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,9 @@ import { List, Map, Set } from 'immutable';
|
||||||
import Assignment from 'Types/session/assignment';
|
import Assignment from 'Types/session/assignment';
|
||||||
import Activity from 'Types/session/activity';
|
import Activity from 'Types/session/activity';
|
||||||
import withRequestState, { RequestTypes } from './requestStateCreator';
|
import withRequestState, { RequestTypes } from './requestStateCreator';
|
||||||
import { createListUpdater, createItemInListUpdater } from './funcTools/tools';
|
import { createListUpdater } from './funcTools/tools';
|
||||||
import { editType, initType } from './funcTools/crud/types';
|
import { editType, initType } from './funcTools/crud/types';
|
||||||
import { createInit, createEdit } from './funcTools/crud';
|
import { createInit, createEdit } from './funcTools/crud';
|
||||||
import IssuesType from 'Types/issue/issuesType'
|
|
||||||
|
|
||||||
const idKey = 'id';
|
const idKey = 'id';
|
||||||
const name = 'assignment';
|
const name = 'assignment';
|
||||||
|
|
@ -22,8 +21,8 @@ const INIT = initType(name);
|
||||||
|
|
||||||
const initialState = Map({
|
const initialState = Map({
|
||||||
list: List(),
|
list: List(),
|
||||||
instance: Assignment(),
|
instance: new Assignment(),
|
||||||
activeIssue: Assignment(),
|
activeIssue: new Assignment(),
|
||||||
issueTypes: List(),
|
issueTypes: List(),
|
||||||
issueTypeIcons: Set(),
|
issueTypeIcons: Set(),
|
||||||
users: List(),
|
users: List(),
|
||||||
|
|
@ -33,22 +32,23 @@ const initialState = Map({
|
||||||
|
|
||||||
const reducer = (state = initialState, action = {}) => {
|
const reducer = (state = initialState, action = {}) => {
|
||||||
const users = state.get('users');
|
const users = state.get('users');
|
||||||
var issueTypes = []
|
let issueTypes = []
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case INIT:
|
case INIT:
|
||||||
action.instance.issueType = issueTypes.length > 0 ? issueTypes[0].id : '';
|
action.instance.issueType = issueTypes.length > 0 ? issueTypes[0].id : '';
|
||||||
return state.set('instance', Assignment(action.instance));
|
return state.set('instance', new Assignment(action.instance));
|
||||||
case EDIT:
|
case EDIT:
|
||||||
return state.mergeIn([ 'instance' ], action.instance);
|
const inst = state.get('instance')
|
||||||
|
return state.set('instance', new Assignment({ ...inst, ...action.instance }));
|
||||||
case FETCH_PROJECTS.SUCCESS:
|
case FETCH_PROJECTS.SUCCESS:
|
||||||
return state.set('projects', List(action.data)).set('projectsFetched', true);
|
return state.set('projects', List(action.data)).set('projectsFetched', true);
|
||||||
case FETCH_ASSIGNMENTS.SUCCESS:
|
case FETCH_ASSIGNMENTS.SUCCESS:
|
||||||
return state.set('list', List(action.data).map(Assignment));
|
return state.set('list', List(action.data).map(as => new Assignment(as)));
|
||||||
case FETCH_ASSIGNMENT.SUCCESS:
|
case FETCH_ASSIGNMENT.SUCCESS:
|
||||||
return state.set('activeIssue', Assignment({ ...action.data, users}));
|
return state.set('activeIssue', new Assignment({ ...action.data, users}));
|
||||||
case FETCH_META.SUCCESS:
|
case FETCH_META.SUCCESS:
|
||||||
issueTypes = action.data.issueTypes
|
issueTypes = action.data.issueTypes
|
||||||
var issueTypeIcons = {}
|
const issueTypeIcons = {}
|
||||||
issueTypes.forEach(iss => {
|
issueTypes.forEach(iss => {
|
||||||
issueTypeIcons[iss.id] = iss.iconUrl
|
issueTypeIcons[iss.id] = iss.iconUrl
|
||||||
})
|
})
|
||||||
|
|
@ -56,12 +56,12 @@ const reducer = (state = initialState, action = {}) => {
|
||||||
.set('users', List(action.data.users))
|
.set('users', List(action.data.users))
|
||||||
.set('issueTypeIcons', issueTypeIcons)
|
.set('issueTypeIcons', issueTypeIcons)
|
||||||
case ADD_ACTIVITY.SUCCESS:
|
case ADD_ACTIVITY.SUCCESS:
|
||||||
const instance = Assignment(action.data);
|
const instance = new Assignment(action.data);
|
||||||
return listUpdater(state, instance);
|
return listUpdater(state, instance);
|
||||||
case ADD_MESSAGE.SUCCESS:
|
case ADD_MESSAGE.SUCCESS:
|
||||||
const user = users.filter(user => user.id === action.data.author).first();
|
const user = users.filter(user => user.id === action.data.author).first();
|
||||||
const activity = new Activity({ type: 'message', user, ...action.data,});
|
const activity = new Activity({ type: 'message', user, ...action.data,});
|
||||||
return state.updateIn([ 'activeIssue', 'activities' ], list => list.push(activity));
|
return state.update([ 'activeIssue' ], issue => issue.activities.push(activity));
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
@ -79,7 +79,7 @@ export default withRequestState({
|
||||||
export const init = createInit(name);
|
export const init = createInit(name);
|
||||||
export const edit = createEdit(name);
|
export const edit = createEdit(name);
|
||||||
|
|
||||||
export function fetchProjects(sessionId) {
|
export function fetchProjects() {
|
||||||
return {
|
return {
|
||||||
types: FETCH_PROJECTS.toArray(),
|
types: FETCH_PROJECTS.toArray(),
|
||||||
call: client => client.get(`/integrations/issues/list_projects`)
|
call: client => client.get(`/integrations/issues/list_projects`)
|
||||||
|
|
@ -100,13 +100,6 @@ export function fetchAssignments(sessionId) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function fetchAssigment(sessionId, id) {
|
|
||||||
return {
|
|
||||||
types: FETCH_ASSIGNMENT.toArray(),
|
|
||||||
call: client => client.get(`/sessions/${ sessionId }/assign/${ id }`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function addActivity(sessionId, params) {
|
export function addActivity(sessionId, params) {
|
||||||
const data = { ...params, assignee: params.assignee, issueType: params.issueType }
|
const data = { ...params, assignee: params.assignee, issueType: params.issueType }
|
||||||
return {
|
return {
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ import { createInit, createEdit } from './funcTools/crud';
|
||||||
const idKey = 'id';
|
const idKey = 'id';
|
||||||
const name = 'assignment';
|
const name = 'assignment';
|
||||||
const listUpdater = createListUpdater(idKey);
|
const listUpdater = createListUpdater(idKey);
|
||||||
const itemInListUpdater = createItemInListUpdater(idKey);
|
|
||||||
|
|
||||||
const FETCH_ASSIGNMENTS = new RequestTypes('asignment/FETCH_ASSIGNMENTS');
|
const FETCH_ASSIGNMENTS = new RequestTypes('asignment/FETCH_ASSIGNMENTS');
|
||||||
const FETCH_ISSUE = new RequestTypes('asignment/FETCH_ISSUE');
|
const FETCH_ISSUE = new RequestTypes('asignment/FETCH_ISSUE');
|
||||||
|
|
@ -23,8 +22,8 @@ const RESET_ACTIVE_ISSUE = 'assignment/RESET_ACTIVE_ISSUE';
|
||||||
|
|
||||||
const initialState = Map({
|
const initialState = Map({
|
||||||
list: List(),
|
list: List(),
|
||||||
instance: Assignment(),
|
instance: new Assignment(),
|
||||||
activeIssue: Assignment(),
|
activeIssue: new Assignment(),
|
||||||
issueTypes: List(),
|
issueTypes: List(),
|
||||||
issueTypeIcons: Set(),
|
issueTypeIcons: Set(),
|
||||||
users: List(),
|
users: List(),
|
||||||
|
|
@ -39,9 +38,9 @@ const reducer = (state = initialState, action = {}) => {
|
||||||
case FETCH_PROJECTS.SUCCESS:
|
case FETCH_PROJECTS.SUCCESS:
|
||||||
return state.set('projects', List(action.data));
|
return state.set('projects', List(action.data));
|
||||||
case FETCH_ASSIGNMENTS.SUCCESS:
|
case FETCH_ASSIGNMENTS.SUCCESS:
|
||||||
return state.set('list', List(action.data).map(Assignment));
|
return state.set('list', action.data.map(as => new Assignment(as)));
|
||||||
case ADD_ACTIVITY.SUCCESS:
|
case ADD_ACTIVITY.SUCCESS:
|
||||||
const instance = Assignment(action.data);
|
const instance = new Assignment(action.data);
|
||||||
return listUpdater(state, instance);
|
return listUpdater(state, instance);
|
||||||
case FETCH_META.SUCCESS:
|
case FETCH_META.SUCCESS:
|
||||||
issueTypes = action.data.issueTypes;
|
issueTypes = action.data.issueTypes;
|
||||||
|
|
@ -53,16 +52,16 @@ const reducer = (state = initialState, action = {}) => {
|
||||||
.set('users', List(action.data.users))
|
.set('users', List(action.data.users))
|
||||||
.set('issueTypeIcons', issueTypeIcons)
|
.set('issueTypeIcons', issueTypeIcons)
|
||||||
case FETCH_ISSUE.SUCCESS:
|
case FETCH_ISSUE.SUCCESS:
|
||||||
return state.set('activeIssue', Assignment({ ...action.data, users}));
|
return state.set('activeIssue', new Assignment({ ...action.data, users}));
|
||||||
case RESET_ACTIVE_ISSUE:
|
case RESET_ACTIVE_ISSUE:
|
||||||
return state.set('activeIssue', Assignment());
|
return state.set('activeIssue', new Assignment());
|
||||||
case ADD_MESSAGE.SUCCESS:
|
case ADD_MESSAGE.SUCCESS:
|
||||||
const user = users.filter(user => user.id === action.data.author).first();
|
const user = users.filter(user => user.id === action.data.author).first();
|
||||||
const activity = new Activity({ type: 'message', user, ...action.data,});
|
const activity = new Activity({ type: 'message', user, ...action.data,});
|
||||||
return state.updateIn([ 'activeIssue', 'activities' ], list => list.push(activity));
|
return state.updateIn([ 'activeIssue', 'activities' ], list => list.push(activity));
|
||||||
case INIT:
|
case INIT:
|
||||||
action.instance.issueType = issueTypes.length > 0 ? issueTypes[0].id : '';
|
action.instance.issueType = issueTypes.length > 0 ? issueTypes[0].id : '';
|
||||||
return state.set('instance', Assignment(action.instance));
|
return state.set('instance', new Assignment(action.instance));
|
||||||
case EDIT:
|
case EDIT:
|
||||||
return state.mergeIn([ 'instance' ], action.instance);
|
return state.mergeIn([ 'instance' ], action.instance);
|
||||||
default:
|
default:
|
||||||
|
|
@ -101,13 +100,6 @@ export function fetchProjects() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function fetchIssue(sessionId, id) {
|
|
||||||
return {
|
|
||||||
types: FETCH_ISSUE.toArray(),
|
|
||||||
call: client => client.get(`/sessions/${ sessionId }/assign/jira/${ id }`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function fetchMeta(projectId) {
|
export function fetchMeta(projectId) {
|
||||||
return {
|
return {
|
||||||
types: FETCH_META.toArray(),
|
types: FETCH_META.toArray(),
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import { List, Map } from 'immutable';
|
import { List, Map } from 'immutable';
|
||||||
import Session from 'Types/session';
|
import Session from 'Types/session';
|
||||||
import ErrorStack from 'Types/session/errorStack';
|
import ErrorStack from 'Types/session/errorStack';
|
||||||
|
import { Location, InjectedEvent } from 'Types/session/event'
|
||||||
import Watchdog from 'Types/watchdog';
|
import Watchdog from 'Types/watchdog';
|
||||||
import { clean as cleanParams } from 'App/api_client';
|
import { clean as cleanParams } from 'App/api_client';
|
||||||
import withRequestState, { RequestTypes } from './requestStateCreator';
|
import withRequestState, { RequestTypes } from './requestStateCreator';
|
||||||
|
|
@ -46,7 +47,7 @@ const defaultDateFilters = {
|
||||||
};
|
};
|
||||||
|
|
||||||
const initObj = {
|
const initObj = {
|
||||||
list: List(),
|
list: [],
|
||||||
sessionIds: [],
|
sessionIds: [],
|
||||||
current: new Session(),
|
current: new Session(),
|
||||||
total: 0,
|
total: 0,
|
||||||
|
|
@ -61,7 +62,7 @@ const initObj = {
|
||||||
filteredEvents: null,
|
filteredEvents: null,
|
||||||
eventsQuery: '',
|
eventsQuery: '',
|
||||||
showChatWindow: false,
|
showChatWindow: false,
|
||||||
liveSessions: List(),
|
liveSessions: [],
|
||||||
visitedEvents: List(),
|
visitedEvents: List(),
|
||||||
insights: List(),
|
insights: List(),
|
||||||
insightFilters: defaultDateFilters,
|
insightFilters: defaultDateFilters,
|
||||||
|
|
@ -76,7 +77,12 @@ const initObj = {
|
||||||
|
|
||||||
const initialState = Map(initObj);
|
const initialState = Map(initObj);
|
||||||
|
|
||||||
const reducer = (state = initialState, action = {}) => {
|
interface IAction extends Record<string, any>{
|
||||||
|
type: string;
|
||||||
|
data: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
const reducer = (state = initialState, action: IAction) => {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case FETCH_ERROR_STACK.SUCCESS:
|
case FETCH_ERROR_STACK.SUCCESS:
|
||||||
return state.set('errorStack', List(action.data.trace).map(es => new ErrorStack(es))).set('sourcemapUploaded', action.data.sourcemapUploaded);
|
return state.set('errorStack', List(action.data.trace).map(es => new ErrorStack(es))).set('sourcemapUploaded', action.data.sourcemapUploaded);
|
||||||
|
|
@ -87,13 +93,11 @@ const reducer = (state = initialState, action = {}) => {
|
||||||
const { sessions, total } = action.data;
|
const { sessions, total } = action.data;
|
||||||
const list = sessions.map(s => new Session(s));
|
const list = sessions.map(s => new Session(s));
|
||||||
|
|
||||||
|
console.log(sessions, list, action)
|
||||||
return state
|
return state
|
||||||
.set('list', list)
|
.set('list', list)
|
||||||
.set('sessionIds', list.map(({ sessionId }) => sessionId).toJS())
|
.set('sessionIds', list.map(({ sessionId }) => sessionId))
|
||||||
.set(
|
.set('favoriteList', list.filter(({ favorite }) => favorite))
|
||||||
'favoriteList',
|
|
||||||
list.filter(({ favorite }) => favorite)
|
|
||||||
)
|
|
||||||
.set('total', total);
|
.set('total', total);
|
||||||
case FETCH_AUTOPLAY_LIST.SUCCESS:
|
case FETCH_AUTOPLAY_LIST.SUCCESS:
|
||||||
let sessionIds = state.get('sessionIds');
|
let sessionIds = state.get('sessionIds');
|
||||||
|
|
@ -125,13 +129,13 @@ const reducer = (state = initialState, action = {}) => {
|
||||||
const events = action.filter.events;
|
const events = action.filter.events;
|
||||||
const session = new Session(action.data);
|
const session = new Session(action.data);
|
||||||
|
|
||||||
const matching = [];
|
const matching: number[] = [];
|
||||||
|
|
||||||
const visitedEvents = [];
|
const visitedEvents: Location[] = [];
|
||||||
const tmpMap = {};
|
const tmpMap = new Set();
|
||||||
session.events.forEach((event) => {
|
session.events.forEach((event) => {
|
||||||
if (event.type === 'LOCATION' && !tmpMap.hasOwnProperty(event.url)) {
|
if (event.type === 'LOCATION' && !tmpMap.has(event.url)) {
|
||||||
tmpMap[event.url] = event.url;
|
tmpMap.add(event.url);
|
||||||
visitedEvents.push(event);
|
visitedEvents.push(event);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -156,7 +160,7 @@ const reducer = (state = initialState, action = {}) => {
|
||||||
.set('host', visitedEvents[0] && visitedEvents[0].host);
|
.set('host', visitedEvents[0] && visitedEvents[0].host);
|
||||||
}
|
}
|
||||||
case FETCH_FAVORITE_LIST.SUCCESS:
|
case FETCH_FAVORITE_LIST.SUCCESS:
|
||||||
return state.set('favoriteList', List(action.data).map(s => new Session(s)));
|
return state.set('favoriteList', action.data.map(s => new Session(s)));
|
||||||
case TOGGLE_FAVORITE.SUCCESS: {
|
case TOGGLE_FAVORITE.SUCCESS: {
|
||||||
const id = action.sessionId;
|
const id = action.sessionId;
|
||||||
let mutableState = state
|
let mutableState = state
|
||||||
|
|
@ -186,7 +190,7 @@ const reducer = (state = initialState, action = {}) => {
|
||||||
diff = diff === 0 ? s1.startedAt - s2.startedAt : diff;
|
diff = diff === 0 ? s1.startedAt - s2.startedAt : diff;
|
||||||
return action.sign * diff;
|
return action.sign * diff;
|
||||||
};
|
};
|
||||||
return state.update('list', (list) => list.sort(comparator)).update('favoriteList', (list) => list.sort(comparator));
|
return state.update('list', (list: Session[]) => list.sort(comparator)).update('favoriteList', (list: Session[]) => list.sort(comparator));
|
||||||
}
|
}
|
||||||
case REDEFINE_TARGET: {
|
case REDEFINE_TARGET: {
|
||||||
// TODO: update for list
|
// TODO: update for list
|
||||||
|
|
|
||||||
|
|
@ -1,47 +0,0 @@
|
||||||
import Record from 'Types/Record';
|
|
||||||
import Activity from './activity';
|
|
||||||
import { List } from 'immutable';
|
|
||||||
import { DateTime } from 'luxon';
|
|
||||||
import { validateName, notEmptyString } from 'App/validate';
|
|
||||||
|
|
||||||
export default Record({
|
|
||||||
id: undefined,
|
|
||||||
title: '',
|
|
||||||
timestamp: undefined,
|
|
||||||
creatorId: undefined,
|
|
||||||
sessionId: undefined,
|
|
||||||
projectId: '',
|
|
||||||
siteId: undefined,
|
|
||||||
activities: List(),
|
|
||||||
closed: false,
|
|
||||||
assignee: '',
|
|
||||||
commentsCount: undefined,
|
|
||||||
issueType: '',
|
|
||||||
description: '',
|
|
||||||
iconUrl: ''
|
|
||||||
}, {
|
|
||||||
fromJS: (assignment) => ({
|
|
||||||
...assignment,
|
|
||||||
timestamp: assignment.createdAt ? DateTime.fromISO(assignment.createdAt) : undefined,
|
|
||||||
activities: assignment.comments ? List(assignment.comments).map(activity => {
|
|
||||||
if (assignment.users) {
|
|
||||||
activity.user = assignment.users.filter(user => user.id === activity.author).first();
|
|
||||||
}
|
|
||||||
return new Activity(activity)
|
|
||||||
}) : List()
|
|
||||||
}),
|
|
||||||
methods: {
|
|
||||||
validate: function() {
|
|
||||||
return !!this.projectId && !!this.issueType &&
|
|
||||||
notEmptyString(this.title) && notEmptyString(this.description)
|
|
||||||
},
|
|
||||||
toCreate: function() {
|
|
||||||
return {
|
|
||||||
title: this.title,
|
|
||||||
description: this.description,
|
|
||||||
assignee: this.assignee,
|
|
||||||
issueType: this.issueType
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
77
frontend/app/types/session/assignment.ts
Normal file
77
frontend/app/types/session/assignment.ts
Normal file
|
|
@ -0,0 +1,77 @@
|
||||||
|
import Activity, { IActivity } from './activity';
|
||||||
|
import { DateTime } from 'luxon';
|
||||||
|
import { notEmptyString } from 'App/validate';
|
||||||
|
|
||||||
|
interface IAssignment {
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
timestamp: number;
|
||||||
|
creatorId: string;
|
||||||
|
sessionId: string;
|
||||||
|
projectId: string;
|
||||||
|
siteId: string;
|
||||||
|
activities: [];
|
||||||
|
closed: boolean;
|
||||||
|
assignee: string;
|
||||||
|
commentsCount: number;
|
||||||
|
issueType: string;
|
||||||
|
description: string;
|
||||||
|
iconUrl: string;
|
||||||
|
createdAt?: string;
|
||||||
|
comments: IActivity[]
|
||||||
|
users: { id: string }[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class Assignment {
|
||||||
|
id: IAssignment["id"];
|
||||||
|
title: IAssignment["title"] = '';
|
||||||
|
timestamp: IAssignment["timestamp"];
|
||||||
|
creatorId: IAssignment["creatorId"];
|
||||||
|
sessionId: IAssignment["sessionId"];
|
||||||
|
projectId: IAssignment["projectId"] = '';
|
||||||
|
siteId: IAssignment["siteId"];
|
||||||
|
activities: IAssignment["activities"];
|
||||||
|
closed: IAssignment["closed"];
|
||||||
|
assignee: IAssignment["assignee"] = '';
|
||||||
|
commentsCount: IAssignment["commentsCount"];
|
||||||
|
issueType: IAssignment["issueType"] = '';
|
||||||
|
description: IAssignment["description"] = '';
|
||||||
|
iconUrl: IAssignment["iconUrl"] = '';
|
||||||
|
|
||||||
|
constructor(assignment?: IAssignment) {
|
||||||
|
if (assignment) {
|
||||||
|
Object.assign(this, {
|
||||||
|
...assignment,
|
||||||
|
timestamp: assignment.createdAt ? DateTime.fromISO(assignment.createdAt) : undefined,
|
||||||
|
activities: assignment.comments ? assignment.comments.map(activity => {
|
||||||
|
if (assignment.users) {
|
||||||
|
// @ts-ignore ???
|
||||||
|
activity.user = assignment.users.filter(user => user.id === activity.author)[0];
|
||||||
|
}
|
||||||
|
return new Activity(activity)
|
||||||
|
}) : []
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
toJS() {
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
validate() {
|
||||||
|
return !!this.projectId && !!this.issueType && notEmptyString(this.title) && notEmptyString(this.description)
|
||||||
|
}
|
||||||
|
|
||||||
|
get isValid() {
|
||||||
|
return !!this.projectId && !!this.issueType && notEmptyString(this.title) && notEmptyString(this.description)
|
||||||
|
}
|
||||||
|
|
||||||
|
toCreate() {
|
||||||
|
return {
|
||||||
|
title: this.title,
|
||||||
|
description: this.description,
|
||||||
|
assignee: this.assignee,
|
||||||
|
issueType: this.issueType
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -10,7 +10,7 @@ function getStck0InfoString(stack: Stack) {
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
type Stack = { function: string; url: string}[]
|
type Stack = { function: string; url: string }[]
|
||||||
|
|
||||||
export interface IError {
|
export interface IError {
|
||||||
sessionId: string
|
sessionId: string
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ export default class ErrorStack {
|
||||||
lineNo: IErrorStack["lineNo"]
|
lineNo: IErrorStack["lineNo"]
|
||||||
colNo: IErrorStack["colNo"]
|
colNo: IErrorStack["colNo"]
|
||||||
offset: IErrorStack["offset"]
|
offset: IErrorStack["offset"]
|
||||||
context:IErrorStack["context"]
|
context: IErrorStack["context"]
|
||||||
|
|
||||||
constructor(es: IErrorStack) {
|
constructor(es: IErrorStack) {
|
||||||
Object.assign(this, {
|
Object.assign(this, {
|
||||||
|
|
|
||||||
|
|
@ -111,7 +111,7 @@ class Input extends Event {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class Location extends Event {
|
export class Location extends Event {
|
||||||
readonly name = 'Location';
|
readonly name = 'Location';
|
||||||
readonly type = LOCATION;
|
readonly type = LOCATION;
|
||||||
url: LocationEvent["url"]
|
url: LocationEvent["url"]
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ const OTHER = 'other' as const;
|
||||||
function getResourceStatus(status: number, success: boolean) {
|
function getResourceStatus(status: number, success: boolean) {
|
||||||
if (status != null) return String(status);
|
if (status != null) return String(status);
|
||||||
if (typeof success === 'boolean' || typeof success === 'number') {
|
if (typeof success === 'boolean' || typeof success === 'number') {
|
||||||
return !!success
|
return !!success
|
||||||
? '2xx-3xx'
|
? '2xx-3xx'
|
||||||
: '4xx-5xx';
|
: '4xx-5xx';
|
||||||
}
|
}
|
||||||
|
|
@ -20,8 +20,12 @@ function getResourceStatus(status: number, success: boolean) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function getResourceSuccess(success: boolean, status: number) {
|
function getResourceSuccess(success: boolean, status: number) {
|
||||||
if (success != null) { return !!success }
|
if (success != null) {
|
||||||
if (status != null) { return status < 400 }
|
return !!success
|
||||||
|
}
|
||||||
|
if (status != null) {
|
||||||
|
return status < 400
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -56,7 +60,7 @@ interface IResource {
|
||||||
success: boolean,
|
success: boolean,
|
||||||
score: number,
|
score: number,
|
||||||
method: string,
|
method: string,
|
||||||
request:string,
|
request: string,
|
||||||
response: string,
|
response: string,
|
||||||
headerSize: number,
|
headerSize: number,
|
||||||
encodedBodySize: number,
|
encodedBodySize: number,
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import { Note } from 'App/services/NotesService'
|
||||||
|
|
||||||
const HASH_MOD = 1610612741;
|
const HASH_MOD = 1610612741;
|
||||||
const HASH_P = 53;
|
const HASH_P = 53;
|
||||||
|
|
||||||
function hashString(s: string): number {
|
function hashString(s: string): number {
|
||||||
let mul = 1;
|
let mul = 1;
|
||||||
let hash = 0;
|
let hash = 0;
|
||||||
|
|
@ -192,7 +193,7 @@ export default class Session {
|
||||||
const isMobile = ['console', 'mobile', 'tablet'].includes(userDeviceType);
|
const isMobile = ['console', 'mobile', 'tablet'].includes(userDeviceType);
|
||||||
|
|
||||||
const events: InjectedEvent[] = []
|
const events: InjectedEvent[] = []
|
||||||
const rawEvents: (EventData & { key: number})[] = []
|
const rawEvents: (EventData & { key: number })[] = []
|
||||||
|
|
||||||
if (session.events?.length) {
|
if (session.events?.length) {
|
||||||
(session.events as EventData[]).forEach((event: EventData, k) => {
|
(session.events as EventData[]).forEach((event: EventData, k) => {
|
||||||
|
|
@ -224,12 +225,13 @@ export default class Session {
|
||||||
|
|
||||||
const exceptions = (errors as IError[]).map(e => new SessionError(e)) || [];
|
const exceptions = (errors as IError[]).map(e => new SessionError(e)) || [];
|
||||||
|
|
||||||
const issuesList = (issues as IIssue[]).map((i, k) => new Issue({ ...i, time: i.timestamp - startedAt, key: k })) || [];
|
const issuesList = (issues as IIssue[]).map(
|
||||||
|
(i, k) => new Issue({ ...i, time: i.timestamp - startedAt, key: k })) || [];
|
||||||
|
|
||||||
const rawNotes = notes;
|
const rawNotes = notes;
|
||||||
const notesWithEvents = [...rawEvents, ...rawNotes].sort((a, b) => {
|
const notesWithEvents = [...rawEvents, ...rawNotes].sort((a, b) => {
|
||||||
// @ts-ignore just in case
|
// @ts-ignore just in case
|
||||||
const aTs = a.timestamp || a.time;
|
const aTs = a.timestamp || a.time;
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const bTs = b.timestamp || b.time;
|
const bTs = b.timestamp || b.time;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,62 +9,62 @@ export const CLOUDWATCH = 'cloudwatch';
|
||||||
export const ELASTICSEARCH = 'elasticsearch';
|
export const ELASTICSEARCH = 'elasticsearch';
|
||||||
export const SUMOLOGIC = 'sumologic';
|
export const SUMOLOGIC = 'sumologic';
|
||||||
|
|
||||||
export const typeList = [ OPENREPLAY, SENTRY, DATADOG, STACKDRIVER, ROLLBAR, BUGSNAG, CLOUDWATCH, ELASTICSEARCH, SUMOLOGIC ];
|
export const typeList = [OPENREPLAY, SENTRY, DATADOG, STACKDRIVER, ROLLBAR, BUGSNAG, CLOUDWATCH, ELASTICSEARCH, SUMOLOGIC];
|
||||||
|
|
||||||
export function isRed(event: StackEvent) {
|
export function isRed(event: StackEvent) {
|
||||||
if (!event.payload) return false;
|
if (!event.payload) return false;
|
||||||
switch(event.source) {
|
switch (event.source) {
|
||||||
case SENTRY:
|
case SENTRY:
|
||||||
return event.payload['event.type'] === 'error';
|
return event.payload['event.type'] === 'error';
|
||||||
case DATADOG:
|
case DATADOG:
|
||||||
return true;
|
return true;
|
||||||
case STACKDRIVER:
|
case STACKDRIVER:
|
||||||
return false;
|
return false;
|
||||||
case ROLLBAR:
|
case ROLLBAR:
|
||||||
return true;
|
return true;
|
||||||
case NEWRELIC:
|
case NEWRELIC:
|
||||||
return true;
|
return true;
|
||||||
case BUGSNAG:
|
case BUGSNAG:
|
||||||
return true;
|
return true;
|
||||||
case CLOUDWATCH:
|
case CLOUDWATCH:
|
||||||
return true;
|
return true;
|
||||||
case SUMOLOGIC:
|
case SUMOLOGIC:
|
||||||
return false;
|
return false;
|
||||||
default:
|
default:
|
||||||
return event.level === 'error';
|
return event.level === 'error';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IStackEvent {
|
export interface IStackEvent {
|
||||||
time: number;
|
time: number;
|
||||||
timestamp: number;
|
timestamp: number;
|
||||||
index: number;
|
index: number;
|
||||||
name: string;
|
name: string;
|
||||||
message: string;
|
message: string;
|
||||||
payload: any;
|
payload: any;
|
||||||
source: any;
|
source: any;
|
||||||
level: string;
|
level: string;
|
||||||
|
|
||||||
isRed: () => boolean;
|
isRed: () => boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class StackEvent {
|
export default class StackEvent {
|
||||||
time: IStackEvent["time"]
|
time: IStackEvent["time"]
|
||||||
index: IStackEvent["index"];
|
index: IStackEvent["index"];
|
||||||
name: IStackEvent["name"];
|
name: IStackEvent["name"];
|
||||||
message: IStackEvent["message"];
|
message: IStackEvent["message"];
|
||||||
payload: IStackEvent["payload"];
|
payload: IStackEvent["payload"];
|
||||||
source: IStackEvent["source"];
|
source: IStackEvent["source"];
|
||||||
level: IStackEvent["level"];
|
level: IStackEvent["level"];
|
||||||
|
|
||||||
constructor(evt: IStackEvent) {
|
constructor(evt: IStackEvent) {
|
||||||
Object.assign(this, {
|
Object.assign(this, {
|
||||||
...evt,
|
...evt,
|
||||||
source: evt.source || OPENREPLAY
|
source: evt.source || OPENREPLAY
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
isRed() {
|
isRed() {
|
||||||
return isRed(this);
|
return isRed(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue