This commit is contained in:
Guillermo Giuliana 2020-01-23 13:12:38 -03:00
commit c862c0a6fa
17 changed files with 1530 additions and 110 deletions

View File

@ -59,10 +59,11 @@ class TicketViewer extends React.Component {
commentEdited: false, commentEdited: false,
commentPrivate: false, commentPrivate: false,
edit: false, edit: false,
editTitle: false,
editId: 0, editId: 0,
tagSelectorLoading: false,
editTitle: false,
newTitle: this.props.ticket.title, newTitle: this.props.ticket.title,
editTitleError: false editTitleError: false,
}; };
componentDidMount() { componentDidMount() {
@ -176,7 +177,14 @@ class TicketViewer extends React.Component {
onChange={this.onDepartmentDropdownChanged.bind(this)} /> onChange={this.onDepartmentDropdownChanged.bind(this)} />
</div> </div>
<div className="col-md-4">{ticket.author.name}</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>
<div className="ticket-viewer__info-row-header row"> <div className="ticket-viewer__info-row-header row">
<div className="col-md-4">{i18n('PRIORITY')}</div> <div className="col-md-4">{i18n('PRIORITY')}</div>
@ -185,7 +193,11 @@ class TicketViewer extends React.Component {
</div> </div>
<div className="ticket-viewer__info-row-values row"> <div className="ticket-viewer__info-row-values row">
<div className="col-md-4"> <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>
<div className="col-md-4"> <div className="col-md-4">
{this.renderAssignStaffList()} {this.renderAssignStaffList()}
@ -510,23 +522,47 @@ class TicketViewer extends React.Component {
} }
addTag(tag) { addTag(tag) {
this.setState({
tagSelectorLoading: true,
})
API.call({ API.call({
path: '/ticket/add-tag', path: '/ticket/add-tag',
data: { data: {
ticketNumber: this.props.ticket.ticketNumber, ticketNumber: this.props.ticket.ticketNumber,
tagId: tag tagId: tag
} }
}).then(this.onTicketModification.bind(this)) })
.then(() => {
this.setState({
tagSelectorLoading: false,
});
this.onTicketModification();
})
.catch(() => this.setState({
tagSelectorLoading: false,
}))
} }
removeTag(tag) { removeTag(tag) {
this.setState({
tagSelectorLoading: true,
});
API.call({ API.call({
path: '/ticket/remove-tag', path: '/ticket/remove-tag',
data: { data: {
ticketNumber: this.props.ticket.ticketNumber, ticketNumber: this.props.ticket.ticketNumber,
tagId: tag tagId: tag
} }
}).then(this.onTicketModification.bind(this)) }).then(() => {
this.setState({
tagSelectorLoading: false,
});
this.onTicketModification();
}).catch(() => this.setState({
tagSelectorLoading: false,
}))
} }
onCustomResponsesChanged({index}) { onCustomResponsesChanged({index}) {

View File

@ -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);
});
});
});

View File

@ -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;

View File

@ -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;
}
}

View File

@ -2,10 +2,11 @@ import React from 'react';
import _ from 'lodash'; import _ from 'lodash';
import classNames from 'classnames'; import classNames from 'classnames';
import {Motion, spring} from 'react-motion'; import {Motion, spring} from 'react-motion';
import keyCode from 'keycode'; import keyCode from 'keycode';
import Menu from 'core-components/menu'; import Menu from 'core-components/menu';
import Icon from 'core-components/icon'; import Icon from 'core-components/icon';
import Loading from 'core-components/loading'
class DropDown extends React.Component { class DropDown extends React.Component {
@ -14,7 +15,11 @@ class DropDown extends React.Component {
selectedIndex: React.PropTypes.number, selectedIndex: React.PropTypes.number,
items: Menu.propTypes.items, items: Menu.propTypes.items,
onChange: React.PropTypes.func, 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 = { static defaultProps = {
@ -28,7 +33,7 @@ class DropDown extends React.Component {
menuId: _.uniqueId('drop-down-menu_'), menuId: _.uniqueId('drop-down-menu_'),
selectedIndex: props.selectedIndex || props.defaultSelectedIndex, selectedIndex: props.selectedIndex || props.defaultSelectedIndex,
highlightedIndex: props.selectedIndex || props.defaultSelectedIndex, highlightedIndex: props.selectedIndex || props.defaultSelectedIndex,
opened: false opened: false,
}; };
} }
@ -44,7 +49,7 @@ class DropDown extends React.Component {
return { return {
defaultStyle: {opacity: 0, translateY: 20}, 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()}> <div {...this.getCurrentItemProps()}>
{this.props.children ? this.props.children : this.renderCurrentItem()} {this.props.children ? this.props.children : this.renderCurrentItem()}
</div> </div>
<Motion defaultStyle={animation.defaultStyle} style={animation.style} onRest={this.onAnimationFinished.bind(this)}> <Motion
{this.renderList.bind(this)} defaultStyle={animation.defaultStyle}
style={animation.style}
onRest={this.onAnimationFinished.bind(this)}>
{this.renderList.bind(this)}
</Motion> </Motion>
</div> </div>
); );
} }
renderList({opacity, translateY}) { renderList({opacity, translateY}) {
let style = { opacity: opacity, transform: `translateY(${translateY}px)`}; let style = {
opacity: opacity,
transform: `translateY(${translateY}px)`
};
return ( return (
<div className="drop-down__list-container" style={style}> <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> </div>
); );
} }
@ -77,7 +92,6 @@ class DropDown extends React.Component {
let item = this.props.items[this.getSelectedIndex()]; let item = this.props.items[this.getSelectedIndex()];
let iconNode = null; let iconNode = null;
if (item.icon) { if (item.icon) {
iconNode = <Icon className="drop-down__current-item-icon" name={item.icon} />; iconNode = <Icon className="drop-down__current-item-icon" name={item.icon} />;
} }
@ -90,12 +104,17 @@ class DropDown extends React.Component {
} }
getClass() { getClass() {
const {
className,
size,
} = this.props;
let classes = { let classes = {
'drop-down': true, 'drop-down': true,
'drop-down_closed': !this.state.opened, 'drop-down_closed': !this.getOpen(),
['drop-down_' + this.props.size]: (this.props.size), ['drop-down_' + size]: (size),
[this.props.className]: (this.props.className) [className]: (className)
}; };
return classNames(classes); return classNames(classes);
@ -103,10 +122,10 @@ class DropDown extends React.Component {
getCurrentItemProps() { getCurrentItemProps() {
return { return {
'aria-expanded': this.state.opened, 'aria-expanded': this.getOpen(),
'aria-autocomplete': 'list', 'aria-autocomplete': 'list',
'aria-owns': this.state.menuId, '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', className: 'drop-down__current-item',
onClick: this.handleClick.bind(this), onClick: this.handleClick.bind(this),
onKeyDown: this.onKeyDown.bind(this), onKeyDown: this.onKeyDown.bind(this),
@ -123,7 +142,7 @@ class DropDown extends React.Component {
items: this.props.items, items: this.props.items,
onItemClick: this.handleItemClick.bind(this), onItemClick: this.handleItemClick.bind(this),
onMouseDown: this.handleListMouseDown.bind(this), onMouseDown: this.handleListMouseDown.bind(this),
selectedIndex: this.state.highlightedIndex, selectedIndex: this.getHighlightedIndex(),
role: 'listbox' role: 'listbox'
}; };
} }
@ -138,26 +157,37 @@ class DropDown extends React.Component {
} }
getKeyActions(event) { getKeyActions(event) {
const {highlightedIndex, opened} = this.state; const highlightedIndex = this.getHighlightedIndex();
const opened = this.getOpen();
const itemsQuantity = this.props.items.length; const itemsQuantity = this.props.items.length;
const {
onHighlightedIndexChange,
onMenuToggle,
} = this.props;
return { return {
'up': () => { 'up': () => {
if (opened) { if (opened) {
const newHighlightedIndex = this.modulo(highlightedIndex - 1, itemsQuantity);
event.preventDefault(); event.preventDefault();
this.setState({ this.setState({
highlightedIndex: this.modulo(highlightedIndex - 1, itemsQuantity) highlightedIndex: newHighlightedIndex,
}); });
onHighlightedIndexChange && onHighlightedIndexChange(newHighlightedIndex);
} }
}, },
'down': () => { 'down': () => {
if (opened) { if (opened) {
const newHighlightedIndex = this.modulo(highlightedIndex + 1, itemsQuantity);
event.preventDefault(); event.preventDefault();
this.setState({ this.setState({
highlightedIndex: this.modulo(highlightedIndex + 1, itemsQuantity) highlightedIndex: newHighlightedIndex,
}); });
onHighlightedIndexChange && onHighlightedIndexChange(newHighlightedIndex);
} }
}, },
'enter': () => { 'enter': () => {
@ -167,6 +197,8 @@ class DropDown extends React.Component {
this.setState({ this.setState({
opened: true opened: true
}); });
onMenuToggle && onMenuToggle(true);
} }
}, },
'space': () => { 'space': () => {
@ -175,32 +207,44 @@ class DropDown extends React.Component {
this.setState({ this.setState({
opened: true opened: true
}); });
onMenuToggle && onMenuToggle(true);
}, },
'esc': () => { 'esc': () => {
this.setState({ this.setState({
opened: false opened: false
}); });
onMenuToggle && onMenuToggle(false);
}, },
'tab': () => { 'tab': () => {
if (this.state.opened) { if (this.getOpen()) {
event.preventDefault(); event.preventDefault();
this.onIndexSelected(highlightedIndex) onHighlightedIndexChange && this.onIndexSelected(highlightedIndex);
} }
} }
}; };
} }
handleBlur() { handleBlur() {
const { onMenuToggle, } = this.props;
this.setState({ this.setState({
opened: false opened: false
}); });
onMenuToggle && onMenuToggle(false);
} }
handleClick() { handleClick() {
const { onMenuToggle, } = this.props;
this.setState({ this.setState({
opened: !this.state.opened opened: !this.getOpen()
}); });
onMenuToggle && onMenuToggle(!this.getOpen());
} }
handleItemClick(index) { handleItemClick(index) {
@ -208,16 +252,25 @@ class DropDown extends React.Component {
} }
onIndexSelected(index) { onIndexSelected(index) {
this.setState({ const {
opened: false, onMenuToggle,
selectedIndex: index, onHighlightedIndexChange,
highlightedIndex: index onChange,
}); loading,
} = this.props;
if (this.props.onChange) { if (!loading){
this.props.onChange({ this.setState({
index 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() { onAnimationFinished() {
if (!this.state.opened && this.state.highlightedIndex !== this.getSelectedIndex()) { const { onHighlightedIndexChange, } = this.props;
if (!this.getOpen() && this.getHighlightedIndex() !== this.getSelectedIndex()) {
this.setState({ this.setState({
highlightedIndex: this.getSelectedIndex() highlightedIndex: this.getSelectedIndex(),
}); });
onHighlightedIndexChange && onHighlightedIndexChange(this.getSelectedIndex());
} }
} }
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) { modulo(number, mod) {
return ((number % mod) + mod) % 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; export default DropDown;

View File

@ -28,6 +28,25 @@
position: absolute; position: absolute;
width: 150px; width: 150px;
z-index: 100; 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 { &_closed {

View File

@ -1,8 +1,6 @@
import React from 'react'; import React from 'react';
import _ from 'lodash'; import _ from 'lodash';
import Icon from 'core-components/icon'; import Autocomplete from 'core-components/autocomplete';
import DropDown from 'core-components/drop-down';
import Tag from 'core-components/tag';
class TagSelector extends React.Component { class TagSelector extends React.Component {
@ -13,55 +11,34 @@ class TagSelector extends React.Component {
})), })),
values: React.PropTypes.arrayOf(React.PropTypes.string), values: React.PropTypes.arrayOf(React.PropTypes.string),
onRemoveClick: React.PropTypes.func, onRemoveClick: React.PropTypes.func,
onTagSelected: React.PropTypes.func,
loading: React.PropTypes.bool,
}; };
render() { 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 ( return (
<div className="tag-selector"> <div className="tag-selector">
<DropDown className="tag-selector__drop-down" items={this.renderTagOptions().map(tag => ({content: tag}))} selectedIndex={-1} size="large"> <Autocomplete
{this.renderSelectedTags()} items={items}
</DropDown> values={values}
onRemoveClick={this.props.onRemoveClick}
onTagSelected={this.props.onTagSelected}
disabled={this.props.loading} />
</div> </div>
); );
} }
renderSelectedTags() { renderTagOption(item) {
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) {
return ( 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-square" style={{backgroundColor:item.color}}/>
<span className="tag-selector__tag-option-name" >{item.name}</span> <span className="tag-selector__tag-option-name" >{item.name}</span>
</div> </div>
); );
} }
onRemoveClick(tagId) {
if(this.props.onRemoveClick){
this.props.onRemoveClick(tagId);
}
}
onTagSelected(tagId) {
if(this.props.onTagSelected){
this.props.onTagSelected(tagId);
}
}
} }
export default TagSelector; export default TagSelector;

View File

@ -9,7 +9,7 @@ export default {
'REPEAT_PASSWORD': 'Repetir Contraseña', 'REPEAT_PASSWORD': 'Repetir Contraseña',
'LOG_IN': 'Iniciar sesión', 'LOG_IN': 'Iniciar sesión',
'SIGN_UP': 'Registrarse', 'SIGN_UP': 'Registrarse',
'FORGOT_PASSWORD': 'Olvidaste tu contraseña?', 'FORGOT_PASSWORD': '¿Olvidó su contraseña?',
'RECOVER_PASSWORD': 'Recuperar Contraseña', 'RECOVER_PASSWORD': 'Recuperar Contraseña',
'SET_UP_PASSWORD': 'Configurar su contraseña', 'SET_UP_PASSWORD': 'Configurar su contraseña',
'RECOVER_SENT': 'Se ha enviado un email con instrucciones de recuperación.', 'RECOVER_SENT': 'Se ha enviado un email con instrucciones de recuperación.',
@ -35,7 +35,7 @@ export default {
'NO_ATTACHMENT': 'No hay archivo adjunto', 'NO_ATTACHMENT': 'No hay archivo adjunto',
'STAFF': 'Staff', 'STAFF': 'Staff',
'CUSTOMER': 'Cliente', 'CUSTOMER': 'Cliente',
'YES': 'Si', 'YES': 'Sí',
'NO': 'No', 'NO': 'No',
'CANCEL': 'Cancelar', 'CANCEL': 'Cancelar',
'MY_ACCOUNT': 'Mi Cuenta', 'MY_ACCOUNT': 'Mi Cuenta',
@ -81,7 +81,7 @@ export default {
'ASSIGN_TO_ME': 'Asignar', 'ASSIGN_TO_ME': 'Asignar',
'UN_ASSIGN': 'Desasignar', 'UN_ASSIGN': 'Desasignar',
'VIEW_TICKET': 'Ver Ticket', '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...', 'SELECT_CUSTOM_RESPONSE': 'Seleccionar una respuesta personalizada...',
'WARNING': 'Advertencia', 'WARNING': 'Advertencia',
'INFO': 'Información', 'INFO': 'Información',
@ -131,7 +131,7 @@ export default {
'UPDATE_PASSWORD': 'Actualizar contraseña', 'UPDATE_PASSWORD': 'Actualizar contraseña',
'UPDATE_LEVEL': 'Actualizar nivel', 'UPDATE_LEVEL': 'Actualizar nivel',
'UPDATE_DEPARTMENTS': 'Actializar departamentos', 'UPDATE_DEPARTMENTS': 'Actializar departamentos',
'EDIT_STAFF': 'Edit staff member', 'EDIT_STAFF': 'Editar miembro del staff',
'ADD_DEPARTMENT': 'Añadir departamento', 'ADD_DEPARTMENT': 'Añadir departamento',
'UPDATE_DEPARTMENT': 'Actualizar Departamento', 'UPDATE_DEPARTMENT': 'Actualizar Departamento',
'TRANSFER_TICKETS_TO': 'Transferir tickets a', 'TRANSFER_TICKETS_TO': 'Transferir tickets a',
@ -165,7 +165,7 @@ export default {
'ALLOWED_LANGUAGES': 'Idiomas Permitidos', 'ALLOWED_LANGUAGES': 'Idiomas Permitidos',
'ALLOWED_LANGUAGES_INFO': 'Los idiomas permitidos son los idiomas que puede utilizar un usuario.', 'ALLOWED_LANGUAGES_INFO': 'Los idiomas permitidos son los idiomas que puede utilizar un usuario.',
'SETTINGS_UPDATED': 'Se han actualizado las configuraciones', 'SETTINGS_UPDATED': 'Se han actualizado las configuraciones',
'ON': 'Si', 'ON': 'Sí',
'OFF': 'No', 'OFF': 'No',
'BOXED': 'Boxed', 'BOXED': 'Boxed',
'FULL_WIDTH': 'Ancho Total', 'FULL_WIDTH': 'Ancho Total',
@ -179,8 +179,8 @@ export default {
'INCLUDE_USERS_VIA_CSV': 'Incluir usuarios por archivo CSV', 'INCLUDE_USERS_VIA_CSV': 'Incluir usuarios por archivo CSV',
'BACKUP_DATABASE': 'Backup database', 'BACKUP_DATABASE': 'Backup database',
'DELETE_ALL_USERS': 'Borrar todos los usuarios', 'DELETE_ALL_USERS': 'Borrar todos los usuarios',
'PLEASE_CONFIRM_PASSWORD': 'Por favor confirma tu contraseña para hacer estos cambios', 'PLEASE_CONFIRM_PASSWORD': 'Por favor, confirme su contraseña para hacer estos cambios',
'REGISTRATION_API_KEYS': 'Registration API keys', 'REGISTRATION_API_KEYS': 'Registro de API keys',
'NAME_OF_KEY': 'Nombre de la clave', 'NAME_OF_KEY': 'Nombre de la clave',
'KEY': 'Clave', 'KEY': 'Clave',
'ADD_API_KEY': 'Añadir nueva clave', 'ADD_API_KEY': 'Añadir nueva clave',
@ -215,7 +215,7 @@ export default {
'OPTION': 'Opción {index}', 'OPTION': 'Opción {index}',
'OPTIONS': 'Opciones', 'OPTIONS': 'Opciones',
'FIELD_DESCRIPTION': 'Descripción del campo (opcional)', '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', 'DESCRIPTION_EDIT_CUSTOM_TAG': 'aquí se puede editar una etiqueta personalizada',
'CUSTOM_FIELDS': 'Campos Personalizados', 'CUSTOM_FIELDS': 'Campos Personalizados',
@ -234,12 +234,12 @@ export default {
'ACTIVITY_RE_OPEN': 'reabrió el ticket', 'ACTIVITY_RE_OPEN': 'reabrió el ticket',
'ACTIVITY_DEPARTMENT_CHANGED': 'cambió el departamento del ticket', 'ACTIVITY_DEPARTMENT_CHANGED': 'cambió el departamento del ticket',
'ACTIVITY_PRIORITY_CHANGED': 'cambió la prioridad 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_EDIT_SETTINGS': 'editó las preferencias',
'ACTIVITY_SIGNUP': 'se registró', 'ACTIVITY_SIGNUP': 'se registró',
'ACTIVITY_EDIT_TITLE': 'editó el titulo del ticket', '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_ADD_ARTICLE': 'añadió el articulo',
'ACTIVITY_DELETE_TOPIC': 'eliminó el tema', 'ACTIVITY_DELETE_TOPIC': 'eliminó el tema',
'ACTIVITY_DELETE_ARTICLE': 'eliminó el artículo', 'ACTIVITY_DELETE_ARTICLE': 'eliminó el artículo',
@ -276,13 +276,13 @@ export default {
'ADMIN_NAME': 'Nombre de la cuenta admin', 'ADMIN_NAME': 'Nombre de la cuenta admin',
'ADMIN_EMAIL': 'Email de la cuenta admin', 'ADMIN_EMAIL': 'Email de la cuenta admin',
'ADMIN_PASSWORD': 'Contraseña 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': '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_TITLE': 'Paso {current} de {total} - {title}',
'STEP_1_DESCRIPTION': 'Seleccione su idioma preferido para el asistente de instalación.', '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_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_4_DESCRIPTION': 'Por favor, seleccione sus preferencias de sistema de usuarios.',
'STEP_5_DESCRIPTION': 'Pro favor, seleccione sus preferencias generales del sistema.', 'STEP_5_DESCRIPTION': 'Pro favor, seleccione sus preferencias generales del sistema.',
@ -292,29 +292,29 @@ export default {
//VIEW DESCRIPTIONS //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.', '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.', '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.', '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': 'Echa un vistazo a nuestros artículos sobre temas comunes, guías y documentación.', '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.', '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.', '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_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', 'CUSTOM_TAGS_DESCRIPTION': 'Aquí puede ver y administrar las etiquetas personalizadas de los tickets para identificarlos mejor',
'MY_TICKETS_DESCRIPTION': 'Aquí puedes ver los tickets que tienes asignado.', 'MY_TICKETS_DESCRIPTION': 'Aquí puede ver los tickets que tienes asignado.',
'NEW_TICKETS_DESCRIPTION': 'Aquí puedes ver todos los tickets nuevos que no están asignados por nadie.', '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 estas asignado.', '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 asignarselo.', 'TICKET_VIEW_DESCRIPTION': 'Este ticket ha sido enviado por un cliente. Aquí puede responderlo o asignárselo.',
'BAN_USERS_DESCRIPTION': 'Aquí puedes ver la lista de email bloqueados. Puedes des-bloquear o agregar más emails a la lista.', '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 estan registrados en el sistema. Puedes buscar a alguien en particular, bloquearlo o borralo.', '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.', '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.', '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', '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.', '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}.', '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.', '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.', '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.', 'DELETE_ARTICLE_DESCRIPTION': 'Va a borrar este artículo para siempre.',
'STAFF_MEMBERS_DESCRIPTION': 'Aquí puedes ver quienes son los miembros de staff.', 'STAFF_MEMBERS_DESCRIPTION': 'Aquí puede ver quiénes son los miembros de staff.',
'ADD_STAFF_DESCRIPTION': 'Usa este formulario para agregar 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.', 'EDIT_STAFF_DESCRIPTION': 'Aquí puede editar información sobre un miembro de staff.',
'MY_ACCOUNT_DESCRIPTION': 'Aquí puede editar información acerca de usted.', '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.', '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', 'REGISTRATION_ENABLED': 'Se ha habilitado el registro',
'ADD_API_KEY_DESCRIPTION': 'Inserte en nombre y una Registration API Key será generada.', '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.', '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.', '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 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.', '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.', '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.', '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.', '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.', '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_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.', 'PRIVATE_TOPIC_DESCRIPTION': 'Este tema solo será visto por los miembros del personal.',
@ -410,7 +410,7 @@ export default {
'SUCCESSFUL_CONNECTION': 'Conexión exitosa', 'SUCCESSFUL_CONNECTION': 'Conexión exitosa',
'UNSUCCESSFUL_CONNECTION': 'Conexión fallida', 'UNSUCCESSFUL_CONNECTION': 'Conexión fallida',
'SERVER_CREDENTIALS_WORKING': 'Las credenciales del servidor están funcionando correctamente', '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)', 'COMMENT_EDITED': '(Comentario editado)',
'TITLE_EDITED': '(Título editado)', 'TITLE_EDITED': '(Título editado)',
@ -428,7 +428,7 @@ export default {
'ACTIVITY_RE_OPEN_THIS': 'reabrió este ticket', 'ACTIVITY_RE_OPEN_THIS': 'reabrió este ticket',
'ACTIVITY_DEPARTMENT_CHANGED_THIS': 'cambió el departamento de este ticket a ', 'ACTIVITY_DEPARTMENT_CHANGED_THIS': 'cambió el departamento de este ticket a ',
'ACTIVITY_PRIORITY_CHANGED_THIS': 'cambió la prioridad 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', 'LEFT_EMPTY_DATABASE': 'Dejar vacío para la creación automática de bases de datos',
'REMEMBER_ME': 'Recordarme', 'REMEMBER_ME': 'Recordarme',
'EMAIL_LOWERCASE': 'email', 'EMAIL_LOWERCASE': 'email',

View File

@ -4,7 +4,7 @@
"respect/validation": "^1.1", "respect/validation": "^1.1",
"phpmailer/phpmailer": "^5.2", "phpmailer/phpmailer": "^5.2",
"google/recaptcha": "~1.1", "google/recaptcha": "~1.1",
"gabordemooij/redbean": "^4.3", "gabordemooij/redbean": "^5.4",
"ifsnop/mysqldump-php": "2.*", "ifsnop/mysqldump-php": "2.*",
"ezyang/htmlpurifier": "^4.8", "ezyang/htmlpurifier": "^4.8",
"codeguy/upload": "^1.3", "codeguy/upload": "^1.3",

View File

@ -1,4 +1,5 @@
<?php <?php
use RedBeanPHP\Facade as RedBean;
use Respect\Validation\Validator as DataValidator; use Respect\Validation\Validator as DataValidator;
DataValidator::with('CustomValidations', true); DataValidator::with('CustomValidations', true);
@ -52,6 +53,7 @@ class InitSettingsController extends Controller {
public function handler() { public function handler() {
if (Setting::isTableEmpty()) { if (Setting::isTableEmpty()) {
RedBean::exec(file_get_contents('data/db_schema.sql'));
$this->storeGlobalSettings(); $this->storeGlobalSettings();
$this->storeMailTemplates(); $this->storeMailTemplates();
$this->storeLanguages(); $this->storeLanguages();

473
server/data/db_schema.sql Normal file
View File

@ -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

View File

@ -9,6 +9,8 @@ if(defined('MYSQL_HOST') && defined('MYSQL_DATABASE') && defined('MYSQL_USER') &
if(!defined('MYSQL_PORT')) define('MYSQL_PORT', '3306'); 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::setup('mysql:host='. MYSQL_HOST . ';port=' . MYSQL_PORT . ';dbname=' . MYSQL_DATABASE , MYSQL_USER, MYSQL_PASSWORD);
RedBean::setAutoResolve(true); RedBean::setAutoResolve(true);
// TODO: Implement freeze
// RedBean::freeze();
} }
// SLIM FRAMEWORK // SLIM FRAMEWORK

View File

@ -6,7 +6,15 @@ abstract class DataStore {
protected $properties = []; protected $properties = [];
public static function isTableEmpty() { 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') { public static function getDataStore($value, $property = 'id') {
@ -112,7 +120,13 @@ abstract class DataStore {
} }
private function parseBeanProp($prop) { 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')) { if (strpos($prop, 'List')) {
$parsedProp = DataStoreList::getList($this->getListType($prop), $parsedProp); $parsedProp = DataStoreList::getList($this->getListType($prop), $parsedProp);

View File

@ -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') { public static function getTicket($value, $property = 'id') {
return parent::getDataStore($value, $property); return parent::getDataStore($value, $property);
} }

View File

@ -65,6 +65,13 @@ class Ticketevent extends DataStore {
]; ];
} }
public static function getFetchAs() {
return [
'authorUser' => 'user',
'authorStaff' => 'staff'
];
}
public function getAuthor() { public function getAuthor() {
if($this->authorUser) { if($this->authorUser) {
return $this->authorUser; return $this->authorUser;

View File

@ -71,6 +71,7 @@ require './ticket/add-tag.rb'
require './ticket/delete-tag.rb' require './ticket/delete-tag.rb'
require './ticket/edit-comment.rb' require './ticket/edit-comment.rb'
require './ticket/edit-title.rb' require './ticket/edit-title.rb'
require './system/custom-fields.rb'
require './system/disable-user-system.rb' require './system/disable-user-system.rb'
require './ticket/search.rb' require './ticket/search.rb'
# require './system/get-stats.rb' # require './system/get-stats.rb'