Add and fix nominee profile photo uploads
- Run artisan migrate to add column photo in nominee table - npm run development/watch
This commit is contained in:
@@ -43,12 +43,19 @@ class AddController extends Controller
|
||||
]);
|
||||
}
|
||||
|
||||
public function insertNominee ($request)
|
||||
public function insertNominee ($request)
|
||||
{
|
||||
$nominee = $request->all();
|
||||
$default_image = config('app.cloudinary_enabled') ? config('app.cloudinary_image_default') : config('app.nominee_image');
|
||||
$nominee['image'] = Util::getImagePath($request, config('app.nominee_directory'), $default_image);
|
||||
$nominee = $request->except(['photo']);
|
||||
$nominee['election_id'] = Util::getCurrentElection();
|
||||
|
||||
if ($request->hasFile('photo')) {
|
||||
$imageData = file_get_contents($request->file('photo'));
|
||||
$nominee['photo'] = $imageData;
|
||||
}
|
||||
|
||||
Nominee::create($nominee);
|
||||
return response()->json(['message' => 'Photo updated successfully']);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,11 +41,10 @@ class UpdateController extends Controller
|
||||
]);
|
||||
}
|
||||
|
||||
private function updateNominee ($request, $id)
|
||||
private function updateNominee ($request, $id)
|
||||
{
|
||||
$nominee = Nominee::find($id);
|
||||
$default_image = $nominee->image;
|
||||
|
||||
|
||||
$nominee->name = $request->name;
|
||||
$nominee->unit = $request->unit;
|
||||
$nominee->no_anggota = $request->no_anggota;
|
||||
@@ -53,11 +52,13 @@ class UpdateController extends Controller
|
||||
$nominee->partylist_id = $request->partylist_id;
|
||||
$nominee->motto = $request->motto;
|
||||
$nominee->description = $request->description;
|
||||
$nominee->image = Util::getImagePath($request, config('app.nominee_directory'), $default_image);
|
||||
$nominee->save();
|
||||
|
||||
if (!empty($request->image))
|
||||
Util::deleteImage($default_image);
|
||||
if ($request->hasFile('photo')) {
|
||||
$imageData = file_get_contents($request->file('photo'));
|
||||
$nominee->photo = $imageData;
|
||||
}
|
||||
|
||||
$nominee->save();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+15
-5
@@ -8,14 +8,24 @@ class Nominee extends Model
|
||||
{
|
||||
protected $table = 'nominee';
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'unit',
|
||||
'no_anggota',
|
||||
'name',
|
||||
'unit',
|
||||
'no_anggota',
|
||||
'position_id',
|
||||
'partylist_id',
|
||||
'election_id',
|
||||
'image',
|
||||
'description',
|
||||
'motto'
|
||||
'motto',
|
||||
'photo',
|
||||
];
|
||||
public $timestamp = false;
|
||||
|
||||
public function getPhotoAttribute($value)
|
||||
{
|
||||
if ($value) {
|
||||
return base64_encode($value);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class AddPhotoColumnToNomineeTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('nominee', function (Blueprint $table) {
|
||||
$table->binary('photo')->nullable();
|
||||
$table->dropColumn('image');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('nominee', function (Blueprint $table) {
|
||||
$table->dropColumn('photo');
|
||||
$table->string('image');
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -46,9 +46,12 @@
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div id="imagePreview">
|
||||
<img :src="imageUrl" v-if="imageUrl" alt="Preview">
|
||||
</div>
|
||||
<div>
|
||||
<label for="image">Masukkan Gambar</label>
|
||||
<input type="file" accept="image/*" @change="uploadImage($event)" id="file-input">
|
||||
<input name="photo" type="file" accept="image/*" id="file-input" @change="handleImageChange">
|
||||
</div>
|
||||
|
||||
|
||||
@@ -70,6 +73,7 @@
|
||||
export default {
|
||||
data: function () {
|
||||
return {
|
||||
imageUrl: '',
|
||||
loading: false
|
||||
}
|
||||
},
|
||||
@@ -88,6 +92,8 @@ export default {
|
||||
vm.$router.push({ name: 'Maklumat Calon' });
|
||||
},
|
||||
error: function (error) {
|
||||
alert('Nominee with the same name has already registered');
|
||||
location.reload();
|
||||
$.notifyClose();
|
||||
vm.loading = false;
|
||||
vm.util.showResult(error, 'error', 'ajax');
|
||||
@@ -98,30 +104,27 @@ export default {
|
||||
})
|
||||
},
|
||||
|
||||
uploadImage(event) {
|
||||
handleImageChange(event) {
|
||||
const file = event.target.files[0]; // Get the selected file
|
||||
const imageType = /image.*/; // RegExp to check if the file is an image
|
||||
|
||||
const URL = 'http://foobar.com/upload';
|
||||
// Check if the selected file is an image
|
||||
if (file && file.type.match(imageType)) {
|
||||
const reader = new FileReader(); // Create a FileReader object
|
||||
|
||||
let data = new FormData();
|
||||
data.append('name', 'my-picture');
|
||||
data.append('file', event.target.files[0]);
|
||||
reader.onload = (e) => {
|
||||
this.imageUrl = e.target.result; // Set the imageUrl to the data URL of the image
|
||||
document.getElementById('imagePreview').style.display = 'block'; // Display the div
|
||||
};
|
||||
|
||||
let config = {
|
||||
header: {
|
||||
'Content-Type': 'image/png'
|
||||
}
|
||||
}
|
||||
|
||||
axios.put(
|
||||
URL,
|
||||
data,
|
||||
config
|
||||
).then(
|
||||
response => {
|
||||
console.log('image upload response > ', response)
|
||||
}
|
||||
)
|
||||
}
|
||||
reader.readAsDataURL(file); // Read the image data as a data URL
|
||||
} else {
|
||||
// Clear the file input and hide the div if the selected file is not an image
|
||||
event.target.value = '';
|
||||
this.imageUrl = '';
|
||||
document.getElementById('imagePreview').style.display = 'none';
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
@@ -136,4 +139,19 @@ export default {
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
#imagePreview {
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
border: 1px solid #ccc;
|
||||
margin-bottom: 10px;
|
||||
display: none; /* Initially hide the div */
|
||||
}
|
||||
|
||||
#imagePreview img {
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -10,12 +10,8 @@
|
||||
@submit.prevent="edit()">
|
||||
<input type="hidden" name="_method" value="PUT"/>
|
||||
|
||||
<div class="col-md-4">
|
||||
<uploader file-name="image" :image-src="data.storageURL+nominee.image"/>
|
||||
</div>
|
||||
|
||||
<div class="col-md-8">
|
||||
|
||||
|
||||
<div class="form-group">
|
||||
<label for="name">Nama</label>
|
||||
<input type="text" name="name" class="form-control" :value="nominee.name" required/>
|
||||
@@ -23,12 +19,12 @@
|
||||
|
||||
<div class="form-group">
|
||||
<label for="student_id">No. Anggota</label>
|
||||
<input type="text" name="student_id" class="form-control" :value="nominee.student_id" required/>
|
||||
<input type="text" name="no_anggota" class="form-control" :value="nominee.no_anggota" required/>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="Unit">Unit</label>
|
||||
<input type="text" name="unit" class="form-control" :value="nominee.Unit" required/>
|
||||
<input type="text" name="unit" class="form-control" :value="nominee.unit" required/>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
@@ -57,10 +53,18 @@
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div id="imagePreview">
|
||||
<img :src="imageUrl" v-if="imageUrl" alt="Preview">
|
||||
</div>
|
||||
<div>
|
||||
<label for="image">Masukkan Gambar</label>
|
||||
<input name="photo" type="file" accept="image/*" id="file-input" @change="handleImageChange">
|
||||
</div>
|
||||
|
||||
<div class="form-group pull-right">
|
||||
<input type="submit" value="Submit" class="btn btn-info">
|
||||
<router-link
|
||||
:to="{name:'Maklumat Calon'}"
|
||||
<router-link
|
||||
:to="{name:'Maklumat Calon'}"
|
||||
class="btn btn-default">
|
||||
Back
|
||||
</router-link>
|
||||
@@ -75,6 +79,7 @@
|
||||
export default{
|
||||
data: function () {
|
||||
return {
|
||||
imageUrl: '',
|
||||
loading: false
|
||||
}
|
||||
},
|
||||
@@ -84,7 +89,38 @@ export default{
|
||||
this.$router.push({name:'Maklumat Calon'});
|
||||
},
|
||||
|
||||
mounted: function () {
|
||||
if(this.nominee.id) {
|
||||
this.imageUrl = this.createBase64ImageUrl(this.nominee.photo);
|
||||
document.getElementById('imagePreview').style.display = 'block';
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
createBase64ImageUrl: function(base64ImageData) {
|
||||
return "data:image/png;base64," + base64ImageData;
|
||||
},
|
||||
handleImageChange(event) {
|
||||
const file = event.target.files[0]; // Get the selected file
|
||||
const imageType = /image.*/; // RegExp to check if the file is an image
|
||||
|
||||
// Check if the selected file is an image
|
||||
if (file && file.type.match(imageType)) {
|
||||
const reader = new FileReader(); // Create a FileReader object
|
||||
|
||||
reader.onload = (e) => {
|
||||
this.imageUrl = e.target.result; // Set the imageUrl to the data URL of the image
|
||||
document.getElementById('imagePreview').style.display = 'block'; // Display the div
|
||||
};
|
||||
|
||||
reader.readAsDataURL(file); // Read the image data as a data URL
|
||||
} else {
|
||||
// Clear the file input and hide the div if the selected file is not an image
|
||||
event.target.value = '';
|
||||
this.imageUrl = '';
|
||||
document.getElementById('imagePreview').style.display = 'none';
|
||||
}
|
||||
},
|
||||
edit: function () {
|
||||
if (this.loading) return;
|
||||
var vm = this;
|
||||
@@ -94,7 +130,7 @@ export default{
|
||||
success: function (response) {
|
||||
$.notifyClose();
|
||||
vm.loading = false;
|
||||
if (vm.util.showResult(response, 'success', 'ajax'))
|
||||
if (vm.util.showResult(response, 'success', 'ajax'))
|
||||
vm.$router.push({name: 'Maklumat Calon'});
|
||||
},
|
||||
error: function (error) {
|
||||
@@ -122,4 +158,19 @@ export default{
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
#imagePreview {
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
border: 1px solid #ccc;
|
||||
margin-bottom: 10px;
|
||||
display: none; /* Initially hide the div */
|
||||
}
|
||||
|
||||
#imagePreview img {
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -2,20 +2,20 @@
|
||||
<div class="row">
|
||||
<div class="col-md-3" style="max-width: 250px;">
|
||||
<ul class="list-group">
|
||||
<router-link
|
||||
tag="li"
|
||||
class="list-group-item"
|
||||
:to="{name: 'Maklumat Calon'}"
|
||||
exact
|
||||
<router-link
|
||||
tag="li"
|
||||
class="list-group-item"
|
||||
:to="{name: 'Maklumat Calon'}"
|
||||
exact
|
||||
replace
|
||||
:class="{'active':position_id==0}">
|
||||
All Position
|
||||
</router-link>
|
||||
<router-link
|
||||
<router-link
|
||||
v-for="position in data.positions"
|
||||
:key="position.id"
|
||||
tag="li"
|
||||
class="list-group-item"
|
||||
tag="li"
|
||||
class="list-group-item"
|
||||
:to="{query: {position_id:position.id}}"
|
||||
exact
|
||||
replace>
|
||||
@@ -43,7 +43,7 @@
|
||||
<tbody>
|
||||
<tr v-for="nominee in nominees">
|
||||
<td>
|
||||
<img :alt="nominee.name" :src="data.storageURL+nominee.image" class="thumbnail" style="height: 60px;width: 60px;">
|
||||
<img :alt="nominee.name" :src="createBase64ImageUrl(nominee.photo)" class="thumbnail" style="height: 60px;width: 60px;">
|
||||
</td>
|
||||
<td>{{ nominee.name}}</td>
|
||||
<td>{{ nominee.no_anggota}}</td>
|
||||
@@ -142,7 +142,11 @@ export default{
|
||||
if (partylists[i].id == id)
|
||||
return partylists[i]['name'];
|
||||
return 'No partylists';
|
||||
}
|
||||
},
|
||||
|
||||
createBase64ImageUrl: function(base64ImageData) {
|
||||
return "data:image/png;base64," + base64ImageData;
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
@@ -165,4 +169,4 @@ export default{
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</script>
|
||||
|
||||
@@ -49,10 +49,10 @@
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="(nominee,i) in data.nominees" v-if="nominee.position_id==position.id">
|
||||
<td><img
|
||||
<td><img
|
||||
@click="view(i)"
|
||||
:src="data.storageURL+nominee.image"
|
||||
class="thumbnail"
|
||||
:src="createBase64ImageUrl(nominee.photo)"
|
||||
class="thumbnail"
|
||||
style="height: 60px;width: 60px;" />
|
||||
</td>
|
||||
<td>{{ nominee.name }}</td>
|
||||
@@ -122,7 +122,7 @@ export default{
|
||||
|
||||
getNominee: function (id) {
|
||||
let nominees = this.data.nominees;
|
||||
for (var i in nominees)
|
||||
for (var i in nominees)
|
||||
if (nominees[i]['id']==id) return nominees[i].name;
|
||||
return '';
|
||||
},
|
||||
@@ -130,7 +130,12 @@ export default{
|
||||
view: function (index) {
|
||||
this.x = this.data.nominees[index];
|
||||
this.util.showModal('#nominee-information-modal');
|
||||
}
|
||||
},
|
||||
|
||||
createBase64ImageUrl: function(base64ImageData) {
|
||||
return "data:image/png;base64," + base64ImageData;
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
computed: {
|
||||
|
||||
Reference in New Issue
Block a user