Skip to content

Commit ece5b34

Browse files
committed
fix(tickets): customized status enhancements and fixes
1 parent 073a308 commit ece5b34

30 files changed

+491
-230
lines changed

public/js/5.js

+27-16
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

public/js/5.js.gz

1.15 KB
Binary file not shown.

src/backup/backup.js

+4-3
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,10 @@ const pkg = require('../../package.json')
2525
global.env = process.env.NODE_ENV || 'production'
2626

2727
let CONNECTION_URI = null
28+
let FILENAME = null
2829

2930
function createZip (callback) {
30-
const filename = 'trudesk-v' + pkg.version + '-' + moment().format('MMDDYYYY_HHmm') + '.zip'
31+
const filename = FILENAME
3132
const output = fs.createWriteStream(path.join(__dirname, '../../backups/', filename))
3233
const archive = archiver('zip', {
3334
zlib: { level: 9 }
@@ -124,6 +125,7 @@ function runBackup (callback) {
124125

125126
;(function () {
126127
CONNECTION_URI = process.env.MONGOURI
128+
FILENAME = process.env.FILENAME || 'trudesk-v' + pkg.version + '-' + moment().format('MMDDYYYY_HHmm') + '.zip'
127129

128130
if (!CONNECTION_URI) return process.send({ error: { message: 'Invalid connection uri' } })
129131
const options = {
@@ -151,9 +153,8 @@ function runBackup (callback) {
151153

152154
runBackup(function (err) {
153155
if (err) return process.send({ success: false, error: err })
154-
const filename = 'trudesk-' + moment().format('MMDDYYYY_HHmm') + '.zip'
155156

156-
winston.info('Backup completed successfully: ' + filename)
157+
winston.info('Backup completed successfully: ' + FILENAME)
157158
process.send({ success: true })
158159
})
159160
})

src/client/actions/tickets.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -80,10 +80,10 @@ export const updateStatus = createAction(UPDATE_STATUS.ACTION, ({ id, name, html
8080
}))
8181

8282
export const deletePriority = createAction(DELETE_PRIORITY.ACTION, ({ id, newPriority }) => ({ id, newPriority }))
83-
export const deleteStatus = createAction(DELETE_STATUS.ACTION, (id) => ({ id }))
83+
export const deleteStatus = createAction(DELETE_STATUS.ACTION, ({ id, newStatusId }) => ({ id, newStatusId }))
8484
export const getTagsWithPage = createAction(GET_TAGS_WITH_PAGE.ACTION, ({ limit, page }) => ({ limit, page }))
8585
export const tagsUpdateCurrentPage = createAction(TAGS_UPDATE_CURRENT_PAGE.ACTION, currentPage => ({ currentPage }))
8686
export const createTag = createAction(CREATE_TAG.ACTION, ({ name, currentPage }) => ({ name, currentPage }))
8787
export const transferToThirdParty = createAction(TRANSFER_TO_THIRDPARTY.ACTION, ({ uid }) => ({ uid }))
8888
export const fetchTicketTypes = createAction(FETCH_TICKET_TYPES.ACTION)
89-
export const fetchTicketStatus = createAction(FETCH_STATUS.ACTION)
89+
export const fetchTicketStatus = createAction(FETCH_STATUS.ACTION)

src/client/api/index.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -167,8 +167,8 @@ api.tickets.getStatus = () => {
167167
})
168168
}
169169

170-
api.tickets.deleteStatus = ({ id }) => {
171-
return axios.post(`/api/v1/tickets/status/${id}/delete`, {}).then(res => {
170+
api.tickets.deleteStatus = ({ id, newStatusId }) => {
171+
return axios.post(`/api/v1/tickets/status/${id}/delete`, { newStatusId }).then(res => {
172172
return res.data
173173
})
174174
}

src/client/components/Nav/Sidebar/index.jsx

-25
Original file line numberDiff line numberDiff line change
@@ -128,31 +128,6 @@ class Sidebar extends React.Component {
128128
href='/tickets/unassigned'
129129
active={activeSubItem === 'tickets-unassigned'}
130130
/>
131-
<NavSeparator />
132-
<SubmenuItem
133-
text='New'
134-
icon='&#xE24D;'
135-
href='/tickets/new'
136-
active={activeSubItem === 'tickets-new'}
137-
/>
138-
<SubmenuItem
139-
text='Pending'
140-
icon='&#xE629;'
141-
href='/tickets/pending'
142-
active={activeSubItem === 'tickets-pending'}
143-
/>
144-
<SubmenuItem
145-
text='Open'
146-
icon='&#xE2C8;'
147-
href='/tickets/open'
148-
active={activeSubItem === 'tickets-open'}
149-
/>
150-
<SubmenuItem
151-
text='Closed'
152-
icon='&#xE2C7;'
153-
href='/tickets/closed'
154-
active={activeSubItem === 'tickets-closed'}
155-
/>
156131
</Submenu>
157132
</SidebarItem>
158133
)}

src/client/components/SearchResults/index.jsx

+25-13
Original file line numberDiff line numberDiff line change
@@ -23,18 +23,6 @@ import $ from 'jquery'
2323
import helpers from 'lib/helpers'
2424

2525
class SearchResults extends React.Component {
26-
componentDidMount () {
27-
helpers.UI.setupDataTethers()
28-
$(document).on('mousedown', SearchResults.documentMouseEvent)
29-
}
30-
31-
componentDidUpdate () {}
32-
33-
componentWillUnmount () {
34-
SearchResults.toggleAnimation(true, false)
35-
$(document).off('mousedown', SearchResults.documentMouseEvent)
36-
}
37-
3826
static documentMouseEvent (event) {
3927
const $target = $(event.target)
4028
const isInContainer = $target.parents('.search-results-container').length > 0
@@ -67,6 +55,18 @@ class SearchResults extends React.Component {
6755
}
6856
}
6957

58+
componentDidMount () {
59+
helpers.UI.setupDataTethers()
60+
$(document).on('mousedown', SearchResults.documentMouseEvent)
61+
}
62+
63+
componentDidUpdate () {}
64+
65+
componentWillUnmount () {
66+
SearchResults.toggleAnimation(true, false)
67+
$(document).off('mousedown', SearchResults.documentMouseEvent)
68+
}
69+
7070
onSearchItemClick (e) {
7171
e.preventDefault()
7272
SearchResults.toggleAnimation(true, false)
@@ -89,8 +89,20 @@ class SearchResults extends React.Component {
8989
{searchResults &&
9090
searchResults.map(item => {
9191
const doc = item.get('_source')
92+
9293
return (
93-
<li key={item.get('_id')} className={`search-results-item status-${doc.get('status')}`}>
94+
<li key={item.get('_id')} className={`search-results-item`} style={{ position: 'relative' }}>
95+
<span
96+
style={{
97+
display: 'block',
98+
height: '100%',
99+
width: 5,
100+
background: doc.getIn(['status', 'htmlColor']),
101+
position: 'absolute',
102+
top: 0,
103+
left: 0
104+
}}
105+
></span>
94106
<a href={`/tickets/${doc.get('uid')}`} onClick={e => this.onSearchItemClick(e)}>
95107
<span className='priority' style={{ background: `${doc.getIn(['priority', 'htmlColor'])}` }} />
96108
<span className='uid'>{doc.get('uid')}</span>

src/client/containers/Modals/CreateStatusModal.jsx

+28-6
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,14 @@ import ColorSelector from 'components/ColorSelector'
2424

2525
import $ from 'jquery'
2626
import helpers from 'lib/helpers'
27+
import EnableSwitch from 'components/Settings/EnableSwitch'
2728

2829
@observer
2930
class CreateStatusModal extends React.Component {
3031
@observable name = ''
3132
@observable htmlColor = '#29B995'
33+
@observable slatimer = true
34+
@observable isResolved = false
3235

3336
constructor (props) {
3437
super(props)
@@ -48,13 +51,15 @@ class CreateStatusModal extends React.Component {
4851
// Form is valid... Submit..
4952
this.props.createStatus({
5053
name: this.name,
51-
htmlColor: this.htmlColor
54+
htmlColor: this.htmlColor,
55+
slatimer: this.slatimer,
56+
isResolved: this.isResolved
5257
})
5358
}
5459

5560
render () {
5661
return (
57-
<BaseModal {...this.props} ref={i => (this.base = i)}>
62+
<BaseModal {...this.props} large={true}>
5863
<form className={'uk-form-stacked'} onSubmit={e => this.onCreateStatusSubmit(e)}>
5964
<div className='uk-margin-medium-bottom uk-clearfix'>
6065
<h2>Create Status</h2>
@@ -63,7 +68,7 @@ class CreateStatusModal extends React.Component {
6368
<div>
6469
<div className='uk-clearfix'>
6570
<div className='z-box uk-grid uk-grid-collpase uk-clearfix'>
66-
<div className='uk-width-1-3'>
71+
<div className='uk-width-1-4'>
6772
<label>Status Name</label>
6873
<input
6974
type='text'
@@ -75,15 +80,33 @@ class CreateStatusModal extends React.Component {
7580
data-validation-error-msg='Invalid name (3+ characters)'
7681
/>
7782
</div>
78-
79-
<div className='uk-width-1-3'>
83+
84+
<div className='uk-width-1-4'>
8085
<ColorSelector
8186
hideRevert={true}
8287
defaultColor={'#29B995'}
8388
validationEnabled={true}
8489
onChange={e => (this.htmlColor = e.target.value)}
8590
/>
8691
</div>
92+
<div className={'uk-width-1-4'}>
93+
<div className={'uk-float-left'}>
94+
<EnableSwitch
95+
stateName={'slatimer'}
96+
label={'SLA'}
97+
checked={this.slatimer}
98+
onChange={e => (this.slatimer = e.target.checked)}
99+
/>
100+
</div>
101+
<div className={'uk-float-left'}>
102+
<EnableSwitch
103+
stateName={'isResolved'}
104+
label={'isResolved'}
105+
checked={this.isResolved}
106+
onChange={e => (this.isResolved = e.target.checked)}
107+
/>
108+
</div>
109+
</div>
87110
</div>
88111
<div className='uk-modal-footer uk-text-right'>
89112
<Button text={'Cancel'} type={'button'} extraClass={'uk-modal-close'} flat={true} waves={true} />
@@ -98,7 +121,6 @@ class CreateStatusModal extends React.Component {
98121
}
99122

100123
CreateStatusModal.propTypes = {
101-
onPriorityCreated: PropTypes.func,
102124
createStatus: PropTypes.func.isRequired
103125
}
104126

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
/*
2+
* . .o8 oooo
3+
* .o8 "888 `888
4+
* .o888oo oooo d8b oooo oooo .oooo888 .ooooo. .oooo.o 888 oooo
5+
* 888 `888""8P `888 `888 d88' `888 d88' `88b d88( "8 888 .8P'
6+
* 888 888 888 888 888 888 888ooo888 `"Y88b. 888888.
7+
* 888 . 888 888 888 888 888 888 .o o. )88b 888 `88b.
8+
* "888" d888b `V88V"V8P' `Y8bod88P" `Y8bod8P' 8""888P' o888o o888o
9+
* ========================================================================
10+
* Author: Chris Brame
11+
* Updated: 6/21/23 11:59 AM
12+
* Copyright (c) 2014-2023. All rights reserved.
13+
*/
14+
15+
import React from 'react'
16+
import PropTypes from 'prop-types'
17+
import { connect } from 'react-redux'
18+
import { fetchTicketStatus, deleteStatus } from 'actions/tickets'
19+
import BaseModal from './BaseModal'
20+
import Button from 'components/Button'
21+
import SingleSelect from 'components/SingleSelect'
22+
23+
import helpers from 'lib/helpers'
24+
25+
class DeleteTicketStatusModal extends React.Component {
26+
constructor (props) {
27+
super(props)
28+
this.state = {
29+
selectedStatus: ''
30+
}
31+
}
32+
33+
componentDidMount () {}
34+
35+
getTicketStatuses () {
36+
return this.props.settings && this.props.settings.get('status') ? this.props.settings.get('status').toArray() : []
37+
}
38+
39+
onSelectChanged (e) {
40+
this.setState({
41+
selectedStatus: e.target.value
42+
})
43+
}
44+
45+
onFormSubmit (e) {
46+
e.preventDefault()
47+
if (!this.state.selectedStatus) {
48+
helpers.UI.showSnackbar('Unable to get new ticket status. Aborting...', true)
49+
return true
50+
}
51+
52+
this.props.deleteStatus({ id: this.props.status.get('_id'), newStatusId: this.state.selectedStatus })
53+
}
54+
55+
render () {
56+
const { status } = this.props
57+
const mappedStatuses = this.getTicketStatuses()
58+
.filter(obj => {
59+
return status.get('name') !== obj.get('name')
60+
})
61+
.map(item => {
62+
return { text: item.get('name'), value: item.get('_id') }
63+
})
64+
return (
65+
<BaseModal {...this.props} options={{ bgclose: false }}>
66+
<form className={'uk-form-stacked'} onSubmit={e => this.onFormSubmit(e)}>
67+
<div className='uk-margin-medium-bottom uk-clearfix'>
68+
<h2>Remove Ticket Status</h2>
69+
<span>
70+
Please select the ticket status you wish to reassign tickets to in order to delete this ticket status.
71+
</span>
72+
</div>
73+
<div className='uk-margin-medium-bottom uk-clearfix'>
74+
<div className='uk-float-left' style={{ width: '100%' }}>
75+
<label className={'uk-form-label nopadding nomargin'}>Status</label>
76+
<SingleSelect
77+
showTextbox={false}
78+
items={mappedStatuses}
79+
onSelectChange={e => this.onSelectChanged(e)}
80+
value={this.state.selectedStatus}
81+
/>
82+
</div>
83+
</div>
84+
<div className='uk-margin-medium-bottom uk-clearfix'>
85+
<span className='uk-text-danger'>
86+
WARNING: This will change all tickets with status <strong>{status.get('name')}</strong> to the selected
87+
ticket status.
88+
<br />
89+
<strong>This is permanent!</strong>
90+
</span>
91+
</div>
92+
<div className='uk-modal-footer uk-text-right'>
93+
<Button text={'Cancel'} flat={true} waves={true} extraClass={'uk-modal-close'} />
94+
<Button text={'Delete'} style={'danger'} flat={true} type={'submit'} />
95+
</div>
96+
</form>
97+
</BaseModal>
98+
)
99+
}
100+
}
101+
102+
DeleteTicketStatusModal.propTypes = {
103+
status: PropTypes.object.isRequired,
104+
settings: PropTypes.object.isRequired,
105+
deleteStatus: PropTypes.func.isRequired,
106+
fetchTicketStatus: PropTypes.func.isRequired
107+
}
108+
109+
const mapStateToProps = state => ({
110+
settings: state.settings.settings,
111+
ticketStatuses: state.ticketsState.ticketStatuses
112+
})
113+
114+
export default connect(mapStateToProps, { fetchTicketStatus, deleteStatus })(DeleteTicketStatusModal)

src/client/containers/Modals/FilterTicketsModal.jsx

+3-3
Original file line numberDiff line numberDiff line change
@@ -91,8 +91,8 @@ class FilterTicketsModal extends React.Component {
9191
}
9292

9393
render () {
94-
const statuses = this.props.ticketStatuses.map(s => ({text: s.get('name'), value: s.get('uid')})).toArray()
95-
94+
const statuses = this.props.ticketStatuses.map(s => ({ text: s.get('name'), value: s.get('_id') })).toArray()
95+
9696
const tags = this.props.ticketTags
9797
.map(t => {
9898
return { text: t.get('name'), value: t.get('_id') }
@@ -229,7 +229,7 @@ const mapStateToProps = state => ({
229229
accountsState: state.accountsState,
230230
ticketTags: state.tagsSettings.tags,
231231
ticketTypes: state.ticketsState.types,
232-
ticketStatuses: state.ticketsState.ticketStatuses,
232+
ticketStatuses: state.ticketsState.ticketStatuses
233233
})
234234

235235
export default connect(mapStateToProps, {

src/client/containers/Modals/index.jsx

+3-1
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,9 @@ import DeleteTicketTypeModal from './DeleteTicketTypeModal'
2323
import FilterTicketModal from './FilterTicketsModal'
2424
import AddPriorityToTypeModal from './AddPriorityToTypeModal'
2525
import CreatePriorityModal from './CreatePriorityModal'
26-
import CreateStatusModal from './CreateStatusModal'
2726
import DeletePriorityModal from './DeletePriorityModal'
27+
import CreateStatusModal from './CreateStatusModal'
28+
import DeleteTicketStatusModal from './DeleteTicketStatusModal'
2829
import CreateTagModal from './CreateTagModal'
2930
import AddTagsModal from './AddTagsModal'
3031
import CreateTicketModal from './CreateTicketModal'
@@ -55,6 +56,7 @@ const MODAL_COMPONENTS = {
5556
CREATE_PRIORITY: CreatePriorityModal,
5657
DELETE_PRIORITY: DeletePriorityModal,
5758
CREATE_STATUS: CreateStatusModal,
59+
DELETE_STATUS: DeleteTicketStatusModal,
5860
CREATE_TAG: CreateTagModal,
5961
ADD_TAGS_MODAL: AddTagsModal,
6062
CREATE_ROLE: CreateRoleModal,

0 commit comments

Comments
 (0)