mirror of
https://github.com/snipe/snipe-it.git
synced 2024-12-25 13:44:06 -08:00
Getting the basic wiring of the importer over into Livewire
WIP: Wiring up more and more of the actions on the importer Files now upload okay, a little glitchy on the display-side though add to readmes so i dont forget
This commit is contained in:
parent
23ca124e94
commit
0a085af0a0
11
FIXME.txt
Normal file
11
FIXME.txt
Normal file
|
@ -0,0 +1,11 @@
|
|||
remove Ziggy
|
||||
and ziggy-js too
|
||||
And what is /public/js/snipeit.js ? That looks like a generated file
|
||||
|
||||
The 'flash' (forced refresh/fake refresh) on uploads is dumb
|
||||
I'm not sure if the order on the uploaded files is right?
|
||||
The Livewire.first() thing is still dumb (but Id o'nt know that we can fix it).
|
||||
|
||||
Deletes need to work (I got this working before using $.ajax; it's not even hard)
|
||||
|
||||
Then mapping and so on.
|
|
@ -15,8 +15,8 @@ class ImportsController extends Controller
|
|||
public function index()
|
||||
{
|
||||
$this->authorize('import');
|
||||
$imports = (new ImportsTransformer)->transformImports(Import::latest()->get());
|
||||
// $imports = (new ImportsTransformer)->transformImports(Import::latest()->get());
|
||||
|
||||
return view('importer/import')->with('imports', $imports);
|
||||
return view('importer/import'); //->with('imports', $imports);
|
||||
}
|
||||
}
|
||||
|
|
45
app/Http/Livewire/Importer.php
Normal file
45
app/Http/Livewire/Importer.php
Normal file
|
@ -0,0 +1,45 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Livewire;
|
||||
|
||||
use Livewire\Component;
|
||||
|
||||
use App\Models\Import;
|
||||
|
||||
use Log;
|
||||
|
||||
class Importer extends Component
|
||||
{
|
||||
public $files;
|
||||
public $processDetails;
|
||||
public $forcerefresh;
|
||||
|
||||
protected $rules = [
|
||||
'files.*.file_path' => 'required|string',
|
||||
'files.*.created_at' => 'required|string',
|
||||
'files.*.filesize' => 'required|integer'
|
||||
];
|
||||
|
||||
public function mount()
|
||||
{
|
||||
//$this->files = Import::all(); // this *SHOULD* be how it works, but...it doesn't?
|
||||
$this->forcerefresh = 0;
|
||||
}
|
||||
|
||||
public function test()
|
||||
{
|
||||
Log::error("Test Button Clicked!!!!");
|
||||
}
|
||||
|
||||
public function toggleEvent($id)
|
||||
{
|
||||
Log::error("toggled on: ".$id);
|
||||
$this->processDetails = Import::find($id);
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
$this->files = Import::all(); //HACK - slows down renders.
|
||||
return view('livewire.importer');
|
||||
}
|
||||
}
|
151
app/Http/Livewire/ImporterFile.php
Normal file
151
app/Http/Livewire/ImporterFile.php
Normal file
|
@ -0,0 +1,151 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Livewire;
|
||||
|
||||
use Livewire\Component;
|
||||
|
||||
use App\Models\CustomField;
|
||||
|
||||
use Log;
|
||||
|
||||
global $general, $accessories, $assets, $consumables, $licenses, $users;
|
||||
|
||||
$general = [
|
||||
'category' => 'Category',
|
||||
'company' => 'Company',
|
||||
'email' => 'Email',
|
||||
'item_name' => 'Item Name',
|
||||
'location' => 'Location',
|
||||
'maintained' => 'Maintained',
|
||||
'manufacturer' => 'Manufacturer',
|
||||
'notes' => 'Notes',
|
||||
'order_number' => 'Order Number',
|
||||
'purchase_cost' => 'Purchase Cost',
|
||||
'purchase_date' => 'Purchase Date',
|
||||
'quantity' => 'Quantity',
|
||||
'requestable' => 'Requestable',
|
||||
'serial' => 'Serial Number',
|
||||
'supplier' => 'Supplier',
|
||||
'username' => 'Username',
|
||||
'department' => 'Department',
|
||||
];
|
||||
|
||||
$accessories = [
|
||||
'model_number' => 'Model Number',
|
||||
];
|
||||
|
||||
$assets = [
|
||||
'asset_tag' => 'Asset Tag',
|
||||
'asset_model' => 'Model Name',
|
||||
'checkout_class' => 'Checkout Type',
|
||||
'checkout_location' => 'Checkout Location',
|
||||
'image' => 'Image Filename',
|
||||
'model_number' => 'Model Number',
|
||||
'full_name' => 'Full Name',
|
||||
'status' => 'Status',
|
||||
'warranty_months' => 'Warranty Months',
|
||||
];
|
||||
|
||||
$consumables = [
|
||||
'item_no' => "Item Number",
|
||||
'model_number' => "Model Number",
|
||||
'min_amt' => "Minimum Quantity",
|
||||
];
|
||||
|
||||
$licenses = [
|
||||
'asset_tag' => 'Assigned To Asset',
|
||||
'expiration_date' => 'Expiration Date',
|
||||
'full_name' => 'Full Name',
|
||||
'license_email' => 'Licensed To Email',
|
||||
'license_name' => 'Licensed To Name',
|
||||
'purchase_order' => 'Purchase Order',
|
||||
'reassignable' => 'Reassignable',
|
||||
'seats' => 'Seats',
|
||||
];
|
||||
|
||||
$users = [
|
||||
'employee_num' => 'Employee Number',
|
||||
'first_name' => 'First Name',
|
||||
'jobtitle' => 'Job Title',
|
||||
'last_name' => 'Last Name',
|
||||
'phone_number' => 'Phone Number',
|
||||
'manager_first_name' => 'Manager First Name',
|
||||
'manager_last_name' => 'Manager Last Name',
|
||||
'activated' => 'Activated',
|
||||
'address' => 'Address',
|
||||
'city' => 'City',
|
||||
'state' => 'State',
|
||||
'country' => 'Country',
|
||||
];
|
||||
|
||||
class ImporterFile extends Component
|
||||
{
|
||||
public $activeFile; //should this get auto-filled?
|
||||
public $customFields;
|
||||
public $importTypes;
|
||||
public $columnOptions;
|
||||
public $importType; // too similar to 'TypeS'?
|
||||
|
||||
private function getColumns($type)
|
||||
{
|
||||
global $general, $accessories, $assets, $consumables, $licenses, $users;
|
||||
|
||||
$customFields = [];
|
||||
foreach($this->customFields AS $field) {
|
||||
$customFields[$field->id] = $field->name;
|
||||
}
|
||||
|
||||
switch($type) {
|
||||
case 'asset':
|
||||
$results = $general + $assets + $customFields;
|
||||
break;
|
||||
case 'accessory':
|
||||
$results = $general + $accessories;
|
||||
break;
|
||||
case 'consumable':
|
||||
$results = $general + $consumables;
|
||||
break;
|
||||
case 'license':
|
||||
$results = $general + $licenses;
|
||||
break;
|
||||
case 'user':
|
||||
$results = $general + $users;
|
||||
break;
|
||||
default:
|
||||
$results = $general;
|
||||
}
|
||||
asort($results); // FIXME - this isn't sorting right yet.
|
||||
return $results;
|
||||
}
|
||||
|
||||
public function mount()
|
||||
{
|
||||
$this->customFields = CustomField::all();
|
||||
|
||||
$this->importTypes = [
|
||||
'asset' => 'Assets', // TODO - translate!
|
||||
'accessory' => 'Accessories',
|
||||
'consumable' => 'Consumables',
|
||||
'component' => 'Components',
|
||||
'license' => 'Licenses',
|
||||
'user' => 'Users'
|
||||
];
|
||||
Log::error("import types: ".print_r($this->importTypes,true));
|
||||
|
||||
$columnOptions = [];
|
||||
$this->columnOptions[''] = $this->getColumns(''); //blank mode? I don't know what this is supposed to mean
|
||||
foreach($this->importTypes AS $type => $name) {
|
||||
$this->columnOptions[$type] = $this->getColumns($type);
|
||||
}
|
||||
}
|
||||
|
||||
public function changeTypes()
|
||||
{
|
||||
Log::error("type changed!");
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.importer-file');
|
||||
}
|
||||
}
|
|
@ -217,7 +217,7 @@ class Setting extends Model
|
|||
*
|
||||
* @author Mogilev Arseny
|
||||
*/
|
||||
public static function fileSizeConvert($bytes): string
|
||||
public static function fileSizeConvert(int $bytes): string
|
||||
{
|
||||
$result = 0;
|
||||
$bytes = floatval($bytes);
|
||||
|
@ -244,6 +244,7 @@ class Setting extends Model
|
|||
],
|
||||
];
|
||||
|
||||
$result = $bytes; // handles the zero case
|
||||
foreach ($arBytes as $arItem) {
|
||||
if ($bytes >= $arItem['VALUE']) {
|
||||
$result = $bytes / $arItem['VALUE'];
|
||||
|
|
|
@ -1,36 +0,0 @@
|
|||
|
||||
<style scoped>
|
||||
</style>
|
||||
|
||||
<template>
|
||||
<div class="col-md-12" :class="alertType">
|
||||
<div class="alert" :class="alertClassName">
|
||||
<button type="button" class="close" @click="hideEvent">×</button>
|
||||
<i class="fas fa-check faa-pulse animated" aria-hidden="true" v-show="alertType == 'success'"></i>
|
||||
<strong>{{ title }} </strong>
|
||||
<slot></slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
/*
|
||||
* The component's data.
|
||||
*/
|
||||
props: ['alertType', 'title'],
|
||||
|
||||
computed: {
|
||||
alertClassName() {
|
||||
return 'alert-' + this.alertType;
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
hideEvent() {
|
||||
this.$emit('hide');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
|
@ -1,42 +0,0 @@
|
|||
|
||||
<style scoped>
|
||||
</style>
|
||||
|
||||
<template>
|
||||
<div class="box" v-if="errors">
|
||||
<div class="box-body">
|
||||
<div class="alert alert-warning">
|
||||
<strong>Warning</strong> Some Errors occured while importing
|
||||
</div>
|
||||
|
||||
<div class="errors-table">
|
||||
<table class="table table-striped table-bordered" id="errors-table">
|
||||
<thead>
|
||||
<th>Item</th>
|
||||
<th>Errors</th>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="(error, item) in errors">
|
||||
<td>{{ item }}</td>
|
||||
<td v-for="(value, field) in error">
|
||||
<b>{{ field }}:</b>
|
||||
<span v-for="errorString in value">{{errorString[0]}}</span>
|
||||
<br />
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
/*
|
||||
* The component's data.
|
||||
*/
|
||||
props: ['errors'],
|
||||
}
|
||||
|
||||
</script>
|
|
@ -1,325 +0,0 @@
|
|||
<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="icheckbox_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="icheckbox_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="icheckbox_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,
|
||||
send_welcome: false,
|
||||
run_backup: 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: '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: '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: 'activated', text: 'Activated' },
|
||||
{id: 'address', text: 'Address' },
|
||||
{id: 'city', text: 'City' },
|
||||
{id: 'state', text: 'State' },
|
||||
{id: 'country', text: 'Country' },
|
||||
{id: 'zip', text: 'ZIP' },
|
||||
{id: 'remote', text: 'Remote'},
|
||||
|
||||
],
|
||||
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>
|
|
@ -1,130 +0,0 @@
|
|||
|
||||
|
||||
<script>
|
||||
require('blueimp-file-upload');
|
||||
var baseUrl = $('meta[name="baseUrl"]').attr('content');
|
||||
export default {
|
||||
/*
|
||||
* The component's data.
|
||||
*/
|
||||
data() {
|
||||
return {
|
||||
files: [],
|
||||
displayImportModal: false,
|
||||
activeFile: null,
|
||||
alert: {
|
||||
type: null,
|
||||
message: null,
|
||||
visible: false,
|
||||
},
|
||||
importErrors: null,
|
||||
progress: {
|
||||
currentClass: "progress-bar-warning",
|
||||
currentPercent: "0",
|
||||
statusText: '',
|
||||
visible: false
|
||||
},
|
||||
customFields: [],
|
||||
};
|
||||
},
|
||||
|
||||
mounted() {
|
||||
window.eventHub.$on('importErrors', this.updateImportErrors);
|
||||
this.fetchFiles();
|
||||
this.fetchCustomFields();
|
||||
let vm = this;
|
||||
$('#fileupload').fileupload({
|
||||
dataType: 'json',
|
||||
done(e, data) {
|
||||
vm.progress.currentClass="progress-bar-success";
|
||||
vm.progress.statusText = "Success!";
|
||||
vm.files = data.result.files.concat(vm.files);
|
||||
console.log(data.result.header_row);
|
||||
},
|
||||
add(e, data) {
|
||||
data.headers = {
|
||||
"X-Requested-With": 'XMLHttpRequest',
|
||||
"X-CSRF-TOKEN": Laravel.csrfToken
|
||||
};
|
||||
data.process().done( () => {data.submit();});
|
||||
vm.progress.visible=true;
|
||||
},
|
||||
progress(e, data) {
|
||||
var progress = parseInt((data.loaded / data.total * 100, 10));
|
||||
vm.progress.currentPercent = progress;
|
||||
vm.progress.statusText = progress+'% Complete';
|
||||
},
|
||||
fail(e, data) {
|
||||
vm.progress.currentClass = "progress-bar-danger";
|
||||
// Display any errors returned from the $.ajax()
|
||||
vm.progress.statusText = data.jqXHR.responseJSON.messages;
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
methods: {
|
||||
fetchFiles() {
|
||||
this.$http.get(baseUrl + 'api/v1/imports')
|
||||
.then( ({data}) => this.files = data, // Success
|
||||
//Fail
|
||||
(response) => {
|
||||
this.alert.type="danger";
|
||||
this.alert.visible=true;
|
||||
this.alert.message="Something went wrong fetching files...";
|
||||
});
|
||||
},
|
||||
fetchCustomFields() {
|
||||
this.$http.get(baseUrl + 'api/v1/fields')
|
||||
.then( ({data}) => {
|
||||
data = data.rows;
|
||||
data.forEach((item) => {
|
||||
this.customFields.push({
|
||||
'id': item.db_column_name,
|
||||
'text': item.name,
|
||||
})
|
||||
})
|
||||
});
|
||||
},
|
||||
deleteFile(file, key) {
|
||||
this.$http.delete(baseUrl + 'api/v1/imports/' + file.id)
|
||||
.then(
|
||||
// Success, remove file from array.
|
||||
(response) => {
|
||||
this.files.splice(key, 1);
|
||||
this.alert.type = response.body.status; // A failed delete can still cause a 200 status code.
|
||||
this.alert.visible = true;
|
||||
this.alert.message = response.body.messages;
|
||||
},
|
||||
(response) => {// Fail
|
||||
// this.files.splice(key, 1);
|
||||
this.alert.type="error";
|
||||
this.alert.visible=true;
|
||||
this.alert.message=response.body.messages;
|
||||
}
|
||||
);
|
||||
},
|
||||
toggleEvent(fileId) {
|
||||
window.eventHub.$emit('showDetails', fileId)
|
||||
},
|
||||
updateAlert(alert) {
|
||||
this.alert = alert;
|
||||
},
|
||||
updateImportErrors(errors) {
|
||||
this.importErrors = errors;
|
||||
},
|
||||
},
|
||||
|
||||
computed: {
|
||||
progressWidth() {
|
||||
return "width: "+this.progress.currentPercent*10+'%';
|
||||
}
|
||||
},
|
||||
|
||||
components: {
|
||||
alert: require('../alert.vue').default,
|
||||
errors: require('./importer-errors.vue').default,
|
||||
importFile: require('./importer-file.vue').default,
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
|
@ -26,10 +26,11 @@ Vue.component(
|
|||
require('./components/passport/PersonalAccessTokens.vue').default
|
||||
);
|
||||
|
||||
Vue.component(
|
||||
'importer',
|
||||
require('./components/importer/importer.vue').default
|
||||
);
|
||||
// This component has been removed and replaced with a Livewire implementation
|
||||
// Vue.component(
|
||||
// 'importer',
|
||||
// require('./components/importer/importer.vue').default
|
||||
// );
|
||||
|
||||
// This component has been removed and replaced with a Livewire implementation
|
||||
// Vue.component(
|
||||
|
|
|
@ -9,112 +9,20 @@
|
|||
{{-- Page content --}}
|
||||
@section('content')
|
||||
{{-- Hide importer until vue has rendered it, if we continue using vue for other things we should move this higher in the style --}}
|
||||
<style>
|
||||
{{-- <style>
|
||||
[v-cloak] {
|
||||
display:none;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<div id="app">
|
||||
<importer inline-template v-cloak>
|
||||
<div class="row">
|
||||
<alert v-show="alert.visible" :alert-type="alert.type" v-on:hide="alert.visible = false">@{{ alert.message }}</alert>
|
||||
<errors :errors="importErrors"></errors>
|
||||
|
||||
<div class="col-md-9">
|
||||
<div class="box">
|
||||
<div class="box-body">
|
||||
<div class="row">
|
||||
|
||||
<div class="col-md-12">
|
||||
|
||||
<div class="col-md-9" v-show="progress.visible" style="padding-bottom:20px">
|
||||
<div class="progress progress-striped-active" style="margin-top: 8px">
|
||||
<div class="progress-bar" :class="progress.currentClass" role="progressbar" :style="progressWidth">
|
||||
<span>@{{ progress.statusText }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3 text-right pull-right">
|
||||
|
||||
<!-- The fileinput-button span is used to style the file input field as button -->
|
||||
@if (!config('app.lock_passwords'))
|
||||
<span class="btn btn-primary fileinput-button">
|
||||
<span>{{ trans('button.select_file') }}</span>
|
||||
<!-- The file input field used as target for the file upload widget -->
|
||||
<label for="files[]"><span class="sr-only">{{ trans('button.select_file') }}</span></label>
|
||||
<input id="fileupload" type="file" name="files[]" data-url="{{ route('api.imports.index') }}" accept="text/csv" aria-label="files[]">
|
||||
</span>
|
||||
@endif
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-12 table-responsive" style="padding-top: 30px;">
|
||||
|
||||
<table data-pagination="true"
|
||||
data-id-table="upload-table"
|
||||
data-search="true"
|
||||
data-side-pagination="client"
|
||||
id="upload-table"
|
||||
class="col-md-12 table table-striped snipe-table">
|
||||
|
||||
<tr>
|
||||
<th class="col-md-6">{{ trans('general.file_name') }}</th>
|
||||
<th class="col-md-3">{{ trans('general.created_at') }}</th>
|
||||
<th class="col-md-1">{{ trans('general.filesize') }}</th>
|
||||
<th class="col-md-1 text-right"><span class="sr-only">{{ trans('general.actions') }}</span></th>
|
||||
</tr>
|
||||
|
||||
<template v-for="(currentFile, index) in files">
|
||||
<tr>
|
||||
<td class="col-md-6">@{{ currentFile.file_path }}</td>
|
||||
<td class="col-md-3">@{{ currentFile.created_at }} </td>
|
||||
<td class="col-md-1">@{{ currentFile.filesize }}</td>
|
||||
<td class="col-md-1 text-right">
|
||||
<button class="btn btn-sm btn-info" @click="toggleEvent(currentFile.id)">
|
||||
<i class="fas fa-retweet fa-fw" aria-hidden="true"></i>
|
||||
<span class="sr-only">{{ trans('general.import') }}</span>
|
||||
</button>
|
||||
|
||||
<button class="btn btn-sm btn-danger" @click="deleteFile(currentFile, index)">
|
||||
<i class="fas fa-trash icon-white" aria-hidden="true"></i><span class="sr-only"></span></button>
|
||||
</td>
|
||||
</tr>
|
||||
<import-file
|
||||
:key="currentFile.id"
|
||||
:file="currentFile"
|
||||
:custom-fields="customFields"
|
||||
@alert="updateAlert(alert)">
|
||||
</import-file>
|
||||
</template>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<h2>{{ trans('general.importing') }}</h2>
|
||||
<p>{!! trans('general.importing_help') !!}</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</importer>
|
||||
</div>
|
||||
THIS IS VUE STUFF ISNT IT?
|
||||
</style> --}}
|
||||
@livewire('importer') {{-- Yes, this is stupid - we should be able to route straight over and not have this, but Livewire doesn't work in this app that way :/ --}}
|
||||
@stop
|
||||
|
||||
@section('moar_scripts')
|
||||
<script nonce="{{ csrf_token() }}">
|
||||
{{-- <script nonce="{{ csrf_token() }}">
|
||||
new Vue({
|
||||
el: '#app'
|
||||
});
|
||||
</script>
|
||||
</script> --}}
|
||||
@endsection
|
||||
|
|
207
resources/views/livewire/importer-file.blade.php
Normal file
207
resources/views/livewire/importer-file.blade.php
Normal file
|
@ -0,0 +1,207 @@
|
|||
{{-- <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">
|
||||
{{ Form::select('importType', $importTypes, 0 /* FIXME whats' the old value? */, ['placeholder' => '', 'wire:model' => 'importType', 'wire:change' => 'changeTypes']) }}
|
||||
{{-- <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"> --}}
|
||||
@if($activeFile->header_row)
|
||||
@foreach($activeFile->header_row AS $index => $header)
|
||||
<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> --}}
|
||||
{{ Form::select('something', $columnOptions[$importType], null /* FIXME whats' the old value? */,['placeholder' => 'Do Not Import']) }}
|
||||
</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 -->
|
||||
@endforeach
|
||||
@else
|
||||
No Columns Found!
|
||||
@endif
|
||||
{{-- </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>
|
||||
|
||||
<script>
|
||||
unused_var_thing = {
|
||||
data() {
|
||||
return {
|
||||
activeFile: this.file,
|
||||
processDetail: false,
|
||||
statusText: null,
|
||||
statusType: null,
|
||||
options: {
|
||||
importType: this.file.import_type,
|
||||
update: false,
|
||||
statusText: null,
|
||||
},
|
||||
|
||||
activeColumn: null,
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.populateSelect2ActiveItems();
|
||||
},
|
||||
computed: {
|
||||
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(route('api.imports.importFile', 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
updateModel(header, value) {
|
||||
this.columnMappings[header] = value;
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
</tr>
|
||||
{{-- </template> --}}
|
212
resources/views/livewire/importer.blade.php
Normal file
212
resources/views/livewire/importer.blade.php
Normal file
|
@ -0,0 +1,212 @@
|
|||
<div id="not-app">
|
||||
{{-- <importer inline-template v-cloak> --}} {{-- like, this, here, that's a literal Vue directive --}}
|
||||
<div class="row">
|
||||
{{-- <alert v-show="alert.visible" :alert-type="alert.type" v-on:hide="alert.visible = false">@{{ alert.message }}</alert> --}}
|
||||
<template>
|
||||
<div class="box" v-if="errors">
|
||||
<div class="box-body">
|
||||
<div class="alert alert-warning">
|
||||
<strong>Warning</strong> Some Errors occured while importing
|
||||
</div>
|
||||
|
||||
<div class="errors-table">
|
||||
<table class="table table-striped table-bordered" id="errors-table">
|
||||
<thead>
|
||||
<th>Item</th>
|
||||
<th>Errors</th>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="(error, item) in errors">
|
||||
<td>{{-- item --}}</td>
|
||||
<td v-for="(value, field) in error">
|
||||
<b>{{-- field --}}:</b>
|
||||
<span v-for="errorString in value">{{-- errorString[0] --}}</span>
|
||||
<br />
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
{{-- alert --}}
|
||||
<template>
|
||||
<div class="col-md-12" :class="alertType">
|
||||
<div class="alert" :class="alertClassName">
|
||||
<button type="button" class="close" @click="hideEvent">×</button>
|
||||
<i class="fas fa-check faa-pulse animated" aria-hidden="true" v-show="alertType == 'success'"></i>
|
||||
<strong>{{-- title --}} </strong>
|
||||
<slot></slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
fixme = {
|
||||
/*
|
||||
* The component's data.
|
||||
*/
|
||||
props: ['alertType', 'title'],
|
||||
|
||||
computed: {
|
||||
alertClassName() {
|
||||
return 'alert-' + this.alertType;
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
hideEvent() {
|
||||
this.$emit('hide');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
{{-- errors thing that's built-in maybe? --}}
|
||||
{{-- <errors :errors="importErrors"></errors> --}}
|
||||
|
||||
<div class="col-md-9">
|
||||
<div class="box">
|
||||
<div class="box-body">
|
||||
<div class="row">
|
||||
|
||||
<div class="col-md-12">
|
||||
|
||||
<div class="col-md-9" style="padding-bottom:20px; display:none" id='progress-container'>
|
||||
<div class="progress progress-striped-active" style="margin-top: 8px"> {{-- so someof these values are in importer.vue! --}}
|
||||
<div id='progress-bar' class="progress-bar" class="progress-bar-warning" role="progressbar" style="width: 0%">
|
||||
<span id='progress-text'></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3 text-right pull-right">
|
||||
|
||||
<!-- The fileinput-button span is used to style the file input field as button -->
|
||||
@if (!config('app.lock_passwords'))
|
||||
<span class="btn btn-primary fileinput-button">
|
||||
<span>{{ trans('admin/importer/general.select_import_file') }}</span>
|
||||
<!-- The file input field used as target for the file upload widget -->
|
||||
<label for="files[]"><span class="sr-only">{{ trans('admin/importer/general.select_file') }}</span></label>
|
||||
<input id="fileupload" type="file" name="files[]" data-url="{{ route('api.imports.index') }}" accept="text/csv" aria-label="files[]">
|
||||
</span>
|
||||
@endif
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-12 table-responsive" style="padding-top: 30px;">
|
||||
<button wire:click="test">Test!</button><br />
|
||||
<table data-pagination="true"
|
||||
data-id-table="upload-table"
|
||||
data-search="true"
|
||||
data-side-pagination="client"
|
||||
id="upload-table"
|
||||
class="col-md-12 table table-striped snipe-table">
|
||||
|
||||
<tr>
|
||||
<th class="col-md-6">{{ trans('admin/importer/table.file') }}</th>
|
||||
<th class="col-md-3">{{ trans('admin/importer/table.created') }}</th>
|
||||
<th class="col-md-1">{{ trans('admin/importer/table.size') }}</th>
|
||||
<th class="col-md-1 text-right"><span class="sr-only">{{ trans('admin/importer/table.process') }}</span></th>
|
||||
<th class="col-md-1 text-right"><span class="sr-only">{{ trans('admin/importer/table.delete') }}</span></th>
|
||||
</tr>
|
||||
|
||||
{{-- <template v-for="currentFile in files"> --}}
|
||||
@foreach($files as $currentFile)
|
||||
<?php
|
||||
\Log::error(print_r($currentFile,true))
|
||||
?>
|
||||
<tr>
|
||||
<td class="col-md-6">{{ $currentFile->file_path }}</td>
|
||||
<td class="col-md-3">{{ $currentFile->created_at }} </td>
|
||||
<td class="col-md-1">{{ $currentFile->filesize }}</td>
|
||||
<td class="col-md-1 text-right">
|
||||
<button class="btn btn-sm btn-info" wire:click="toggleEvent({{ $currentFile->id }})">
|
||||
{{ trans('admin/importer/button.process') }}
|
||||
</button>
|
||||
</td>
|
||||
<td class="col-md-1 text-right">
|
||||
<button class="btn btn-sm btn-danger" @click="deleteFile(currentFile)">
|
||||
<i class="fas fa-trash icon-white" aria-hidden="true"></i><span class="sr-only"></span></button>
|
||||
</td>
|
||||
</tr>
|
||||
@if( $currentFile && $processDetails && ($currentFile->id == $processDetails->id))
|
||||
@livewire('importer-file', ['activeFile' => $currentFile])
|
||||
{{-- <import-file
|
||||
:key="currentFile.id"
|
||||
:file="currentFile"
|
||||
:custom-fields="customFields"
|
||||
@alert="updateAlert(alert)">
|
||||
</import-file> --}}
|
||||
@endif
|
||||
@endforeach
|
||||
{{-- </template> --}}
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<h2>{{ trans('general.importing') }}</h2>
|
||||
<p>{!! trans('general.importing_help') !!}</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
{{-- </importer> --}}
|
||||
</div>
|
||||
@push('js')
|
||||
<script>
|
||||
document.addEventListener('livewire:load', function () {
|
||||
console.log("OKAY - we are gonna dump us out some files here!")
|
||||
console.dir(Livewire.first().files)
|
||||
})
|
||||
|
||||
$('#fileupload').fileupload({
|
||||
dataType: 'json',
|
||||
done: function(e, data) {
|
||||
$('#progress-bar').attr("class", "progress-bar-success");
|
||||
$('#progress-text').text("Success!"); // same here? TODO - internationalize!
|
||||
$('#progress-bar').attr('style', 'width: 100%'); // weird, wasn't needed before....
|
||||
console.log("Dumping livewire files!!!!!!!!!")
|
||||
console.dir(Livewire.first().files)
|
||||
console.log("And now dumping data.result.files!!!!!")
|
||||
console.dir(data.result.files)
|
||||
//Livewire.first().files = data.result.files.concat(Livewire.first().files); // FIXME - how to get in and out of the Livewire.first().something.... (this doesn't work either)
|
||||
// Livewire.first().files = Livewire.first().files.concat(data.result.files) //I don't quite see why this should be like this, but, well, whatever.
|
||||
//fuckit, let's just force a refresh?
|
||||
Livewire.first().forcerefresh = Livewire.first().forcerefresh+1 // this is a horrible hack; please forgive me :(
|
||||
console.log(data.result.header_row);
|
||||
console.dir()
|
||||
},
|
||||
add: function(e, data) {
|
||||
data.headers = {
|
||||
"X-Requested-With": 'XMLHttpRequest',
|
||||
"X-CSRF-TOKEN": $('meta[name="csrf-token"]').attr('content')
|
||||
};
|
||||
data.process().done( function () {data.submit();});
|
||||
$('#progress-container').show();
|
||||
},
|
||||
progress: function(e, data) {
|
||||
var progress = parseInt((data.loaded / data.total * 100, 10));
|
||||
$('#progress-bar').attr('style', 'width: '+progress+'%');
|
||||
$('#progress-text').text(progress+'% Complete');
|
||||
},
|
||||
fail: function(e, data) {
|
||||
$('#progress-bar').attr("class", "progress-bar-danger");
|
||||
// Display any errors returned from the $.ajax()
|
||||
console.dir(data.jqXHR.responseJSON.messages) // FIXME don't dupm to console
|
||||
$('#progress-bar').attr('style', 'width: 100%');
|
||||
$('#progress-text').text(data.jqXHR.responseJSON.messages);
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@endpush
|
Loading…
Reference in a new issue