Merge branch 'master' into master
This commit is contained in:
commit
e24ec402e4
|
@ -59,10 +59,11 @@ class TicketViewer extends React.Component {
|
|||
commentEdited: false,
|
||||
commentPrivate: false,
|
||||
edit: false,
|
||||
editTitle: false,
|
||||
editId: 0,
|
||||
tagSelectorLoading: false,
|
||||
editTitle: false,
|
||||
newTitle: this.props.ticket.title,
|
||||
editTitleError: false
|
||||
editTitleError: false,
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
|
@ -176,7 +177,14 @@ class TicketViewer extends React.Component {
|
|||
onChange={this.onDepartmentDropdownChanged.bind(this)} />
|
||||
</div>
|
||||
<div className="col-md-4">{ticket.author.name}</div>
|
||||
<div className="col-md-4"> <TagSelector items={this.props.tags} values={this.props.ticket.tags} onRemoveClick={this.removeTag.bind(this)} onTagSelected={this.addTag.bind(this)}/></div>
|
||||
<div className="col-md-4">
|
||||
<TagSelector
|
||||
items={this.props.tags}
|
||||
values={this.props.ticket.tags}
|
||||
onRemoveClick={this.removeTag.bind(this)}
|
||||
onTagSelected={this.addTag.bind(this)}
|
||||
loading={this.state.tagSelectorLoading}/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="ticket-viewer__info-row-header row">
|
||||
<div className="col-md-4">{i18n('PRIORITY')}</div>
|
||||
|
@ -185,7 +193,11 @@ class TicketViewer extends React.Component {
|
|||
</div>
|
||||
<div className="ticket-viewer__info-row-values row">
|
||||
<div className="col-md-4">
|
||||
<DropDown className="ticket-viewer__editable-dropdown" items={priorityList} selectedIndex={priorities[ticket.priority]} onChange={this.onPriorityDropdownChanged.bind(this)} />
|
||||
<DropDown
|
||||
className="ticket-viewer__editable-dropdown"
|
||||
items={priorityList}
|
||||
selectedIndex={priorities[ticket.priority]}
|
||||
onChange={this.onPriorityDropdownChanged.bind(this)} />
|
||||
</div>
|
||||
<div className="col-md-4">
|
||||
{this.renderAssignStaffList()}
|
||||
|
@ -510,23 +522,47 @@ class TicketViewer extends React.Component {
|
|||
}
|
||||
|
||||
addTag(tag) {
|
||||
this.setState({
|
||||
tagSelectorLoading: true,
|
||||
})
|
||||
API.call({
|
||||
path: '/ticket/add-tag',
|
||||
data: {
|
||||
ticketNumber: this.props.ticket.ticketNumber,
|
||||
tagId: tag
|
||||
}
|
||||
}).then(this.onTicketModification.bind(this))
|
||||
})
|
||||
.then(() => {
|
||||
this.setState({
|
||||
tagSelectorLoading: false,
|
||||
});
|
||||
this.onTicketModification();
|
||||
})
|
||||
.catch(() => this.setState({
|
||||
tagSelectorLoading: false,
|
||||
}))
|
||||
}
|
||||
|
||||
removeTag(tag) {
|
||||
this.setState({
|
||||
tagSelectorLoading: true,
|
||||
});
|
||||
|
||||
API.call({
|
||||
path: '/ticket/remove-tag',
|
||||
data: {
|
||||
ticketNumber: this.props.ticket.ticketNumber,
|
||||
tagId: tag
|
||||
}
|
||||
}).then(this.onTicketModification.bind(this))
|
||||
}).then(() => {
|
||||
this.setState({
|
||||
tagSelectorLoading: false,
|
||||
});
|
||||
|
||||
this.onTicketModification();
|
||||
}).catch(() => this.setState({
|
||||
tagSelectorLoading: false,
|
||||
}))
|
||||
}
|
||||
|
||||
onCustomResponsesChanged({index}) {
|
||||
|
|
|
@ -265,4 +265,4 @@ let DemoPage = React.createClass({
|
|||
}
|
||||
});
|
||||
|
||||
export default DemoPage;
|
||||
export default DemoPage;
|
|
@ -0,0 +1,500 @@
|
|||
// LIBS
|
||||
const _ = require('lodash');
|
||||
|
||||
// MOCKS
|
||||
const Tag = ReactMock();
|
||||
const DropDown = ReactMock();
|
||||
|
||||
// COMPONENT
|
||||
const Autocomplete = requireUnit('core-components/autocomplete', {
|
||||
'core-components/drop-down': DropDown,
|
||||
'core-components/tag': Tag,
|
||||
});
|
||||
|
||||
|
||||
const color = [
|
||||
'red',
|
||||
'cyan',
|
||||
'blue',
|
||||
'green',
|
||||
];
|
||||
|
||||
let countries = ["Afghanistan","Åland Islands","Albania","Algeria","American Samoa","AndorrA","Angola","Anguilla","Antarctica","Antigua and Barbuda","Argentina","Armenia","Aruba","Australia","Austria","Azerbaijan","Bahamas","Bahrain","Bangladesh","Barbados","Belarus","Belgium","Belize","Benin","Bermuda","Bhutan","Bolivia","Bosnia and Herzegovina","Botswana","Bouvet Island","Brazil","British Indian Ocean Territory","Brunei Darussalam","Bulgaria","Burkina Faso","Burundi","Cambodia","Cameroon","Canada","Cape Verde","Cayman Islands","Central African Republic","Chad","Chile","China","Christmas Island","Cocos (Keeling) Islands","Colombia","Comoros","Congo","Congo, The Democratic Republic of the","Cook Islands","Costa Rica","Cote D'Ivoire","Croatia","Cuba","Cyprus","Czech Republic","Denmark","Djibouti","Dominica","Dominican Republic","Ecuador","Egypt","El Salvador","Equatorial Guinea","Eritrea","Estonia","Ethiopia","Falkland Islands (Malvinas)","Faroe Islands","Fiji","Finland","France","French Guiana","French Polynesia","French Southern Territories","Gabon","Gambia","Georgia","Germany","Ghana","Gibraltar","Greece","Greenland","Grenada","Guadeloupe","Guam","Guatemala","Guernsey","Guinea","Guinea-Bissau","Guyana","Haiti","Heard Island and Mcdonald Islands","Holy See (Vatican City State)","Honduras","Hong Kong","Hungary","Iceland","India","Indonesia","Iran, Islamic Republic Of","Iraq","Ireland","Isle of Man","Israel","Italy","Jamaica","Japan","Jersey","Jordan","Kazakhstan","Kenya","Kiribati","Korea, Democratic People'S Republic of","Korea, Republic of","Kuwait","Kyrgyzstan","Lao People'S Democratic Republic","Latvia","Lebanon","Lesotho","Liberia","Libyan Arab Jamahiriya","Liechtenstein","Lithuania","Luxembourg","Macao","Macedonia, The Former Yugoslav Republic of","Madagascar","Malawi","Malaysia","Maldives","Mali","Malta","Marshall Islands","Martinique","Mauritania","Mauritius","Mayotte","Mexico","Micronesia, Federated States of","Moldova, Republic of","Monaco","Mongolia","Montserrat","Morocco","Mozambique","Myanmar","Namibia","Nauru","Nepal","Netherlands","Netherlands Antilles","New Caledonia","New Zealand","Nicaragua","Niger","Nigeria","Niue","Norfolk Island","Northern Mariana Islands","Norway","Oman","Pakistan","Palau","Palestinian Territory, Occupied","Panama","Papua New Guinea","Paraguay","Peru","Philippines","Pitcairn","Poland","Portugal","Puerto Rico","Qatar","Reunion","Romania","Russian Federation","RWANDA","Saint Helena","Saint Kitts and Nevis","Saint Lucia","Saint Pierre and Miquelon","Saint Vincent and the Grenadines","Samoa","San Marino","Sao Tome and Principe","Saudi Arabia","Senegal","Serbia and Montenegro","Seychelles","Sierra Leone","Singapore","Slovakia","Slovenia","Solomon Islands","Somalia","South Africa","South Georgia and the South Sandwich Islands","Spain","Sri Lanka","Sudan","Suriname","Svalbard and Jan Mayen","Swaziland","Sweden","Switzerland","Syrian Arab Republic","Taiwan, Province of China","Tajikistan","Tanzania, United Republic of","Thailand","Timor-Leste","Togo","Tokelau","Tonga","Trinidad and Tobago","Tunisia","Turkey","Turkmenistan","Turks and Caicos Islands","Tuvalu","Uganda","Ukraine","United Arab Emirates","United Kingdom","United States","United States Minor Outlying Islands","Uruguay","Uzbekistan","Vanuatu","Venezuela","Viet Nam","Virgin Islands, British","Virgin Islands, U.S.","Wallis and Futuna","Western Sahara","Yemen","Zambia","Zimbabwe"];
|
||||
countries = countries.map((name, index) => {
|
||||
return {
|
||||
id: index,
|
||||
name: name.toLowerCase(),
|
||||
content: name,
|
||||
color: color[_.random(0, color.length-1)],
|
||||
}
|
||||
});
|
||||
|
||||
const timeout = function (f, t) {
|
||||
return new Promise(function (res) {
|
||||
setTimeout(function() {
|
||||
res(f());
|
||||
},t);
|
||||
});
|
||||
};
|
||||
|
||||
const searchApi = spy((query, blacklist = []) => {
|
||||
const data = countries.filter(x => !_.includes(blacklist, x.id));
|
||||
|
||||
return new Promise((res,rej) => {
|
||||
setTimeout(function () {
|
||||
const result = data.filter(item => _.includes(item.name, query));
|
||||
res(result.slice(0, 10));
|
||||
}, query == 'brazilq' ? 100 : 50);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Autocomplete component with external api', function () {
|
||||
let selectedList = [], autocompleteInput, autocompleteDropdown, autocompleteWithExternalApi, tag;
|
||||
function renderAutocomplete(props) {
|
||||
selectedList = [];
|
||||
|
||||
autocompleteWithExternalApi = TestUtils.renderIntoDocument(
|
||||
<Autocomplete
|
||||
getItemListFromQuery={searchApi}
|
||||
onChange={selectedListAutocomplete => selectedList = selectedListAutocomplete} />
|
||||
);
|
||||
|
||||
autocompleteInput = TestUtils.scryRenderedDOMComponentsWithClass(autocompleteWithExternalApi, 'autocomplete__input')[0];
|
||||
autocompleteDropdown = TestUtils.scryRenderedComponentsWithType(autocompleteWithExternalApi, DropDown)[0];
|
||||
}
|
||||
|
||||
describe('writing in input', function() {
|
||||
beforeEach(function() {
|
||||
renderAutocomplete();
|
||||
});
|
||||
|
||||
it('should open menu with list', function() {
|
||||
expect(searchApi).to.have.been.calledWith("", selectedList.map(item => item.id));
|
||||
expect(selectedList.length).to.equal(0);
|
||||
|
||||
autocompleteInput.value = "ho";
|
||||
TestUtils.Simulate.change(autocompleteInput);
|
||||
|
||||
expect(autocompleteDropdown.props.opened).to.equal(true);
|
||||
expect(autocompleteDropdown.props.loading).to.equal(true);
|
||||
expect(selectedList.length).to.equal(0);
|
||||
});
|
||||
|
||||
it('should select item if enter is pressed', function() {
|
||||
expect(searchApi).to.have.been.calledWith("", selectedList.map(item => item.id));
|
||||
expect(selectedList.length).to.equal(0);
|
||||
|
||||
autocompleteInput.value = "argentina";
|
||||
TestUtils.Simulate.change(autocompleteInput);
|
||||
|
||||
expect(autocompleteDropdown.props.opened).to.equal(true);
|
||||
expect(autocompleteDropdown.props.loading).to.equal(true);
|
||||
|
||||
return timeout(function() {
|
||||
expect(autocompleteDropdown.props.loading).to.equal(false);
|
||||
expect(searchApi).to.have.been.calledWith("argentina", selectedList.map(item => item.id));
|
||||
expect(autocompleteDropdown.props.items.length).to.equal(1);
|
||||
expect(autocompleteDropdown.props.items[0].name).to.equal("argentina");
|
||||
expect(autocompleteDropdown.props.items[0].id).to.equal(10);
|
||||
expect(selectedList.length).to.equal(0);
|
||||
|
||||
autocompleteDropdown.props.onChange({index: 0});
|
||||
|
||||
expect(autocompleteDropdown.props.opened).to.equal(false);
|
||||
expect(searchApi).to.have.been.calledWith("", selectedList.map(item => item.id));
|
||||
expect(selectedList.length).to.equal(1);
|
||||
expect(selectedList[0].name).to.equal("argentina");
|
||||
expect(selectedList[0].id).to.equal(10);
|
||||
}, 360);
|
||||
});
|
||||
|
||||
it('should sinc', function() {
|
||||
expect(searchApi).to.have.been.calledWith("", selectedList.map(item => item.id));
|
||||
expect(selectedList.length).to.equal(0);
|
||||
|
||||
autocompleteInput.value = "brazilq";
|
||||
TestUtils.Simulate.change(autocompleteInput);
|
||||
|
||||
expect(autocompleteDropdown.props.opened).to.equal(true);
|
||||
expect(autocompleteDropdown.props.loading).to.equal(true);
|
||||
|
||||
return timeout(function() {
|
||||
expect(autocompleteDropdown.props.loading).to.equal(true);
|
||||
expect(searchApi).to.have.not.been.calledWith("brazil", selectedList.map(item => item.id));
|
||||
expect(selectedList.length).to.equal(0);
|
||||
|
||||
autocompleteInput.value = "brazil";
|
||||
TestUtils.Simulate.change(autocompleteInput);
|
||||
|
||||
expect(autocompleteDropdown.props.opened).to.equal(true);
|
||||
expect(autocompleteDropdown.props.loading).to.equal(true);
|
||||
|
||||
return timeout(function() {
|
||||
expect(autocompleteDropdown.props.loading).to.equal(false);
|
||||
expect(searchApi).to.have.been.calledWith("brazil", selectedList.map(item => item.id));
|
||||
expect(autocompleteDropdown.props.items.length).to.equal(1);
|
||||
expect(autocompleteDropdown.props.items[0].name).to.equal("brazil");
|
||||
expect(autocompleteDropdown.props.items[0].id).to.equal(30);
|
||||
expect(selectedList.length).to.equal(0);
|
||||
|
||||
autocompleteDropdown.props.onChange({index: 0});
|
||||
|
||||
expect(autocompleteDropdown.props.opened).to.equal(false);
|
||||
expect(searchApi).to.have.been.calledWith("", selectedList.map(item => item.id));
|
||||
expect(autocompleteDropdown.props.opened).to.equal(false);
|
||||
expect(selectedList.length).to.equal(1);
|
||||
expect(selectedList[0].name).to.equal("brazil");
|
||||
expect(selectedList[0].id).to.equal(30);
|
||||
|
||||
autocompleteDropdown.props.onMenuToggle(true);
|
||||
|
||||
expect(autocompleteDropdown.props.opened).to.equal(true);
|
||||
expect(autocompleteDropdown.props.items.length).to.equal(0);
|
||||
}, 360);
|
||||
}, 25);
|
||||
});
|
||||
|
||||
it('should delete item if backspace is pressed and input value is ""', function() {
|
||||
expect(searchApi).to.have.been.calledWith("", selectedList.map(item => item.id));
|
||||
expect(selectedList.length).to.equal(0);
|
||||
|
||||
autocompleteInput.value = "ang";
|
||||
TestUtils.Simulate.change(autocompleteInput);
|
||||
|
||||
expect(autocompleteDropdown.props.opened).to.equal(true);
|
||||
expect(autocompleteDropdown.props.loading).to.equal(true);
|
||||
|
||||
return timeout(function() {
|
||||
expect(autocompleteDropdown.props.loading).to.equal(false);
|
||||
expect(searchApi).to.have.been.calledWith("ang", selectedList.map(item => item.id));
|
||||
expect(autocompleteDropdown.props.items.length).to.equal(3);
|
||||
expect(autocompleteDropdown.props.items[0].name).to.equal("angola");
|
||||
expect(autocompleteDropdown.props.items[0].id).to.equal(6);
|
||||
expect(selectedList.length).to.equal(0);
|
||||
|
||||
autocompleteDropdown.props.onChange({index: 0});
|
||||
|
||||
expect(autocompleteDropdown.props.opened).to.equal(false);
|
||||
expect(searchApi).to.have.been.calledWith("", selectedList.map(item => item.id));
|
||||
expect(selectedList.length).to.equal(1);
|
||||
expect(selectedList[0].name).to.equal("angola");
|
||||
expect(selectedList[0].id).to.equal(6);
|
||||
|
||||
autocompleteInput.value = "ang";
|
||||
TestUtils.Simulate.change(autocompleteInput);
|
||||
|
||||
expect(autocompleteDropdown.props.opened).to.equal(true);
|
||||
expect(autocompleteDropdown.props.loading).to.equal(true);
|
||||
|
||||
return timeout(function() {
|
||||
expect(autocompleteDropdown.props.loading).to.equal(false);
|
||||
expect(searchApi).to.have.been.calledWith("ang", selectedList.map(item => item.id));
|
||||
expect(autocompleteDropdown.props.items.length).to.equal(2);
|
||||
expect(autocompleteDropdown.props.items[0].name).to.equal("anguilla");
|
||||
expect(autocompleteDropdown.props.items[0].id).to.equal(7);
|
||||
expect(selectedList.length).to.equal(1);
|
||||
|
||||
autocompleteDropdown.props.onChange({index: 0});
|
||||
|
||||
expect(autocompleteDropdown.props.opened).to.equal(false);
|
||||
expect(searchApi).to.have.been.calledWith("", selectedList.map(item => item.id));
|
||||
expect(selectedList.length).to.equal(2);
|
||||
expect(selectedList[0].name).to.equal("angola");
|
||||
expect(selectedList[0].id).to.equal(6);
|
||||
expect(selectedList[1].name).to.equal("anguilla");
|
||||
expect(selectedList[1].id).to.equal(7);
|
||||
expect(autocompleteDropdown.props.items.length).to.equal(1);
|
||||
expect(autocompleteDropdown.props.items[0].name).to.equal("bangladesh");
|
||||
expect(autocompleteDropdown.props.items[0].id).to.equal(18);
|
||||
|
||||
TestUtils.Simulate.keyDown(autocompleteInput, {key: 'backspace', keyCode: 8, which: 8});
|
||||
|
||||
expect(searchApi).to.have.been.calledWith("", selectedList.map(item => item.id));
|
||||
expect(selectedList.length).to.equal(1);
|
||||
expect(selectedList[0].name).to.equal("angola");
|
||||
expect(selectedList[0].id).to.equal(6);
|
||||
|
||||
autocompleteInput.value = "ang";
|
||||
TestUtils.Simulate.change(autocompleteInput);
|
||||
|
||||
expect(autocompleteDropdown.props.opened).to.equal(true);
|
||||
expect(autocompleteDropdown.props.loading).to.equal(true);
|
||||
|
||||
return timeout(function() {
|
||||
expect(autocompleteDropdown.props.loading).to.equal(false);
|
||||
expect(searchApi).to.have.been.calledWith("ang", selectedList.map(item => item.id));
|
||||
expect(autocompleteDropdown.props.items.length).to.equal(2);
|
||||
expect(autocompleteDropdown.props.items[0].name).to.equal("anguilla");
|
||||
expect(autocompleteDropdown.props.items[0].id).to.equal(7);
|
||||
expect(selectedList.length).to.equal(1);
|
||||
}, 360);
|
||||
},360);
|
||||
}, 360);
|
||||
});
|
||||
|
||||
it("should delete item if click is pressed", function() {
|
||||
expect(searchApi).to.have.been.calledWith("", selectedList.map(item => item.id));
|
||||
expect(selectedList.length).to.equal(0);
|
||||
|
||||
autocompleteInput.value = "ang";
|
||||
TestUtils.Simulate.change(autocompleteInput);
|
||||
|
||||
expect(autocompleteDropdown.props.opened).to.equal(true);
|
||||
expect(autocompleteDropdown.props.loading).to.equal(true);
|
||||
|
||||
return timeout(function () {
|
||||
expect(autocompleteDropdown.props.loading).to.equal(false);
|
||||
expect(searchApi).to.have.been.calledWith("ang", selectedList.map(item => item.id));
|
||||
expect(autocompleteDropdown.props.items.length).to.equal(3);
|
||||
expect(selectedList.length).to.equal(0);
|
||||
|
||||
autocompleteDropdown.props.onChange({index: 0});
|
||||
|
||||
expect(autocompleteDropdown.props.opened).to.equal(false);
|
||||
expect(searchApi).to.have.been.calledWith("", selectedList.map(item => item.id));
|
||||
expect(selectedList.length).to.equal(1);
|
||||
expect(selectedList[0].name).to.equal("angola");
|
||||
expect(selectedList[0].id).to.equal(6);
|
||||
|
||||
tag = TestUtils.scryRenderedComponentsWithType(autocompleteWithExternalApi, Tag)[0];
|
||||
tag.props.onRemoveClick({ preventDefault: stub() });
|
||||
|
||||
expect(selectedList.length).to.equal(0);
|
||||
|
||||
autocompleteInput.value = "ang";
|
||||
TestUtils.Simulate.change(autocompleteInput);
|
||||
|
||||
expect(autocompleteDropdown.props.opened).to.equal(true);
|
||||
expect(autocompleteDropdown.props.loading).to.equal(true);
|
||||
|
||||
return timeout(function () {
|
||||
expect(autocompleteDropdown.props.loading).to.equal(false);
|
||||
expect(searchApi).to.have.been.calledWith("ang", selectedList.map(item => item.id));
|
||||
expect(autocompleteDropdown.props.items.length).to.equal(3);
|
||||
expect(autocompleteDropdown.props.items[0].name).to.equal("angola");
|
||||
expect(autocompleteDropdown.props.items[0].id).to.equal(6);
|
||||
expect(selectedList.length).to.equal(0);
|
||||
}, 360);
|
||||
}, 360);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Autocomplete component with items', function() {
|
||||
let selectedList = [], autocompleteInput, autocompleteDropdown, itemsList, autocomplete;
|
||||
function renderAutocomplete(props) {
|
||||
selectedList = [];
|
||||
itemsList = [
|
||||
{id: 45, name: 'lautaro', content: 'Lautaro.', color: 'gray'},
|
||||
{id: 46, name: 'dsafa', content: 'dsafa', color: 'black'},
|
||||
{id: 47, name: 'asdasdasd', content: 'asdasdasd.', color: 'red'},
|
||||
{id: 48, name: '123123123', content: '123123123.', color: 'blue'},
|
||||
{id: 49, name: 'hola', content: 'hola', color: 'green'},
|
||||
];
|
||||
|
||||
autocomplete = TestUtils.renderIntoDocument(
|
||||
<Autocomplete
|
||||
items={itemsList}
|
||||
onChange={selectedListAutocomplete => selectedList = selectedListAutocomplete} />
|
||||
);
|
||||
|
||||
autocompleteInput = TestUtils.scryRenderedDOMComponentsWithClass(autocomplete, 'autocomplete__input')[0];
|
||||
autocompleteDropdown = TestUtils.scryRenderedComponentsWithType(autocomplete, DropDown)[0];
|
||||
}
|
||||
|
||||
beforeEach(function() {
|
||||
renderAutocomplete();
|
||||
});
|
||||
|
||||
it('should open menu with list', function() {
|
||||
expect(selectedList.length).to.equal(0);
|
||||
|
||||
autocompleteInput.value = "la";
|
||||
TestUtils.Simulate.change(autocompleteInput);
|
||||
|
||||
expect(autocompleteDropdown.props.opened).to.equal(true);
|
||||
expect(autocompleteDropdown.props.items.length).to.equal(2);
|
||||
});
|
||||
|
||||
it('should select item if enter is pressed', function() {
|
||||
expect(selectedList.length).to.equal(0);
|
||||
|
||||
autocompleteInput.value = "la";
|
||||
TestUtils.Simulate.change(autocompleteInput);
|
||||
|
||||
expect(autocompleteDropdown.props.opened).to.equal(true);
|
||||
expect(autocompleteDropdown.props.items.length).to.equal(2);
|
||||
expect(autocompleteDropdown.props.items[0].name).to.equal("lautaro");
|
||||
expect(autocompleteDropdown.props.items[0].id).to.equal(45);
|
||||
expect(autocompleteDropdown.props.items[1].name).to.equal("hola");
|
||||
expect(autocompleteDropdown.props.items[1].id).to.equal(49);
|
||||
|
||||
autocompleteDropdown.props.onChange({index: 0});
|
||||
|
||||
expect(autocompleteDropdown.props.opened).to.equal(false);
|
||||
expect(selectedList.length).to.equal(1);
|
||||
expect(selectedList[0].name).to.equal("lautaro");
|
||||
expect(selectedList[0].id).to.equal(45);
|
||||
|
||||
autocompleteInput.value = "";
|
||||
TestUtils.Simulate.change(autocompleteInput);
|
||||
|
||||
expect(autocompleteDropdown.props.opened).to.equal(true);
|
||||
expect(autocompleteDropdown.props.items.length).to.equal(4);
|
||||
expect(autocompleteDropdown.props.items[0].name).to.equal("dsafa");
|
||||
expect(autocompleteDropdown.props.items[0].id).to.equal(46);
|
||||
expect(autocompleteDropdown.props.items[1].name).to.equal("asdasdasd");
|
||||
expect(autocompleteDropdown.props.items[1].id).to.equal(47);
|
||||
expect(autocompleteDropdown.props.items[2].name).to.equal("123123123");
|
||||
expect(autocompleteDropdown.props.items[2].id).to.equal(48);
|
||||
expect(autocompleteDropdown.props.items[3].name).to.equal("hola");
|
||||
expect(autocompleteDropdown.props.items[3].id).to.equal(49);
|
||||
|
||||
autocompleteDropdown.props.onChange({index: 2});
|
||||
|
||||
expect(autocompleteDropdown.props.opened).to.equal(false);
|
||||
expect(selectedList.length).to.equal(2);
|
||||
expect(selectedList[0].name).to.equal("lautaro");
|
||||
expect(selectedList[0].id).to.equal(45);
|
||||
expect(selectedList[1].name).to.equal("123123123");
|
||||
expect(selectedList[1].id).to.equal(48);
|
||||
|
||||
autocompleteInput.value = "";
|
||||
TestUtils.Simulate.change(autocompleteInput);
|
||||
|
||||
expect(autocompleteDropdown.props.opened).to.equal(true);
|
||||
expect(autocompleteDropdown.props.items.length).to.equal(3);
|
||||
expect(autocompleteDropdown.props.items[0].name).to.equal("dsafa");
|
||||
expect(autocompleteDropdown.props.items[0].id).to.equal(46);
|
||||
expect(autocompleteDropdown.props.items[1].name).to.equal("asdasdasd");
|
||||
expect(autocompleteDropdown.props.items[1].id).to.equal(47);
|
||||
expect(autocompleteDropdown.props.items[2].name).to.equal("hola");
|
||||
expect(autocompleteDropdown.props.items[2].id).to.equal(49);
|
||||
|
||||
autocompleteInput.value = "lau";
|
||||
TestUtils.Simulate.change(autocompleteInput);
|
||||
|
||||
expect(autocompleteDropdown.props.opened).to.equal(true);
|
||||
expect(autocompleteDropdown.props.items.length).to.equal(0);
|
||||
|
||||
autocompleteInput.value = "la";
|
||||
TestUtils.Simulate.change(autocompleteInput);
|
||||
|
||||
expect(autocompleteDropdown.props.opened).to.equal(true);
|
||||
expect(autocompleteDropdown.props.items.length).to.equal(1);
|
||||
expect(autocompleteDropdown.props.items[0].name).to.equal("hola");
|
||||
expect(autocompleteDropdown.props.items[0].id).to.equal(49);
|
||||
|
||||
autocompleteDropdown.props.onChange({index: 0});
|
||||
|
||||
expect(autocompleteDropdown.props.opened).to.equal(false);
|
||||
expect(selectedList.length).to.equal(3);
|
||||
expect(selectedList[0].name).to.equal("lautaro");
|
||||
expect(selectedList[0].id).to.equal(45);
|
||||
expect(selectedList[1].name).to.equal("123123123");
|
||||
expect(selectedList[1].id).to.equal(48);
|
||||
expect(selectedList[2].name).to.equal("hola");
|
||||
expect(selectedList[2].id).to.equal(49);
|
||||
});
|
||||
|
||||
it('should delete item if (backspace or click) is pressed and input value is "" ', function() {
|
||||
expect(selectedList.length).to.equal(0);
|
||||
|
||||
autocompleteInput.value = "123";
|
||||
TestUtils.Simulate.change(autocompleteInput);
|
||||
|
||||
expect(autocompleteDropdown.props.opened).to.equal(true);
|
||||
expect(autocompleteDropdown.props.items.length).to.equal(1);
|
||||
expect(autocompleteDropdown.props.items[0].name).to.equal("123123123");
|
||||
expect(autocompleteDropdown.props.items[0].id).to.equal(48);
|
||||
|
||||
autocompleteDropdown.props.onChange({index: 0});
|
||||
|
||||
expect(autocompleteDropdown.props.opened).to.equal(false);
|
||||
expect(selectedList.length).to.equal(1);
|
||||
expect(selectedList[0].name).to.equal("123123123");
|
||||
expect(selectedList[0].id).to.equal(48);
|
||||
|
||||
autocompleteInput.value = "la";
|
||||
TestUtils.Simulate.change(autocompleteInput);
|
||||
|
||||
expect(autocompleteDropdown.props.opened).to.equal(true);
|
||||
expect(autocompleteDropdown.props.items.length).to.equal(2);
|
||||
expect(autocompleteDropdown.props.items[0].name).to.equal("lautaro");
|
||||
expect(autocompleteDropdown.props.items[0].id).to.equal(45);
|
||||
expect(autocompleteDropdown.props.items[1].name).to.equal("hola");
|
||||
expect(autocompleteDropdown.props.items[1].id).to.equal(49);
|
||||
|
||||
autocompleteDropdown.props.onChange({index: 1});
|
||||
|
||||
expect(autocompleteDropdown.props.opened).to.equal(false);
|
||||
expect(selectedList.length).to.equal(2);
|
||||
expect(selectedList[0].name).to.equal("123123123");
|
||||
expect(selectedList[0].id).to.equal(48);
|
||||
expect(selectedList[1].name).to.equal("hola");
|
||||
expect(selectedList[1].id).to.equal(49);
|
||||
|
||||
autocompleteInput.value = "l";
|
||||
TestUtils.Simulate.change(autocompleteInput);
|
||||
|
||||
expect(autocompleteDropdown.props.opened).to.equal(true);
|
||||
expect(autocompleteDropdown.props.items.length).to.equal(1);
|
||||
expect(autocompleteDropdown.props.items[0].name).to.equal("lautaro");
|
||||
expect(autocompleteDropdown.props.items[0].id).to.equal(45);
|
||||
|
||||
autocompleteDropdown.props.onChange({index: 0});
|
||||
|
||||
expect(autocompleteDropdown.props.opened).to.equal(false);
|
||||
expect(selectedList.length).to.equal(3);
|
||||
expect(selectedList[0].name).to.equal("123123123");
|
||||
expect(selectedList[0].id).to.equal(48);
|
||||
expect(selectedList[1].name).to.equal("hola");
|
||||
expect(selectedList[1].id).to.equal(49);
|
||||
expect(selectedList[2].name).to.equal("lautaro");
|
||||
expect(selectedList[2].id).to.equal(45);
|
||||
|
||||
TestUtils.Simulate.keyDown(autocompleteInput, {key: 'backspace', keyCode: 8, which: 8});
|
||||
|
||||
expect(selectedList.length).to.equal(2);
|
||||
|
||||
autocompleteInput.value = "asd";
|
||||
TestUtils.Simulate.change(autocompleteInput);
|
||||
TestUtils.Simulate.keyDown(autocompleteInput, {key: 'backspace', keyCode: 8, which: 8});
|
||||
|
||||
expect(selectedList.length).to.equal(2);
|
||||
expect(selectedList[0].name).to.equal("123123123");
|
||||
expect(selectedList[0].id).to.equal(48);
|
||||
expect(selectedList[1].name).to.equal("hola");
|
||||
expect(selectedList[1].id).to.equal(49);
|
||||
|
||||
tag = TestUtils.scryRenderedComponentsWithType(autocomplete, Tag)[0];
|
||||
tag.props.onRemoveClick({ preventDefault: stub() });
|
||||
|
||||
expect(selectedList.length).to.equal(1);
|
||||
expect(selectedList[0].name).to.equal("hola");
|
||||
expect(selectedList[0].id).to.equal(49);
|
||||
|
||||
autocompleteInput.value = "a";
|
||||
TestUtils.Simulate.change(autocompleteInput);
|
||||
TestUtils.Simulate.keyDown(autocompleteInput, {key: 'backspace', keyCode: 8, which: 8});
|
||||
TestUtils.Simulate.keyDown(autocompleteInput, {key: 'backspace', keyCode: 8, which: 8});
|
||||
TestUtils.Simulate.keyDown(autocompleteInput, {key: 'backspace', keyCode: 8, which: 8});
|
||||
TestUtils.Simulate.keyDown(autocompleteInput, {key: 'backspace', keyCode: 8, which: 8});
|
||||
TestUtils.Simulate.keyDown(autocompleteInput, {key: 'backspace', keyCode: 8, which: 8});
|
||||
|
||||
expect(selectedList.length).to.equal(1);
|
||||
|
||||
autocompleteInput.value = "";
|
||||
TestUtils.Simulate.change(autocompleteInput);
|
||||
TestUtils.Simulate.keyDown(autocompleteInput, {key: 'backspace', keyCode: 8, which: 8});
|
||||
|
||||
expect(selectedList.length).to.equal(0);
|
||||
|
||||
TestUtils.Simulate.keyDown(autocompleteInput, {key: 'backspace', keyCode: 8, which: 8});
|
||||
TestUtils.Simulate.keyDown(autocompleteInput, {key: 'backspace', keyCode: 8, which: 8});
|
||||
TestUtils.Simulate.keyDown(autocompleteInput, {key: 'backspace', keyCode: 8, which: 8});
|
||||
TestUtils.Simulate.keyDown(autocompleteInput, {key: 'backspace', keyCode: 8, which: 8});
|
||||
|
||||
expect(selectedList.length).to.equal(0);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,269 @@
|
|||
import React from 'react';
|
||||
import _ from 'lodash';
|
||||
import keyCode from 'keycode';
|
||||
|
||||
import DropDown from 'core-components/drop-down';
|
||||
import Tag from 'core-components/tag';
|
||||
|
||||
const ItemsSchema = React.PropTypes.arrayOf(React.PropTypes.shape({
|
||||
id: React.PropTypes.number,
|
||||
name: React.PropTypes.string,
|
||||
content: React.PropTypes.node,
|
||||
color: React.PropTypes.string,
|
||||
}));
|
||||
|
||||
class Autocomplete extends React.Component {
|
||||
|
||||
static propTypes = {
|
||||
items: ItemsSchema,
|
||||
onChange: React.PropTypes.func,
|
||||
values: ItemsSchema,
|
||||
onRemoveClick: React.PropTypes.func,
|
||||
onTagSelected: React.PropTypes.func,
|
||||
getItemListFromQuery: React.PropTypes.func,
|
||||
disabled: React.PropTypes.bool,
|
||||
};
|
||||
|
||||
id = 1;
|
||||
|
||||
state = {
|
||||
selectedItems: [],
|
||||
inputValue: "",
|
||||
opened: false,
|
||||
highlightedIndex: 0,
|
||||
itemsFromQuery: [],
|
||||
loading: false,
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
const { getItemListFromQuery, } = this.props;
|
||||
|
||||
this.setTimeout = _.throttle((query) => {
|
||||
let id = ++this.id;
|
||||
|
||||
getItemListFromQuery(query, this.getSelectedItems().map(item => item.id))
|
||||
.then(result => {
|
||||
if(id === this.id)
|
||||
this.setState({
|
||||
itemsFromQuery: result,
|
||||
loading: false,
|
||||
});
|
||||
})
|
||||
.catch(() => this.setState({
|
||||
loading: false,
|
||||
}));
|
||||
}, 300, {leading: false});
|
||||
|
||||
this.searchApi("");
|
||||
}
|
||||
|
||||
render() {
|
||||
let inputWidth = 0;
|
||||
|
||||
if(this.span) {
|
||||
this.span.style.display = 'inline';
|
||||
this.span.textContent = this.state.inputValue;
|
||||
inputWidth = Math.ceil(this.span.getBoundingClientRect().width)-31;
|
||||
this.span.style.display = 'none';
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="autocomplete">
|
||||
<label className="autocomplete__label" onClick={(e) => e.stopPropagation()}>
|
||||
<DropDown
|
||||
className="autocomplete__drop-down"
|
||||
items={this.getDropdownList()}
|
||||
size="large"
|
||||
onChange={e => this.onChangeDropDown(e)}
|
||||
onMenuToggle={e => this.onMenuToggle(e)}
|
||||
opened={this.state.opened}
|
||||
onHighlightedIndexChange={n => this.onHighlightedIndexChange(n)}
|
||||
highlightedIndex={this.state.highlightedIndex}
|
||||
loading={this.state.loading}
|
||||
>
|
||||
{this.renderSelectedItems()}
|
||||
<input
|
||||
className="autocomplete__input"
|
||||
id="query"
|
||||
ref={input => this.input = input}
|
||||
value={this.state.inputValue}
|
||||
onKeyDown={e => this.onKeyDown(e)}
|
||||
onChange={e => this.onChangeInput(e.target.value)}
|
||||
style={this.span ? {width: inputWidth} : {}} />
|
||||
<span className="sizer" ref={span => this.span = span} />
|
||||
</DropDown>
|
||||
</label>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderSelectedItems() {
|
||||
return this.getSelectedItems().map(item => this.renderSelectedItem(item));
|
||||
}
|
||||
|
||||
renderSelectedItem(item) {
|
||||
return <Tag
|
||||
name={item.name}
|
||||
color={item.color}
|
||||
showDeleteButton
|
||||
onRemoveClick={this.onRemoveClick.bind(this,item.id)}
|
||||
key={item.id} />
|
||||
}
|
||||
|
||||
getDropdownList() {
|
||||
const {
|
||||
items,
|
||||
} = this.props;
|
||||
let dropdownList = [];
|
||||
|
||||
if(items !== undefined) {
|
||||
const list = this.getUnselectedList(items, this.getSelectedItems());
|
||||
|
||||
dropdownList = list.filter(s => _.includes(s.name, this.state.inputValue));
|
||||
} else {
|
||||
dropdownList = this.getUnselectedList(this.state.itemsFromQuery, this.getSelectedItems());
|
||||
}
|
||||
|
||||
return dropdownList;
|
||||
}
|
||||
|
||||
getUnselectedList(list, selectedList) {
|
||||
|
||||
return list.filter(item => !_.some(selectedList, item));
|
||||
}
|
||||
|
||||
getSelectedItems() {
|
||||
const { values, } = this.props;
|
||||
|
||||
return (values !== undefined) ? values : this.state.selectedItems;
|
||||
}
|
||||
|
||||
onRemoveClick(itemId, event) {
|
||||
const {
|
||||
onChange,
|
||||
onRemoveClick,
|
||||
} = this.props;
|
||||
const newList = this.getSelectedItems().filter(item => item.id != itemId);
|
||||
event.preventDefault();
|
||||
|
||||
this.setState({
|
||||
selectedItems: newList,
|
||||
opened: false,
|
||||
highlightedIndex: 0,
|
||||
});
|
||||
|
||||
onChange && onChange(newList);
|
||||
onRemoveClick && onRemoveClick(itemId);
|
||||
this.searchApi("", newList);
|
||||
}
|
||||
|
||||
onChangeDropDown(e) {
|
||||
const {
|
||||
onChange,
|
||||
onTagSelected,
|
||||
} = this.props;
|
||||
|
||||
if(this.getDropdownList().length) {
|
||||
const itemSelected = this.getDropdownList()[e.index];
|
||||
const newList = [...this.getSelectedItems(), itemSelected];
|
||||
|
||||
this.setState({
|
||||
selectedItems: newList,
|
||||
inputValue: "",
|
||||
highlightedIndex: 0,
|
||||
opened: false,
|
||||
});
|
||||
|
||||
onChange && onChange(newList);
|
||||
onTagSelected && onTagSelected(itemSelected.id);
|
||||
this.searchApi("", newList);
|
||||
}
|
||||
}
|
||||
|
||||
onChangeInput(str) {
|
||||
const { getItemListFromQuery, } = this.props;
|
||||
|
||||
this.setState({
|
||||
inputValue: str,
|
||||
opened: true,
|
||||
highlightedIndex: 0,
|
||||
});
|
||||
|
||||
if(getItemListFromQuery !== undefined) {
|
||||
this.setState({
|
||||
loading: true,
|
||||
});
|
||||
|
||||
this.setTimeout(str);
|
||||
}
|
||||
}
|
||||
|
||||
onMenuToggle(e) {
|
||||
this.setState({
|
||||
opened: e,
|
||||
});
|
||||
}
|
||||
|
||||
onHighlightedIndexChange(index) {
|
||||
this.setState({
|
||||
highlightedIndex: index,
|
||||
});
|
||||
}
|
||||
|
||||
onKeyDown(event) {
|
||||
const {
|
||||
onChange,
|
||||
onRemoveClick,
|
||||
} = this.props;
|
||||
|
||||
if(this.props.disabled) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if(keyCode(event) === "space") {
|
||||
event.stopPropagation();
|
||||
}
|
||||
|
||||
if(keyCode(event) === "backspace" && this.state.inputValue === "") {
|
||||
const lastSelectedItemsIndex = this.getSelectedItems().length-1;
|
||||
const newList = this.getSelectedItems().slice(0, lastSelectedItemsIndex);
|
||||
this.setState({
|
||||
selectedItems: newList,
|
||||
highlightedIndex: 0,
|
||||
});
|
||||
onChange && onChange(newList);
|
||||
|
||||
if(this.getSelectedItems().length) {
|
||||
const itemId = this.getSelectedItems()[lastSelectedItemsIndex].id;
|
||||
|
||||
onRemoveClick && onRemoveClick(itemId);
|
||||
}
|
||||
|
||||
this.searchApi("", newList);
|
||||
}
|
||||
}
|
||||
|
||||
searchApi(query, blacklist=this.getSelectedItems()) {
|
||||
const { getItemListFromQuery, } = this.props;
|
||||
|
||||
if(getItemListFromQuery !== undefined) {
|
||||
getItemListFromQuery(query, blacklist.map(item => item.id))
|
||||
.then(result => {
|
||||
this.setState({
|
||||
itemsFromQuery: result,
|
||||
loading: false,
|
||||
});
|
||||
})
|
||||
.catch(() => {
|
||||
this.setState({
|
||||
loading: false,
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default Autocomplete;
|
|
@ -0,0 +1,42 @@
|
|||
@import "../scss/vars";
|
||||
|
||||
.autocomplete {
|
||||
margin-bottom: 30px;
|
||||
text-align: left;
|
||||
|
||||
&__drop-down {
|
||||
.drop-down__current-item {
|
||||
cursor: text;
|
||||
background-color: $very-light-grey;
|
||||
border: 1px solid $grey;
|
||||
min-height: 38px;
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
border-color: $primary-blue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__label {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
&__input {
|
||||
display: inline-block;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
outline: none;
|
||||
padding-left: 5px;
|
||||
width: 10px;
|
||||
max-width: 100%;
|
||||
min-width: 10px;
|
||||
}
|
||||
|
||||
.sizer {
|
||||
line-height: 1.21428571em;
|
||||
padding: .67857143em 2.1em .67857143em 1em;
|
||||
display: none;
|
||||
white-space: pre;
|
||||
}
|
||||
}
|
|
@ -2,10 +2,11 @@ import React from 'react';
|
|||
import _ from 'lodash';
|
||||
import classNames from 'classnames';
|
||||
import {Motion, spring} from 'react-motion';
|
||||
import keyCode from 'keycode';
|
||||
import keyCode from 'keycode';
|
||||
|
||||
import Menu from 'core-components/menu';
|
||||
import Icon from 'core-components/icon';
|
||||
import Loading from 'core-components/loading'
|
||||
|
||||
class DropDown extends React.Component {
|
||||
|
||||
|
@ -14,7 +15,11 @@ class DropDown extends React.Component {
|
|||
selectedIndex: React.PropTypes.number,
|
||||
items: Menu.propTypes.items,
|
||||
onChange: React.PropTypes.func,
|
||||
size: React.PropTypes.oneOf(['small', 'medium', 'large'])
|
||||
size: React.PropTypes.oneOf(['small', 'medium', 'large']),
|
||||
highlightedIndex: React.PropTypes.number,
|
||||
onHighlightedIndexChange: React.PropTypes.func,
|
||||
opened: React.PropTypes.bool,
|
||||
onMenuToggle: React.PropTypes.func,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
|
@ -28,7 +33,7 @@ class DropDown extends React.Component {
|
|||
menuId: _.uniqueId('drop-down-menu_'),
|
||||
selectedIndex: props.selectedIndex || props.defaultSelectedIndex,
|
||||
highlightedIndex: props.selectedIndex || props.defaultSelectedIndex,
|
||||
opened: false
|
||||
opened: false,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -44,7 +49,7 @@ class DropDown extends React.Component {
|
|||
|
||||
return {
|
||||
defaultStyle: {opacity: 0, translateY: 20},
|
||||
style: (this.state.opened) ? openedStyle : closedStyle
|
||||
style: (this.getOpen()) ? openedStyle : closedStyle
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -56,19 +61,29 @@ class DropDown extends React.Component {
|
|||
<div {...this.getCurrentItemProps()}>
|
||||
{this.props.children ? this.props.children : this.renderCurrentItem()}
|
||||
</div>
|
||||
<Motion defaultStyle={animation.defaultStyle} style={animation.style} onRest={this.onAnimationFinished.bind(this)}>
|
||||
{this.renderList.bind(this)}
|
||||
<Motion
|
||||
defaultStyle={animation.defaultStyle}
|
||||
style={animation.style}
|
||||
onRest={this.onAnimationFinished.bind(this)}>
|
||||
{this.renderList.bind(this)}
|
||||
</Motion>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderList({opacity, translateY}) {
|
||||
let style = { opacity: opacity, transform: `translateY(${translateY}px)`};
|
||||
let style = {
|
||||
opacity: opacity,
|
||||
transform: `translateY(${translateY}px)`
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="drop-down__list-container" style={style}>
|
||||
<Menu {...this.getMenuProps()} />
|
||||
{this.props.loading ?
|
||||
<div><Loading className='drop-down__loading' /></div> :
|
||||
this.props.items.length ?
|
||||
<Menu {...this.getMenuProps()} /> :
|
||||
<div className='drop-down__empty-list'>Empty</div>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -77,7 +92,6 @@ class DropDown extends React.Component {
|
|||
let item = this.props.items[this.getSelectedIndex()];
|
||||
let iconNode = null;
|
||||
|
||||
|
||||
if (item.icon) {
|
||||
iconNode = <Icon className="drop-down__current-item-icon" name={item.icon} />;
|
||||
}
|
||||
|
@ -90,12 +104,17 @@ class DropDown extends React.Component {
|
|||
}
|
||||
|
||||
getClass() {
|
||||
const {
|
||||
className,
|
||||
size,
|
||||
} = this.props;
|
||||
|
||||
let classes = {
|
||||
'drop-down': true,
|
||||
'drop-down_closed': !this.state.opened,
|
||||
'drop-down_closed': !this.getOpen(),
|
||||
|
||||
['drop-down_' + this.props.size]: (this.props.size),
|
||||
[this.props.className]: (this.props.className)
|
||||
['drop-down_' + size]: (size),
|
||||
[className]: (className)
|
||||
};
|
||||
|
||||
return classNames(classes);
|
||||
|
@ -103,10 +122,10 @@ class DropDown extends React.Component {
|
|||
|
||||
getCurrentItemProps() {
|
||||
return {
|
||||
'aria-expanded': this.state.opened,
|
||||
'aria-expanded': this.getOpen(),
|
||||
'aria-autocomplete': 'list',
|
||||
'aria-owns': this.state.menuId,
|
||||
'aria-activedescendant': this.state.menuId + '__' + this.state.highlightedIndex,
|
||||
'aria-activedescendant': this.state.menuId + '__' + this.getHighlightedIndex(),
|
||||
className: 'drop-down__current-item',
|
||||
onClick: this.handleClick.bind(this),
|
||||
onKeyDown: this.onKeyDown.bind(this),
|
||||
|
@ -123,7 +142,7 @@ class DropDown extends React.Component {
|
|||
items: this.props.items,
|
||||
onItemClick: this.handleItemClick.bind(this),
|
||||
onMouseDown: this.handleListMouseDown.bind(this),
|
||||
selectedIndex: this.state.highlightedIndex,
|
||||
selectedIndex: this.getHighlightedIndex(),
|
||||
role: 'listbox'
|
||||
};
|
||||
}
|
||||
|
@ -138,26 +157,37 @@ class DropDown extends React.Component {
|
|||
}
|
||||
|
||||
getKeyActions(event) {
|
||||
const {highlightedIndex, opened} = this.state;
|
||||
const highlightedIndex = this.getHighlightedIndex();
|
||||
const opened = this.getOpen();
|
||||
const itemsQuantity = this.props.items.length;
|
||||
const {
|
||||
onHighlightedIndexChange,
|
||||
onMenuToggle,
|
||||
} = this.props;
|
||||
|
||||
return {
|
||||
'up': () => {
|
||||
if (opened) {
|
||||
const newHighlightedIndex = this.modulo(highlightedIndex - 1, itemsQuantity);
|
||||
event.preventDefault();
|
||||
|
||||
this.setState({
|
||||
highlightedIndex: this.modulo(highlightedIndex - 1, itemsQuantity)
|
||||
highlightedIndex: newHighlightedIndex,
|
||||
});
|
||||
|
||||
onHighlightedIndexChange && onHighlightedIndexChange(newHighlightedIndex);
|
||||
}
|
||||
},
|
||||
'down': () => {
|
||||
if (opened) {
|
||||
const newHighlightedIndex = this.modulo(highlightedIndex + 1, itemsQuantity);
|
||||
event.preventDefault();
|
||||
|
||||
this.setState({
|
||||
highlightedIndex: this.modulo(highlightedIndex + 1, itemsQuantity)
|
||||
highlightedIndex: newHighlightedIndex,
|
||||
});
|
||||
|
||||
onHighlightedIndexChange && onHighlightedIndexChange(newHighlightedIndex);
|
||||
}
|
||||
},
|
||||
'enter': () => {
|
||||
|
@ -167,6 +197,8 @@ class DropDown extends React.Component {
|
|||
this.setState({
|
||||
opened: true
|
||||
});
|
||||
|
||||
onMenuToggle && onMenuToggle(true);
|
||||
}
|
||||
},
|
||||
'space': () => {
|
||||
|
@ -175,32 +207,44 @@ class DropDown extends React.Component {
|
|||
this.setState({
|
||||
opened: true
|
||||
});
|
||||
|
||||
onMenuToggle && onMenuToggle(true);
|
||||
},
|
||||
'esc': () => {
|
||||
this.setState({
|
||||
opened: false
|
||||
});
|
||||
|
||||
onMenuToggle && onMenuToggle(false);
|
||||
},
|
||||
'tab': () => {
|
||||
if (this.state.opened) {
|
||||
if (this.getOpen()) {
|
||||
event.preventDefault();
|
||||
|
||||
this.onIndexSelected(highlightedIndex)
|
||||
onHighlightedIndexChange && this.onIndexSelected(highlightedIndex);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
handleBlur() {
|
||||
const { onMenuToggle, } = this.props;
|
||||
|
||||
this.setState({
|
||||
opened: false
|
||||
});
|
||||
|
||||
onMenuToggle && onMenuToggle(false);
|
||||
}
|
||||
|
||||
handleClick() {
|
||||
const { onMenuToggle, } = this.props;
|
||||
|
||||
this.setState({
|
||||
opened: !this.state.opened
|
||||
opened: !this.getOpen()
|
||||
});
|
||||
|
||||
onMenuToggle && onMenuToggle(!this.getOpen());
|
||||
}
|
||||
|
||||
handleItemClick(index) {
|
||||
|
@ -208,16 +252,25 @@ class DropDown extends React.Component {
|
|||
}
|
||||
|
||||
onIndexSelected(index) {
|
||||
this.setState({
|
||||
opened: false,
|
||||
selectedIndex: index,
|
||||
highlightedIndex: index
|
||||
});
|
||||
const {
|
||||
onMenuToggle,
|
||||
onHighlightedIndexChange,
|
||||
onChange,
|
||||
loading,
|
||||
} = this.props;
|
||||
|
||||
if (this.props.onChange) {
|
||||
this.props.onChange({
|
||||
index
|
||||
if (!loading){
|
||||
this.setState({
|
||||
opened: false,
|
||||
selectedIndex: index,
|
||||
highlightedIndex: index
|
||||
});
|
||||
|
||||
onHighlightedIndexChange && onHighlightedIndexChange(index);
|
||||
|
||||
onMenuToggle && onMenuToggle(false);
|
||||
|
||||
onChange && onChange({ index });
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -226,20 +279,37 @@ class DropDown extends React.Component {
|
|||
}
|
||||
|
||||
onAnimationFinished() {
|
||||
if (!this.state.opened && this.state.highlightedIndex !== this.getSelectedIndex()) {
|
||||
const { onHighlightedIndexChange, } = this.props;
|
||||
|
||||
|
||||
if (!this.getOpen() && this.getHighlightedIndex() !== this.getSelectedIndex()) {
|
||||
this.setState({
|
||||
highlightedIndex: this.getSelectedIndex()
|
||||
highlightedIndex: this.getSelectedIndex(),
|
||||
});
|
||||
|
||||
onHighlightedIndexChange && onHighlightedIndexChange(this.getSelectedIndex());
|
||||
}
|
||||
}
|
||||
|
||||
getSelectedIndex() {
|
||||
return (this.props.selectedIndex !== undefined) ? this.props.selectedIndex : this.state.selectedIndex;
|
||||
const { selectedIndex, } = this.props;
|
||||
return (selectedIndex !== undefined) ? selectedIndex : this.state.selectedIndex;
|
||||
}
|
||||
|
||||
modulo(number, mod) {
|
||||
return ((number % mod) + mod) % mod;
|
||||
}
|
||||
|
||||
getOpen(){
|
||||
const { opened, } = this.props;
|
||||
return (opened !== undefined) ? opened : this.state.opened;
|
||||
}
|
||||
|
||||
getHighlightedIndex() {
|
||||
const { highlightedIndex, } = this.props;
|
||||
return (highlightedIndex !== undefined) ? highlightedIndex : this.state.highlightedIndex;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default DropDown;
|
||||
|
|
|
@ -28,6 +28,25 @@
|
|||
position: absolute;
|
||||
width: 150px;
|
||||
z-index: 100;
|
||||
background-color: white;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
&__loading {
|
||||
padding-top: 12.5%;
|
||||
|
||||
.loading__icon {
|
||||
border-color: rgba(150, 150, 150, 0.2);
|
||||
border-left-color: $primary-red;
|
||||
}
|
||||
}
|
||||
|
||||
&__empty-list {
|
||||
padding: 10px;
|
||||
background-color: white;
|
||||
color: $dark-grey;
|
||||
font-style: italic;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
&_closed {
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
import React from 'react';
|
||||
import _ from 'lodash';
|
||||
import Icon from 'core-components/icon';
|
||||
import DropDown from 'core-components/drop-down';
|
||||
import Tag from 'core-components/tag';
|
||||
import Autocomplete from 'core-components/autocomplete';
|
||||
|
||||
class TagSelector extends React.Component {
|
||||
|
||||
|
@ -13,55 +11,34 @@ class TagSelector extends React.Component {
|
|||
})),
|
||||
values: React.PropTypes.arrayOf(React.PropTypes.string),
|
||||
onRemoveClick: React.PropTypes.func,
|
||||
onTagSelected: React.PropTypes.func,
|
||||
loading: React.PropTypes.bool,
|
||||
};
|
||||
|
||||
render() {
|
||||
const items = this.props.items.map(tag => ({...tag, content: this.renderTagOption(tag)}));
|
||||
const values = items.filter(item => _.includes(this.props.values, item.name));
|
||||
|
||||
return (
|
||||
<div className="tag-selector">
|
||||
<DropDown className="tag-selector__drop-down" items={this.renderTagOptions().map(tag => ({content: tag}))} selectedIndex={-1} size="large">
|
||||
{this.renderSelectedTags()}
|
||||
</DropDown>
|
||||
<Autocomplete
|
||||
items={items}
|
||||
values={values}
|
||||
onRemoveClick={this.props.onRemoveClick}
|
||||
onTagSelected={this.props.onTagSelected}
|
||||
disabled={this.props.loading} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderSelectedTags() {
|
||||
const itemList = this.props.values.map(value => _.find(this.props.items, {name:value}));
|
||||
|
||||
return itemList.map(this.renderSelectedTag.bind(this));
|
||||
}
|
||||
|
||||
|
||||
renderSelectedTag(item,index) {
|
||||
return <Tag name={item.name} color={item.color} showDeleteButton onRemoveClick={this.onRemoveClick.bind(this,item.id)} key={index}/>;
|
||||
|
||||
}
|
||||
|
||||
renderTagOptions() {
|
||||
const itemList = _.filter(this.props.items,(item) => !_.includes(this.props.values,item.name));
|
||||
|
||||
return itemList.map(this.renderTagOption.bind(this));
|
||||
}
|
||||
|
||||
renderTagOption(item,index) {
|
||||
renderTagOption(item) {
|
||||
return (
|
||||
<div onClick={this.onTagSelected.bind(this,item.id)} className="tag-selector__tag-option" key={index}>
|
||||
<div className="tag-selector__tag-option" key={`tag-option-${item.id}`}>
|
||||
<span className="tag-selector__tag-option-square" style={{backgroundColor:item.color}}/>
|
||||
<span className="tag-selector__tag-option-name" >{item.name}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
onRemoveClick(tagId) {
|
||||
if(this.props.onRemoveClick){
|
||||
this.props.onRemoveClick(tagId);
|
||||
}
|
||||
}
|
||||
|
||||
onTagSelected(tagId) {
|
||||
if(this.props.onTagSelected){
|
||||
this.props.onTagSelected(tagId);
|
||||
}
|
||||
}
|
||||
}
|
||||
export default TagSelector;
|
||||
|
|
|
@ -9,7 +9,7 @@ export default {
|
|||
'REPEAT_PASSWORD': 'Repetir Contraseña',
|
||||
'LOG_IN': 'Iniciar sesión',
|
||||
'SIGN_UP': 'Registrarse',
|
||||
'FORGOT_PASSWORD': 'Olvidaste tu contraseña?',
|
||||
'FORGOT_PASSWORD': '¿Olvidó su contraseña?',
|
||||
'RECOVER_PASSWORD': 'Recuperar Contraseña',
|
||||
'SET_UP_PASSWORD': 'Configurar su contraseña',
|
||||
'RECOVER_SENT': 'Se ha enviado un email con instrucciones de recuperación.',
|
||||
|
@ -35,7 +35,7 @@ export default {
|
|||
'NO_ATTACHMENT': 'No hay archivo adjunto',
|
||||
'STAFF': 'Staff',
|
||||
'CUSTOMER': 'Cliente',
|
||||
'YES': 'Si',
|
||||
'YES': 'Sí',
|
||||
'NO': 'No',
|
||||
'CANCEL': 'Cancelar',
|
||||
'MY_ACCOUNT': 'Mi Cuenta',
|
||||
|
@ -81,7 +81,7 @@ export default {
|
|||
'ASSIGN_TO_ME': 'Asignar',
|
||||
'UN_ASSIGN': 'Desasignar',
|
||||
'VIEW_TICKET': 'Ver Ticket',
|
||||
'VIEW_TICKET_DESCRIPTION': 'Revisa el estado de tu ticket usando el número de ticket y tu email.',
|
||||
'VIEW_TICKET_DESCRIPTION': 'Revise el estado de su ticket usando el número de ticket y su email.',
|
||||
'SELECT_CUSTOM_RESPONSE': 'Seleccionar una respuesta personalizada...',
|
||||
'WARNING': 'Advertencia',
|
||||
'INFO': 'Información',
|
||||
|
@ -131,7 +131,7 @@ export default {
|
|||
'UPDATE_PASSWORD': 'Actualizar contraseña',
|
||||
'UPDATE_LEVEL': 'Actualizar nivel',
|
||||
'UPDATE_DEPARTMENTS': 'Actializar departamentos',
|
||||
'EDIT_STAFF': 'Edit staff member',
|
||||
'EDIT_STAFF': 'Editar miembro del staff',
|
||||
'ADD_DEPARTMENT': 'Añadir departamento',
|
||||
'UPDATE_DEPARTMENT': 'Actualizar Departamento',
|
||||
'TRANSFER_TICKETS_TO': 'Transferir tickets a',
|
||||
|
@ -165,7 +165,7 @@ export default {
|
|||
'ALLOWED_LANGUAGES': 'Idiomas Permitidos',
|
||||
'ALLOWED_LANGUAGES_INFO': 'Los idiomas permitidos son los idiomas que puede utilizar un usuario.',
|
||||
'SETTINGS_UPDATED': 'Se han actualizado las configuraciones',
|
||||
'ON': 'Si',
|
||||
'ON': 'Sí',
|
||||
'OFF': 'No',
|
||||
'BOXED': 'Boxed',
|
||||
'FULL_WIDTH': 'Ancho Total',
|
||||
|
@ -179,8 +179,8 @@ export default {
|
|||
'INCLUDE_USERS_VIA_CSV': 'Incluir usuarios por archivo CSV',
|
||||
'BACKUP_DATABASE': 'Backup database',
|
||||
'DELETE_ALL_USERS': 'Borrar todos los usuarios',
|
||||
'PLEASE_CONFIRM_PASSWORD': 'Por favor confirma tu contraseña para hacer estos cambios',
|
||||
'REGISTRATION_API_KEYS': 'Registration API keys',
|
||||
'PLEASE_CONFIRM_PASSWORD': 'Por favor, confirme su contraseña para hacer estos cambios',
|
||||
'REGISTRATION_API_KEYS': 'Registro de API keys',
|
||||
'NAME_OF_KEY': 'Nombre de la clave',
|
||||
'KEY': 'Clave',
|
||||
'ADD_API_KEY': 'Añadir nueva clave',
|
||||
|
@ -215,7 +215,7 @@ export default {
|
|||
'OPTION': 'Opción {index}',
|
||||
'OPTIONS': 'Opciones',
|
||||
'FIELD_DESCRIPTION': 'Descripción del campo (opcional)',
|
||||
'DESCRIPTION_ADD_CUSTOM_TAG': 'Aquí puedes agregar una nueva etiqueta personalizada',
|
||||
'DESCRIPTION_ADD_CUSTOM_TAG': 'Aquí puede agregar una nueva etiqueta personalizada',
|
||||
'DESCRIPTION_EDIT_CUSTOM_TAG': 'aquí se puede editar una etiqueta personalizada',
|
||||
'CUSTOM_FIELDS': 'Campos Personalizados',
|
||||
|
||||
|
@ -234,12 +234,12 @@ export default {
|
|||
'ACTIVITY_RE_OPEN': 'reabrió el ticket',
|
||||
'ACTIVITY_DEPARTMENT_CHANGED': 'cambió el departamento del ticket',
|
||||
'ACTIVITY_PRIORITY_CHANGED': 'cambió la prioridad del ticket',
|
||||
'ACTIVITY_EDIT_COMMENT': 'editado un comentario del billete',
|
||||
'ACTIVITY_EDIT_COMMENT': 'editó un comentario del ticket',
|
||||
|
||||
'ACTIVITY_EDIT_SETTINGS': 'editó las preferencias',
|
||||
'ACTIVITY_SIGNUP': 'se registró',
|
||||
'ACTIVITY_EDIT_TITLE': 'editó el titulo del ticket',
|
||||
'ACTIVITY_ADD_TOPIC': 'añadio el tema',
|
||||
'ACTIVITY_ADD_TOPIC': 'añadió el tema',
|
||||
'ACTIVITY_ADD_ARTICLE': 'añadió el articulo',
|
||||
'ACTIVITY_DELETE_TOPIC': 'eliminó el tema',
|
||||
'ACTIVITY_DELETE_ARTICLE': 'eliminó el artículo',
|
||||
|
@ -276,13 +276,13 @@ export default {
|
|||
'ADMIN_NAME': 'Nombre de la cuenta admin',
|
||||
'ADMIN_EMAIL': 'Email de la cuenta admin',
|
||||
'ADMIN_PASSWORD': 'Contraseña de la cuenta admin',
|
||||
'ADMIN_PASSWORD_DESCRIPTION': 'Por favor recuerde esta contraseña. Es necesaria para acceder al panel de administración. Puedes cambiarla después.',
|
||||
'ADMIN_PASSWORD_DESCRIPTION': 'Por favor, recuerde esta contraseña. Es necesaria para acceder al panel de administración. Puedes cambiarla después.',
|
||||
'INSTALLATION_COMPLETED': 'Instalación Completada.',
|
||||
'INSTALLATION_COMPLETED_DESCRIPTION': 'La instalación de OpenSupports esta completada. Redirigiendo al panel de administración...',
|
||||
'INSTALLATION_COMPLETED_DESCRIPTION': 'La instalación de OpenSupports ha finalizado. Redirigiendo al panel de administración...',
|
||||
|
||||
'STEP_TITLE': 'Paso {current} de {total} - {title}',
|
||||
'STEP_1_DESCRIPTION': 'Seleccione su idioma preferido para el asistente de instalación.',
|
||||
'STEP_2_DESCRIPTION': 'Aqui estan listados los requisitos para OpenSupports. Por favor, asegúrese de cumplir con todos los requisitos.',
|
||||
'STEP_2_DESCRIPTION': 'Aqui están listados los requisitos para OpenSupports. Por favor, asegúrese de cumplir con todos los requisitos.',
|
||||
'STEP_3_DESCRIPTION': 'Por favor, complete la configuracion de la base de datos MySQL.',
|
||||
'STEP_4_DESCRIPTION': 'Por favor, seleccione sus preferencias de sistema de usuarios.',
|
||||
'STEP_5_DESCRIPTION': 'Pro favor, seleccione sus preferencias generales del sistema.',
|
||||
|
@ -292,29 +292,29 @@ export default {
|
|||
//VIEW DESCRIPTIONS
|
||||
'CREATE_TICKET_DESCRIPTION': 'Este es un formulario para crear tickets. Rellene el formulario y envíenos sus dudas. Nuestro sistema de soporte responderá lo antes posible.',
|
||||
'TICKET_LIST_DESCRIPTION': 'Aquí puede encontrar una lista de todos los tickets que ha enviado a nuestro equipo de soporte.',
|
||||
'TICKETS_DESCRIPTION': 'Envíe un ticket a través de nuestro centro de soporte y obtenga respuesta de sus dudas, sugerencias y problemas.',
|
||||
'ARTICLES_DESCRIPTION': 'Echa un vistazo a nuestros artículos sobre temas comunes, guías y documentación.',
|
||||
'TICKETS_DESCRIPTION': 'Envíe un ticket a través de nuestro centro de soporte y obtenga respuesta de sus dudas, sugerencias o problemas.',
|
||||
'ARTICLES_DESCRIPTION': 'Eche un vistazo a nuestros artículos sobre temas comunes, guías y documentación.',
|
||||
'ACCOUNT_DESCRIPTION': 'Todos sus tickets están almacenados en el perfil de su cuenta. Mantenga un registro de todos los tickets envíados a nuestro equipo de soporte.',
|
||||
'SUPPORT_CENTER_DESCRIPTION': 'Bienvenido a nuestro centro de soporte. Puede ponerse en contacto con nosotros a través de un sistema de tickets. Sus tickets serán contestados por nuestro personal.',
|
||||
'CUSTOM_RESPONSES_DESCRIPTION': 'Las respuestas personalizadas son respuestas automatizadas para problemas comunes.',
|
||||
'CUSTOM_TAGS_DESCRIPTION': 'Aquí puede ver administrar las etiquetas personalizadas de los tickets para identificarlos mejor',
|
||||
'MY_TICKETS_DESCRIPTION': 'Aquí puedes ver los tickets que tienes asignado.',
|
||||
'NEW_TICKETS_DESCRIPTION': 'Aquí puedes ver todos los tickets nuevos que no están asignados por nadie.',
|
||||
'ALL_TICKETS_DESCRIPTION': 'Aquí puede ver los tickets de los departamentos a cuales estas asignado.',
|
||||
'TICKET_VIEW_DESCRIPTION': 'Este ticket ha sido enviado por un cliente. Aquí puede responderlo o asignarselo.',
|
||||
'BAN_USERS_DESCRIPTION': 'Aquí puedes ver la lista de email bloqueados. Puedes des-bloquear o agregar más emails a la lista.',
|
||||
'LIST_USERS_DESCRIPTION': 'Esta es la lista de usuarios que estan registrados en el sistema. Puedes buscar a alguien en particular, bloquearlo o borralo.',
|
||||
'CUSTOM_TAGS_DESCRIPTION': 'Aquí puede ver y administrar las etiquetas personalizadas de los tickets para identificarlos mejor',
|
||||
'MY_TICKETS_DESCRIPTION': 'Aquí puede ver los tickets que tienes asignado.',
|
||||
'NEW_TICKETS_DESCRIPTION': 'Aquí puede ver todos los tickets nuevos que no están asignados a nadie.',
|
||||
'ALL_TICKETS_DESCRIPTION': 'Aquí puede ver los tickets de los departamentos a cuales está asignado.',
|
||||
'TICKET_VIEW_DESCRIPTION': 'Este ticket ha sido enviado por un cliente. Aquí puede responderlo o asignárselo.',
|
||||
'BAN_USERS_DESCRIPTION': 'Aquí puede ver la lista de email bloqueados. Puede desbloquear o agregar más emails a la lista.',
|
||||
'LIST_USERS_DESCRIPTION': 'Esta es la lista de usuarios que están registrados en el sistema. Puede buscar a alguien en particular, bloquearlo o borralo.',
|
||||
'USER_VIEW_DESCRIPTION': 'Aquí puede encontrar toda la información sobre un usuario y todos los tickets enviados por el usuario. También puede eliminarlo o bloquearlo.',
|
||||
'DELETE_USER_DESCRIPTION': 'El usuario no podrá iniciar sesión y todas sus tickets se borrarán. Además, el correo electrónico ya no puede utilizarse.',
|
||||
'DELETE_USER_DESCRIPTION': 'El usuario no podrá iniciar sesión y todos sus tickets se borrarán. Además, el correo electrónico ya no puede utilizarse.',
|
||||
'DELETE_TOPIC_DESCRIPTION': 'Al borrar el tema, todos los artículos en él se borrarán.',
|
||||
'SEARCH_TICKETS_DESCRIPTION': 'Aquí puede buscar tickets por filtros específicos',
|
||||
'EDIT_TOPIC_DESCRIPTION': 'Aquí puede cambiar el nombre, el icono y el color del icono del tema.',
|
||||
'ADD_ARTICLE_DESCRIPTION': 'Aquí puede agregar un artículo que estará disponible para cada usuario. Se agregará dentro de la categoría {category}.',
|
||||
'LIST_ARTICLES_DESCRIPTION': 'Esta es una lista de artículos que incluye información sobre nuestros servicios.',
|
||||
'ADD_TOPIC_DESCRIPTION': 'Aquí puede agregar un tema que funciona como una categoría para los artículos.',
|
||||
'DELETE_ARTICLE_DESCRIPTION': 'Vas a borrar este artículo para siempre.',
|
||||
'STAFF_MEMBERS_DESCRIPTION': 'Aquí puedes ver quienes son los miembros de staff.',
|
||||
'ADD_STAFF_DESCRIPTION': 'Usa este formulario para agregar miembros de staff.',
|
||||
'DELETE_ARTICLE_DESCRIPTION': 'Va a borrar este artículo para siempre.',
|
||||
'STAFF_MEMBERS_DESCRIPTION': 'Aquí puede ver quiénes son los miembros de staff.',
|
||||
'ADD_STAFF_DESCRIPTION': 'Use este formulario para agregar miembros de staff.',
|
||||
'EDIT_STAFF_DESCRIPTION': 'Aquí puede editar información sobre un miembro de staff.',
|
||||
'MY_ACCOUNT_DESCRIPTION': 'Aquí puede editar información acerca de usted.',
|
||||
'DEPARTMENTS_DESCRIPTION': 'Un departamento es un grupo donde los tickets pueden ir. Se utilizan para categorizar los tickets. Puede asignarlos a otros miembros de staff.',
|
||||
|
@ -331,12 +331,12 @@ export default {
|
|||
'REGISTRATION_ENABLED': 'Se ha habilitado el registro',
|
||||
'ADD_API_KEY_DESCRIPTION': 'Inserte en nombre y una Registration API Key será generada.',
|
||||
'SIGN_UP_VIEW_DESCRIPTION': 'Aquí puede crear una cuenta para nuestro centro de soporte. Es necesario para enviar tickets y ver la documentación.',
|
||||
'EDIT_PROFILE_VIEW_DESCRIPTION': 'Aquí puedes editar tu usuario cambiando tu correo electrónico o tu contraseña.',
|
||||
'ENABLE_USER_SYSTEM_DESCRIPTION': 'Habilitar/Deshabilitar el uso de un sistema de usuario. Si lo deshabilitas, todos los usuarios serán eliminados pero los tickets serán guardados. Si lo habilitas, se crearán los usuarios de los tickets existentes.',
|
||||
'EDIT_PROFILE_VIEW_DESCRIPTION': 'Aquí puede editar su usuario cambiando el correo electrónico o la contraseña.',
|
||||
'ENABLE_USER_SYSTEM_DESCRIPTION': 'Habilitar/Deshabilitar el uso de un sistema de usuario. Si lo deshabilita, todos los usuarios serán eliminados pero los tickets serán guardados. Si lo habilita, se crearán los usuarios de los tickets existentes.',
|
||||
'CSV_DESCRIPTION': 'El archivo CSV debe tener 3 columnas: correo electrónico, contraseña, nombre. No hay límite en el recuento de filas. Se creará un usuario por fila en el archivo.',
|
||||
'SMTP_SERVER_DESCRIPTION': 'La configuracion de SMTP permite que la applicacion mande emails. Si no es configurado, ningún mail sera enviado OpenSupports.',
|
||||
'IMAP_SERVER_DESCRIPTION': 'La configuración del servidor IMAP permite que la aplicación cree tickets de los correos electrónicos enviados a un buzón.',
|
||||
'ENABLE_USER_DESCRIPTION': 'Esta acción permite al usuario iniciar sesión y crear tickets..',
|
||||
'ENABLE_USER_DESCRIPTION': 'Esta acción permite al usuario iniciar sesión y crear tickets.',
|
||||
'DISABLE_USER_DESCRIPTION': 'El usuario estará deshabilitado y no podrá iniciar sesión y crear tickets.',
|
||||
'PRIVATE_RESPONSE_DESCRIPTION': 'Esta respuesta solo será vista por los miembros del personal.',
|
||||
'PRIVATE_TOPIC_DESCRIPTION': 'Este tema solo será visto por los miembros del personal.',
|
||||
|
@ -410,7 +410,7 @@ export default {
|
|||
'SUCCESSFUL_CONNECTION': 'Conexión exitosa',
|
||||
'UNSUCCESSFUL_CONNECTION': 'Conexión fallida',
|
||||
'SERVER_CREDENTIALS_WORKING': 'Las credenciales del servidor están funcionando correctamente',
|
||||
'DELETE_CUSTOM_FIELD_SURE': 'Algunos usuarios pueden estar usando este campo. ¿Seguro que quieres borrarlo?',
|
||||
'DELETE_CUSTOM_FIELD_SURE': 'Algunos usuarios pueden estar usando este campo. ¿Seguro que quiere borrarlo?',
|
||||
|
||||
'COMMENT_EDITED': '(Comentario editado)',
|
||||
'TITLE_EDITED': '(Título editado)',
|
||||
|
@ -428,7 +428,7 @@ export default {
|
|||
'ACTIVITY_RE_OPEN_THIS': 'reabrió este ticket',
|
||||
'ACTIVITY_DEPARTMENT_CHANGED_THIS': 'cambió el departamento de este ticket a ',
|
||||
'ACTIVITY_PRIORITY_CHANGED_THIS': 'cambió la prioridad de este ticket a ',
|
||||
'DATE_PREFIX': 'el dia',
|
||||
'DATE_PREFIX': 'el día',
|
||||
'LEFT_EMPTY_DATABASE': 'Dejar vacío para la creación automática de bases de datos',
|
||||
'REMEMBER_ME': 'Recordarme',
|
||||
'EMAIL_LOWERCASE': 'email',
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
"respect/validation": "^1.1",
|
||||
"phpmailer/phpmailer": "^5.2",
|
||||
"google/recaptcha": "~1.1",
|
||||
"gabordemooij/redbean": "^4.3",
|
||||
"gabordemooij/redbean": "^5.4",
|
||||
"ifsnop/mysqldump-php": "2.*",
|
||||
"ezyang/htmlpurifier": "^4.8",
|
||||
"codeguy/upload": "^1.3",
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
use RedBeanPHP\Facade as RedBean;
|
||||
use Respect\Validation\Validator as DataValidator;
|
||||
DataValidator::with('CustomValidations', true);
|
||||
|
||||
|
@ -52,6 +53,7 @@ class InitSettingsController extends Controller {
|
|||
|
||||
public function handler() {
|
||||
if (Setting::isTableEmpty()) {
|
||||
RedBean::exec(file_get_contents('data/db_schema.sql'));
|
||||
$this->storeGlobalSettings();
|
||||
$this->storeMailTemplates();
|
||||
$this->storeLanguages();
|
||||
|
|
|
@ -0,0 +1,473 @@
|
|||
-- MySQL dump 10.13 Distrib 5.6.46, for Linux (x86_64)
|
||||
-- ------------------------------------------------------
|
||||
-- Server version 5.6.46
|
||||
|
||||
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
|
||||
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
|
||||
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
|
||||
/*!40101 SET NAMES utf8 */;
|
||||
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
|
||||
/*!40103 SET TIME_ZONE='+00:00' */;
|
||||
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
|
||||
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
|
||||
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
|
||||
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
|
||||
|
||||
--
|
||||
-- Table structure for table `apikey`
|
||||
--
|
||||
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE IF NOT EXISTS `apikey` (
|
||||
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`type` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`name` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`token` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `article`
|
||||
--
|
||||
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE IF NOT EXISTS `article` (
|
||||
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`title` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`content` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`last_edited` double DEFAULT NULL,
|
||||
`position` tinyint(1) unsigned DEFAULT NULL,
|
||||
`topic_id` int(11) unsigned DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `index_foreignkey_article_topic` (`topic_id`),
|
||||
CONSTRAINT `c_fk_article_topic_id` FOREIGN KEY (`topic_id`) REFERENCES `topic` (`id`) ON DELETE SET NULL ON UPDATE SET NULL
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `ban`
|
||||
--
|
||||
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE IF NOT EXISTS `ban` (
|
||||
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`email` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `customfield`
|
||||
--
|
||||
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE IF NOT EXISTS `customfield` (
|
||||
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`name` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`description` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`type` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `customfieldoption`
|
||||
--
|
||||
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE IF NOT EXISTS `customfieldoption` (
|
||||
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`name` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`customfield_id` int(11) unsigned DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `index_foreignkey_customfieldoption_customfield` (`customfield_id`),
|
||||
CONSTRAINT `c_fk_customfieldoption_customfield_id` FOREIGN KEY (`customfield_id`) REFERENCES `customfield` (`id`) ON DELETE SET NULL ON UPDATE SET NULL
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `customfieldvalue`
|
||||
--
|
||||
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE IF NOT EXISTS `customfieldvalue` (
|
||||
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`value` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`user_id` int(11) unsigned DEFAULT NULL,
|
||||
`customfield_id` int(11) unsigned DEFAULT NULL,
|
||||
`customfieldoption_id` int(11) unsigned DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `index_foreignkey_customfieldvalue_user` (`user_id`),
|
||||
KEY `index_foreignkey_customfieldvalue_customfield` (`customfield_id`),
|
||||
KEY `index_foreignkey_customfieldvalue_customfieldoption` (`customfieldoption_id`),
|
||||
CONSTRAINT `c_fk_customfieldvalue_customfield_id` FOREIGN KEY (`customfield_id`) REFERENCES `customfield` (`id`) ON DELETE SET NULL ON UPDATE SET NULL,
|
||||
CONSTRAINT `c_fk_customfieldvalue_customfieldoption_id` FOREIGN KEY (`customfieldoption_id`) REFERENCES `customfieldoption` (`id`) ON DELETE SET NULL ON UPDATE SET NULL,
|
||||
CONSTRAINT `c_fk_customfieldvalue_user_id` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `customresponse`
|
||||
--
|
||||
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE IF NOT EXISTS `customresponse` (
|
||||
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`name` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`language` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`content` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `department`
|
||||
--
|
||||
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE IF NOT EXISTS `department` (
|
||||
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`owners` int(11) unsigned DEFAULT NULL,
|
||||
`name` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`private` int(11) unsigned DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `department_staff`
|
||||
--
|
||||
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE IF NOT EXISTS `department_staff` (
|
||||
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`department_id` int(11) unsigned DEFAULT NULL,
|
||||
`staff_id` int(11) unsigned DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `UQ_f2a19cacc73eb380315c104650ebc8c42d1b75a0` (`department_id`,`staff_id`),
|
||||
KEY `index_foreignkey_department_staff_department` (`department_id`),
|
||||
KEY `index_foreignkey_department_staff_staff` (`staff_id`),
|
||||
CONSTRAINT `c_fk_department_staff_department_id` FOREIGN KEY (`department_id`) REFERENCES `department` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
CONSTRAINT `c_fk_department_staff_staff_id` FOREIGN KEY (`staff_id`) REFERENCES `staff` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `language`
|
||||
--
|
||||
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE IF NOT EXISTS `language` (
|
||||
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`code` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`allowed` int(11) unsigned DEFAULT NULL,
|
||||
`supported` tinyint(1) unsigned DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `log`
|
||||
--
|
||||
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE IF NOT EXISTS `log` (
|
||||
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`type` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`to` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`date` double DEFAULT NULL,
|
||||
`author_name` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`author_staff_id` int(11) unsigned DEFAULT NULL,
|
||||
`author_user_id` int(11) unsigned DEFAULT NULL,
|
||||
`author_staff` tinyint(1) unsigned DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `index_foreignkey_log_author_staff` (`author_staff_id`),
|
||||
KEY `index_foreignkey_log_author_user` (`author_user_id`),
|
||||
CONSTRAINT `c_fk_log_author_staff_id` FOREIGN KEY (`author_staff_id`) REFERENCES `staff` (`id`) ON DELETE SET NULL ON UPDATE SET NULL,
|
||||
CONSTRAINT `c_fk_log_author_user_id` FOREIGN KEY (`author_user_id`) REFERENCES `user` (`id`) ON DELETE SET NULL ON UPDATE SET NULL
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `mailtemplate`
|
||||
--
|
||||
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE IF NOT EXISTS `mailtemplate` (
|
||||
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`template` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`subject` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`language` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`text1` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`text2` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`text3` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `recoverpassword`
|
||||
--
|
||||
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE IF NOT EXISTS `recoverpassword` (
|
||||
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`email` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`token` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`staff` tinyint(1) unsigned DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `sessioncookie`
|
||||
--
|
||||
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE IF NOT EXISTS `sessioncookie` (
|
||||
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`token` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`ip` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`creation_date` double DEFAULT NULL,
|
||||
`expiration_date` double DEFAULT NULL,
|
||||
`user_id` int(11) unsigned DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `index_foreignkey_sessioncookie_user` (`user_id`),
|
||||
CONSTRAINT `c_fk_sessioncookie_user_id` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE SET NULL ON UPDATE SET NULL
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `setting`
|
||||
--
|
||||
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE IF NOT EXISTS `setting` (
|
||||
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`name` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`value` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `staff`
|
||||
--
|
||||
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE IF NOT EXISTS `staff` (
|
||||
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`level` int(11) unsigned DEFAULT NULL,
|
||||
`send_email_on_new_ticket` int(11) unsigned DEFAULT NULL,
|
||||
`name` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`email` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`password` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`profile_pic` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`verification_token` tinyint(1) unsigned DEFAULT NULL,
|
||||
`disabled` tinyint(1) unsigned DEFAULT NULL,
|
||||
`last_login` double DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `staff_ticket`
|
||||
--
|
||||
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE IF NOT EXISTS `staff_ticket` (
|
||||
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`ticket_id` int(11) unsigned DEFAULT NULL,
|
||||
`staff_id` int(11) unsigned DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `UQ_8a53d6ad25611e7060ab030785c3c0cef48bc533` (`staff_id`,`ticket_id`),
|
||||
KEY `index_foreignkey_staff_ticket_ticket` (`ticket_id`),
|
||||
KEY `index_foreignkey_staff_ticket_staff` (`staff_id`),
|
||||
CONSTRAINT `c_fk_staff_ticket_staff_id` FOREIGN KEY (`staff_id`) REFERENCES `staff` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
CONSTRAINT `c_fk_staff_ticket_ticket_id` FOREIGN KEY (`ticket_id`) REFERENCES `ticket` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `tag`
|
||||
--
|
||||
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE IF NOT EXISTS `tag` (
|
||||
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`name` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`color` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `tag_ticket`
|
||||
--
|
||||
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE IF NOT EXISTS `tag_ticket` (
|
||||
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`tag_id` int(11) unsigned DEFAULT NULL,
|
||||
`ticket_id` int(11) unsigned DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `UQ_79dc4901a5620abff6d65219e6aef86fe9ab63bc` (`tag_id`,`ticket_id`),
|
||||
KEY `index_foreignkey_tag_ticket_tag` (`tag_id`),
|
||||
KEY `index_foreignkey_tag_ticket_ticket` (`ticket_id`),
|
||||
CONSTRAINT `c_fk_tag_ticket_tag_id` FOREIGN KEY (`tag_id`) REFERENCES `tag` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
CONSTRAINT `c_fk_tag_ticket_ticket_id` FOREIGN KEY (`ticket_id`) REFERENCES `ticket` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `ticket`
|
||||
--
|
||||
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE IF NOT EXISTS `ticket` (
|
||||
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`ticket_number` int(11) unsigned DEFAULT NULL,
|
||||
`unread` tinyint(1) unsigned DEFAULT NULL,
|
||||
`priority` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`unread_staff` tinyint(1) unsigned DEFAULT NULL,
|
||||
`title` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`content` text COLLATE utf8mb4_unicode_ci,
|
||||
`language` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`file` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`date` double DEFAULT NULL,
|
||||
`closed` tinyint(1) unsigned DEFAULT NULL,
|
||||
`author_email` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`author_name` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`department_id` int(11) unsigned DEFAULT NULL,
|
||||
`author` tinyint(1) unsigned DEFAULT NULL,
|
||||
`author_id` int(11) unsigned DEFAULT NULL,
|
||||
`author_staff` tinyint(1) unsigned DEFAULT NULL,
|
||||
`author_staff_id` int(11) unsigned DEFAULT NULL,
|
||||
`owner_id` int(11) unsigned DEFAULT NULL,
|
||||
`owner` tinyint(1) unsigned DEFAULT NULL,
|
||||
`edited_content` tinyint(1) unsigned DEFAULT NULL,
|
||||
`edited_title` tinyint(1) unsigned DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `index_foreignkey_ticket_department` (`department_id`),
|
||||
KEY `index_foreignkey_ticket_author` (`author_id`),
|
||||
KEY `index_foreignkey_ticket_author_staff` (`author_staff_id`),
|
||||
KEY `index_foreignkey_ticket_owner` (`owner_id`),
|
||||
CONSTRAINT `c_fk_ticket_author_id` FOREIGN KEY (`author_id`) REFERENCES `user` (`id`) ON DELETE SET NULL ON UPDATE SET NULL,
|
||||
CONSTRAINT `c_fk_ticket_author_staff_id` FOREIGN KEY (`author_staff_id`) REFERENCES `staff` (`id`) ON DELETE SET NULL ON UPDATE SET NULL,
|
||||
CONSTRAINT `c_fk_ticket_department_id` FOREIGN KEY (`department_id`) REFERENCES `department` (`id`) ON DELETE SET NULL ON UPDATE SET NULL,
|
||||
CONSTRAINT `c_fk_ticket_owner_id` FOREIGN KEY (`owner_id`) REFERENCES `staff` (`id`) ON DELETE SET NULL ON UPDATE SET NULL
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `ticket_user`
|
||||
--
|
||||
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE IF NOT EXISTS `ticket_user` (
|
||||
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`ticket_id` int(11) unsigned DEFAULT NULL,
|
||||
`user_id` int(11) unsigned DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `UQ_f73b0a7296c1f9101e14c7c4bd6476b9f9a6a2b7` (`ticket_id`,`user_id`),
|
||||
KEY `index_foreignkey_ticket_user_ticket` (`ticket_id`),
|
||||
KEY `index_foreignkey_ticket_user_user` (`user_id`),
|
||||
CONSTRAINT `c_fk_ticket_user_ticket_id` FOREIGN KEY (`ticket_id`) REFERENCES `ticket` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
CONSTRAINT `c_fk_ticket_user_user_id` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `ticketevent`
|
||||
--
|
||||
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE IF NOT EXISTS `ticketevent` (
|
||||
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`type` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`content` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`date` double DEFAULT NULL,
|
||||
`ticket_id` int(11) unsigned DEFAULT NULL,
|
||||
`author_staff_id` int(11) unsigned DEFAULT NULL,
|
||||
`file` tinyint(1) unsigned DEFAULT NULL,
|
||||
`private` int(11) unsigned DEFAULT NULL,
|
||||
`author_user_id` int(11) unsigned DEFAULT NULL,
|
||||
`edited_content` tinyint(1) unsigned DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `index_foreignkey_ticketevent_ticket` (`ticket_id`),
|
||||
KEY `index_foreignkey_ticketevent_author_staff` (`author_staff_id`),
|
||||
KEY `index_foreignkey_ticketevent_author_user` (`author_user_id`),
|
||||
CONSTRAINT `c_fk_ticketevent_author_staff_id` FOREIGN KEY (`author_staff_id`) REFERENCES `staff` (`id`) ON DELETE SET NULL ON UPDATE SET NULL,
|
||||
CONSTRAINT `c_fk_ticketevent_author_user_id` FOREIGN KEY (`author_user_id`) REFERENCES `user` (`id`) ON DELETE SET NULL ON UPDATE SET NULL,
|
||||
CONSTRAINT `c_fk_ticketevent_ticket_id` FOREIGN KEY (`ticket_id`) REFERENCES `ticket` (`id`) ON DELETE SET NULL ON UPDATE SET NULL
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `topic`
|
||||
--
|
||||
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE IF NOT EXISTS `topic` (
|
||||
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`name` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`icon` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`icon_color` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`private` int(11) unsigned DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `user`
|
||||
--
|
||||
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE IF NOT EXISTS `user` (
|
||||
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`email` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`password` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`name` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`signup_date` double DEFAULT NULL,
|
||||
`tickets` int(11) unsigned DEFAULT NULL,
|
||||
`verification_token` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`disabled` int(11) unsigned DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
|
||||
|
||||
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
|
||||
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
|
||||
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
|
||||
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
|
||||
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
|
||||
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
|
||||
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
|
||||
|
||||
-- Dump completed on 2020-01-18 20:58:21
|
|
@ -9,6 +9,8 @@ if(defined('MYSQL_HOST') && defined('MYSQL_DATABASE') && defined('MYSQL_USER') &
|
|||
if(!defined('MYSQL_PORT')) define('MYSQL_PORT', '3306');
|
||||
RedBean::setup('mysql:host='. MYSQL_HOST . ';port=' . MYSQL_PORT . ';dbname=' . MYSQL_DATABASE , MYSQL_USER, MYSQL_PASSWORD);
|
||||
RedBean::setAutoResolve(true);
|
||||
// TODO: Implement freeze
|
||||
// RedBean::freeze();
|
||||
}
|
||||
|
||||
// SLIM FRAMEWORK
|
||||
|
|
|
@ -6,7 +6,15 @@ abstract class DataStore {
|
|||
protected $properties = [];
|
||||
|
||||
public static function isTableEmpty() {
|
||||
return (RedBean::count(static::TABLE) === 0);
|
||||
try {
|
||||
return (RedBean::count(static::TABLE) === 0);
|
||||
} catch(\Exception $e) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public static function getFetchAs() {
|
||||
return [];
|
||||
}
|
||||
|
||||
public static function getDataStore($value, $property = 'id') {
|
||||
|
@ -112,7 +120,13 @@ abstract class DataStore {
|
|||
}
|
||||
|
||||
private function parseBeanProp($prop) {
|
||||
$parsedProp = $this->_bean[$prop];
|
||||
$fetchAs = static::getFetchAs();
|
||||
|
||||
if(array_key_exists($prop, $fetchAs)) {
|
||||
$parsedProp = $this->_bean->fetchAs($fetchAs[$prop])[$prop];
|
||||
} else {
|
||||
$parsedProp = $this->_bean[$prop];
|
||||
}
|
||||
|
||||
if (strpos($prop, 'List')) {
|
||||
$parsedProp = DataStoreList::getList($this->getListType($prop), $parsedProp);
|
||||
|
|
|
@ -56,6 +56,14 @@ class Ticket extends DataStore {
|
|||
);
|
||||
}
|
||||
|
||||
public static function getFetchAs() {
|
||||
return [
|
||||
'author' => 'user',
|
||||
'authorStaff' => 'staff',
|
||||
'owner' => 'staff',
|
||||
];
|
||||
}
|
||||
|
||||
public static function getTicket($value, $property = 'id') {
|
||||
return parent::getDataStore($value, $property);
|
||||
}
|
||||
|
|
|
@ -65,6 +65,13 @@ class Ticketevent extends DataStore {
|
|||
];
|
||||
}
|
||||
|
||||
public static function getFetchAs() {
|
||||
return [
|
||||
'authorUser' => 'user',
|
||||
'authorStaff' => 'staff'
|
||||
];
|
||||
}
|
||||
|
||||
public function getAuthor() {
|
||||
if($this->authorUser) {
|
||||
return $this->authorUser;
|
||||
|
|
|
@ -71,6 +71,7 @@ require './ticket/add-tag.rb'
|
|||
require './ticket/delete-tag.rb'
|
||||
require './ticket/edit-comment.rb'
|
||||
require './ticket/edit-title.rb'
|
||||
require './system/custom-fields.rb'
|
||||
require './system/disable-user-system.rb'
|
||||
require './ticket/search.rb'
|
||||
# require './system/get-stats.rb'
|
||||
|
|
Loading…
Reference in New Issue