mirror of
https://github.com/snipe/snipe-it.git
synced 2024-11-11 08:04:09 -08:00
5a84863873
Fix a bug where "name" was used ambigously and mapping "item name" to "name" would confuse the importer into thinking it should also be a user name. Now we default to "full name" for the users name, and "item name" for the item name. These are still both configurable through the custom mapping. Also update sample csvs and remove an outdated sample.
248 lines
10 KiB
Vue
248 lines
10 KiB
Vue
<style>
|
|
tr {
|
|
padding-left:30px;
|
|
}
|
|
</style>
|
|
|
|
<template>
|
|
<tr v-show="processDetail">
|
|
<td colspan="3">
|
|
<h4 class="modal-title">Import File:</h4>
|
|
<div class="dynamic-form-row">
|
|
<div class="col-md-4 col-xs-12">
|
|
<label for="import-type">Import Type:</label>
|
|
</div>
|
|
<div class="col-md-4 col-xs-12">
|
|
<select2 :options="options.importTypes" v-model="options.importType" required>
|
|
<option disabled value="0"></option>
|
|
</select2>
|
|
</div>
|
|
</div>
|
|
<div class="dynamic-form-row">
|
|
<div class="col-md-4 col-xs-12">
|
|
<label for="import-update">Update Existing Values?:</label>
|
|
</div>
|
|
<div class="col-md-4 col-xs-12">
|
|
<input type="checkbox" name="import-update" v-model="options.update">
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-md-12" style="padding-top: 30px;">
|
|
<table class="table">
|
|
<thead>
|
|
<th>Header Field</th>
|
|
<th>Import Field</th>
|
|
<th>Sample Value</th>
|
|
</thead>
|
|
<tbody>
|
|
<template v-for="(header, index) in file.header_row">
|
|
<tr>
|
|
<td>
|
|
<label :for="header" class="controllabel">{{ header }}</label>
|
|
</td>
|
|
<td>
|
|
<div required>
|
|
<select2 :options="columns" v-model="columnMappings[header]">
|
|
<option value="0">Do Not Import</option>
|
|
</select2>
|
|
</div>
|
|
</td>
|
|
<td>
|
|
<div>{{ activeFile.first_row[index] }}</div>
|
|
</td>
|
|
</tr>
|
|
</template>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</td>
|
|
|
|
<td>
|
|
<button type="button" class="btn btn-sm btn-default" @click="processDetail = false">Cancel</button>
|
|
<button type="submit" class="btn btn-sm btn-primary" @click="postSave">Import</button>
|
|
<div
|
|
class="alert col-md-5 col-md-offset-1"
|
|
:class="alertClass"
|
|
style="text-align:left"
|
|
v-if="statusText"
|
|
>
|
|
{{ this.statusText }}
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
</template>
|
|
|
|
<script>
|
|
export default {
|
|
props: ['file', 'customFields'],
|
|
data() {
|
|
return {
|
|
activeFile: this.file,
|
|
processDetail: false,
|
|
statusText: null,
|
|
statusType: null,
|
|
options: {
|
|
importType: this.file.import_type,
|
|
update: false,
|
|
importTypes: [
|
|
{ id: 'asset', text: 'Assets' },
|
|
{ id: 'accessory', text: 'Accessories' },
|
|
{ id: 'consumable', text: 'Consumables' },
|
|
{ id: 'component', text: 'Components' },
|
|
{ id: 'license', text: 'Licenses' },
|
|
{ id: 'user', text: 'Users' }
|
|
],
|
|
statusText: null,
|
|
},
|
|
columnOptions: {
|
|
general: [
|
|
{id: 'category', text: 'Category' },
|
|
{id: 'company', text: 'Company' },
|
|
{id: 'checkout_to', text: 'Checked out to' },
|
|
{id: 'email', text: 'Email' },
|
|
{id: 'first_name', text: 'First Name' },
|
|
{id: 'item_name', text: 'Item Name' },
|
|
{id: 'last_name', text: 'Last Name' },
|
|
{id: 'location', text: 'Location' },
|
|
{id: 'maintained', text: 'Maintained' },
|
|
{id: 'manufacturer', text: 'Manufacturer' },
|
|
{id: 'notes', text: 'Notes' },
|
|
{id: 'order_number', text: 'Order Number' },
|
|
{id: 'purchase_cost', text: 'Purchase Cost' },
|
|
{id: 'purchase_date', text: 'Purchase Date' },
|
|
{id: 'quantity', text: 'Quantity' },
|
|
{id: 'requestable', text: 'Requestable' },
|
|
{id: 'serial', text: 'Serial Number' },
|
|
{id: 'supplier', text: 'Supplier' },
|
|
{id: 'username', text: 'Username' },
|
|
],
|
|
assets: [
|
|
{id: 'asset_tag', text: 'Asset Tag' },
|
|
{id: 'asset_model', text: 'Model Name' },
|
|
{id: 'image', text: 'Image Filename' },
|
|
{id: 'model_number', text: 'Model Number' },
|
|
{id: 'full_name', text: 'Full Name' },
|
|
{id: 'status', text: 'Status' },
|
|
{id: 'warranty_months', text: 'Warranty Months' },
|
|
],
|
|
licenses: [
|
|
{id: 'expiration_date', text: 'Expiration Date' },
|
|
{id: 'license_email', text: 'Licensed To Email' },
|
|
{id: 'license_name', text: 'Licensed To Name' },
|
|
{id: 'purchase_order', text: 'Purchase Order' },
|
|
{id: 'reassignable', text: 'Reassignable' },
|
|
{id: 'seats', text: 'Seats' },
|
|
],
|
|
users: [
|
|
{id: 'employee_num', text: 'Employee Number' },
|
|
{id: 'jobtitle', text: 'Job Title' },
|
|
{id: 'phone_number', text: 'Phone Number' },
|
|
],
|
|
customFields: this.customFields,
|
|
},
|
|
columnMappings: this.file.field_map || {},
|
|
activeColumn: null,
|
|
}
|
|
},
|
|
created() {
|
|
window.eventHub.$on('showDetails', this.toggleExtendedDisplay)
|
|
this.populateSelect2ActiveItems();
|
|
},
|
|
computed: {
|
|
columns() {
|
|
switch(this.options.importType) {
|
|
case 'asset':
|
|
return this.columnOptions.general.concat(this.columnOptions.assets).concat(this.columnOptions.customFields);
|
|
case 'license':
|
|
return this.columnOptions.general.concat(this.columnOptions.licenses);
|
|
case 'user':
|
|
return this.columnOptions.general.concat(this.columnOptions.users);
|
|
}
|
|
return this.columnOptions.general;
|
|
},
|
|
alertClass() {
|
|
if(this.statusType=='success') {
|
|
return 'alert-success';
|
|
}
|
|
if(this.statusType=='error') {
|
|
return 'alert-danger';
|
|
}
|
|
return 'alert-info';
|
|
},
|
|
},
|
|
watch: {
|
|
columns() {
|
|
console.log("CHANGED");
|
|
this.populateSelect2ActiveItems();
|
|
}
|
|
},
|
|
methods: {
|
|
postSave() {
|
|
console.log('saving');
|
|
console.log(this.options.importType);
|
|
if(!this.options.importType) {
|
|
this.statusType='error';
|
|
this.statusText= "An import type is required... ";
|
|
return;
|
|
}
|
|
this.statusType='pending';
|
|
this.statusText = "Processing...";
|
|
this.$http.post(route('api.imports.importFile', this.file.id), {
|
|
'import-update': this.options.update,
|
|
'import-type': this.options.importType,
|
|
'column-mappings': this.columnMappings
|
|
}).then( ({body}) => {
|
|
// Success
|
|
this.statusType="success";
|
|
this.statusText = "Success... Redirecting.";
|
|
window.location.href = body.messages.redirect_url;
|
|
}, ({body}) => {
|
|
// Failure
|
|
if(body.status == 'import-errors') {
|
|
window.eventHub.$emit('importErrors', body.messages);
|
|
this.statusType='error';
|
|
this.statusText = "Error";
|
|
} else {
|
|
this.$emit('alert', {
|
|
message: body.messages,
|
|
type: "danger",
|
|
visible: true,
|
|
})
|
|
}
|
|
this.displayImportModal=false;
|
|
});
|
|
},
|
|
populateSelect2ActiveItems() {
|
|
if(this.file.field_map == null) {
|
|
// Begin by populating the active selection in dropdowns with blank values.
|
|
for (var i=0; i < this.file.header_row.length; i++) {
|
|
this.$set(this.columnMappings, this.file.header_row[i], null);
|
|
}
|
|
// Then, for any values that have a likely match, we make that active.
|
|
for(var j=0; j < this.columns.length; j++) {
|
|
let column = this.columns[j];
|
|
let lower = this.file.header_row.map((value) => value.toLowerCase());
|
|
console.dir(lower);
|
|
let index = lower.indexOf(column.text.toLowerCase())
|
|
if(index != -1) {
|
|
this.$set(this.columnMappings, this.file.header_row[index], column.id)
|
|
}
|
|
}
|
|
}
|
|
},
|
|
toggleExtendedDisplay(fileId) {
|
|
if(fileId == this.file.id) {
|
|
this.processDetail = !this.processDetail
|
|
}
|
|
},
|
|
updateModel(header, value) {
|
|
console.log(header, value);
|
|
this.columnMappings[header] = value;
|
|
}
|
|
},
|
|
components: {
|
|
select2: require('../select2.vue')
|
|
}
|
|
}
|
|
</script>
|