mirror of
https://github.com/snipe/snipe-it.git
synced 2024-11-11 16:14:18 -08:00
6756dd193e
Cleaning up routes to match laravel-scim-server's recommended implementation Some actually *working* changes for SCIM support?! Whoops, forgot my route file Fix public SCIM routes Removed Ziggy, removed old generated file, yanked Ziggy references Resolves the first set of comments for SCIM Ensure all /api routes have baseUrl prepended Fix the parent:: call to be, uh, actually correct :P Clarify the route-ordering, as it is quite tricky This gets it so that users can actually be saved.. Work around the lack of callbacks with some inheritance Mapped a bunch more fields from SCIM into Snipe-IT's user table More baseUrl shenanigans since we yanked Ziggy :/ Properly map job title and work with some other necessary attributes Map more fields... Finalized basic mapping for core and enterprise namespaces Latest tuned settings for SCIM config to work with Azure (and others)
328 lines
15 KiB
Vue
328 lines
15 KiB
Vue
<template>
|
|
<tr v-show="processDetail">
|
|
<td colspan="5">
|
|
<div class="col-md-12">
|
|
|
|
<div class="row">
|
|
<div class="dynamic-form-row">
|
|
<div class="col-md-5 col-xs-12">
|
|
<label for="import-type">Import Type:</label>
|
|
</div>
|
|
|
|
<div class="col-md-7 col-xs-12">
|
|
<select2 :options="options.importTypes" v-model="options.importType" required>
|
|
<option disabled value="0"></option>
|
|
</select2>
|
|
</div>
|
|
|
|
</div><!-- /dynamic-form-row -->
|
|
<div class="dynamic-form-row">
|
|
<div class="col-md-5 col-xs-12">
|
|
<label for="import-update">Update Existing Values?:</label>
|
|
</div>
|
|
<div class="col-md-7 col-xs-12">
|
|
<input type="checkbox" class="iCheck minimal" name="import-update" v-model="options.update">
|
|
</div>
|
|
</div><!-- /dynamic-form-row -->
|
|
|
|
<div class="dynamic-form-row">
|
|
<div class="col-md-5 col-xs-12">
|
|
<label for="send-welcome">Send Welcome Email for new Users?</label>
|
|
</div>
|
|
<div class="col-md-7 col-xs-12">
|
|
<input type="checkbox" class="minimal" name="send-welcome" v-model="options.send_welcome">
|
|
</div>
|
|
</div><!-- /dynamic-form-row -->
|
|
|
|
<div class="dynamic-form-row">
|
|
<div class="col-md-5 col-xs-12">
|
|
<label for="run-backup">Backup before importing?</label>
|
|
</div>
|
|
<div class="col-md-7 col-xs-12">
|
|
<input type="checkbox" class="minimal" name="run-backup" v-model="options.run_backup">
|
|
</div>
|
|
</div><!-- /dynamic-form-row -->
|
|
|
|
<div class="alert col-md-8 col-md-offset-2" style="text-align:left"
|
|
:class="alertClass"
|
|
v-if="statusText">
|
|
{{ this.statusText }}
|
|
</div><!-- /alert -->
|
|
</div> <!-- /div row -->
|
|
|
|
<div class="row">
|
|
<div class="col-md-12" style="padding-top: 30px;">
|
|
<div class="col-md-4 text-right"><h4>Header Field</h4></div>
|
|
<div class="col-md-4"><h4>Import Field</h4></div>
|
|
<div class="col-md-4"><h4>Sample Value</h4></div>
|
|
</div>
|
|
</div><!-- /div row -->
|
|
|
|
<template v-for="(header, index) in file.header_row">
|
|
<div class="row">
|
|
<div class="col-md-12">
|
|
<div class="col-md-4 text-right">
|
|
<label :for="header" class="control-label">{{ header }}</label>
|
|
</div>
|
|
<div class="col-md-4 form-group">
|
|
<div required>
|
|
<select2 :options="columns" v-model="columnMappings[header]">
|
|
<option value="0">Do Not Import</option>
|
|
</select2>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<p class="form-control-static">{{ activeFile.first_row[index] }}</p>
|
|
</div>
|
|
</div><!-- /div col-md-8 -->
|
|
</div><!-- /div row -->
|
|
</template>
|
|
|
|
<div class="row">
|
|
<div class="col-md-6 col-md-offset-2 text-right" style="padding-top: 20px;">
|
|
<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>
|
|
<br><br>
|
|
</div>
|
|
</div><!-- /div row -->
|
|
<div class="row">
|
|
<div class="alert col-md-8 col-md-offset-2" style="padding-top: 20px;"
|
|
:class="alertClass"
|
|
v-if="statusText">
|
|
{{ this.statusText }}
|
|
</div>
|
|
</div><!-- /div row -->
|
|
|
|
</div><!-- /div v-show -->
|
|
|
|
</td>
|
|
</tr>
|
|
</template>
|
|
|
|
<script>
|
|
var baseUrl = $('meta[name="baseUrl"]').attr('content');
|
|
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: 'email', text: 'Email' },
|
|
{id: 'item_name', text: 'Item Name' },
|
|
{id: 'location', text: 'Location' },
|
|
{id: 'maintained', text: 'Maintained' },
|
|
{id: 'manufacturer', text: 'Manufacturer' },
|
|
{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' },
|
|
{id: 'department', text: 'Department' },
|
|
],
|
|
accessories:[
|
|
{id: 'model_number', text: 'Model Number'},
|
|
{id: 'notes', text: 'Notes' },
|
|
],
|
|
assets: [
|
|
{id: 'asset_tag', text: 'Asset Tag' },
|
|
{id: 'asset_model', text: 'Model Name' },
|
|
{id: 'asset_notes', text: 'Asset Notes' },
|
|
{id: 'model_notes', text: 'Model Notes' },
|
|
{id: 'checkout_class', text: 'Checkout Type' },
|
|
{id: 'checkout_location', text: 'Checkout Location' },
|
|
{id: 'image', text: 'Image Filename' },
|
|
{id: 'model_number', text: 'Model Number' },
|
|
{id: 'asset_notes', text: 'Asset Notes' },
|
|
{id: 'model_notes', text: 'Model Notes' },
|
|
{id: 'full_name', text: 'Full Name' },
|
|
{id: 'status', text: 'Status' },
|
|
{id: 'warranty_months', text: 'Warranty Months' },
|
|
{id: 'last_audit_date', text: 'Last Audit Date' },
|
|
{id: 'next_audit_date', text: 'Audit Date' },
|
|
],
|
|
consumables: [
|
|
{id: 'item_no', text: "Item Number"},
|
|
{id: 'model_number', text: "Model Number"},
|
|
{id: 'min_amt', text: "Minimum Quantity"},
|
|
{id: 'notes', text: 'Notes' },
|
|
],
|
|
licenses: [
|
|
{id: 'asset_tag', text: 'Assigned To Asset'},
|
|
{id: 'expiration_date', text: 'Expiration Date' },
|
|
{id: 'full_name', text: 'Full Name' },
|
|
{id: 'notes', text: 'Notes' },
|
|
{id: 'license_email', text: 'Licensed To Email' },
|
|
{id: 'license_name', text: 'Licensed To Name' },
|
|
{id: 'notes', text: 'Notes' },
|
|
{id: 'purchase_order', text: 'Purchase Order' },
|
|
{id: 'reassignable', text: 'Reassignable' },
|
|
{id: 'seats', text: 'Seats' },
|
|
],
|
|
users: [
|
|
{id: 'employee_num', text: 'Employee Number' },
|
|
{id: 'first_name', text: 'First Name' },
|
|
{id: 'jobtitle', text: 'Job Title' },
|
|
{id: 'last_name', text: 'Last Name' },
|
|
{id: 'phone_number', text: 'Phone Number' },
|
|
{id: 'manager_first_name', text: 'Manager First Name' },
|
|
{id: 'notes', text: 'Notes' },
|
|
{id: 'manager_last_name', text: 'Manager Last Name' },
|
|
{id: 'notes', text: 'Notes' },
|
|
{id: 'activated', text: 'Activated' },
|
|
{id: 'address', text: 'Address' },
|
|
{id: 'city', text: 'City' },
|
|
{id: 'state', text: 'State' },
|
|
{id: 'zip', text: 'ZIP' },
|
|
{id: 'country', text: 'Country' },
|
|
{id: 'zip', text: 'ZIP' },
|
|
|
|
],
|
|
customFields: this.customFields,
|
|
},
|
|
columnMappings: this.file.field_map || {},
|
|
activeColumn: null,
|
|
}
|
|
},
|
|
created() {
|
|
window.eventHub.$on('showDetails', this.toggleExtendedDisplay)
|
|
this.populateSelect2ActiveItems();
|
|
},
|
|
computed: {
|
|
columns() {
|
|
// function to sort objects by their display text.
|
|
function sorter(a,b) {
|
|
if (a.text < b.text)
|
|
return -1;
|
|
if (a.text > b.text)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
switch(this.options.importType) {
|
|
case 'asset':
|
|
return this.columnOptions.general
|
|
.concat(this.columnOptions.assets)
|
|
.concat(this.columnOptions.customFields)
|
|
.sort(sorter);
|
|
case 'accessory':
|
|
return this.columnOptions.general
|
|
.concat(this.columnOptions.accessories)
|
|
.sort(sorter);
|
|
case 'consumable':
|
|
console.log('Returned consumable');
|
|
return this.columnOptions.general
|
|
.concat(this.columnOptions.consumables)
|
|
.sort(sorter);
|
|
case 'license':
|
|
return this.columnOptions.general.concat(this.columnOptions.licenses).sort(sorter);
|
|
case 'user':
|
|
return this.columnOptions.general.concat(this.columnOptions.users).sort(sorter);
|
|
}
|
|
return this.columnOptions.general;
|
|
},
|
|
alertClass() {
|
|
if(this.statusType=='success') {
|
|
return 'alert-success';
|
|
}
|
|
if(this.statusType=='error') {
|
|
return 'alert-danger';
|
|
}
|
|
return 'alert-info';
|
|
},
|
|
},
|
|
watch: {
|
|
columns() {
|
|
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(baseUrl + 'api/v1/imports/process/' + this.file.id, {
|
|
'import-update': this.options.update,
|
|
'send-welcome': this.options.send_welcome,
|
|
'import-type': this.options.importType,
|
|
'run-backup': this.options.run_backup,
|
|
'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());
|
|
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) {
|
|
this.columnMappings[header] = value;
|
|
}
|
|
},
|
|
components: {
|
|
select2: require('../select2.vue').default
|
|
}
|
|
}
|
|
</script>
|