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:
committed by
nurafrinaalimi16
parent
8bde7a0544
commit
1d892b064e
@@ -45,10 +45,17 @@ class AddController extends Controller
|
|||||||
|
|
||||||
public function insertNominee ($request)
|
public function insertNominee ($request)
|
||||||
{
|
{
|
||||||
$nominee = $request->all();
|
$nominee = $request->except(['photo']);
|
||||||
$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['election_id'] = Util::getCurrentElection();
|
$nominee['election_id'] = Util::getCurrentElection();
|
||||||
|
|
||||||
|
if ($request->hasFile('photo')) {
|
||||||
|
$imageData = file_get_contents($request->file('photo'));
|
||||||
|
$nominee['photo'] = $imageData;
|
||||||
|
}
|
||||||
|
|
||||||
Nominee::create($nominee);
|
Nominee::create($nominee);
|
||||||
|
return response()->json(['message' => 'Photo updated successfully']);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,7 +44,6 @@ class UpdateController extends Controller
|
|||||||
private function updateNominee ($request, $id)
|
private function updateNominee ($request, $id)
|
||||||
{
|
{
|
||||||
$nominee = Nominee::find($id);
|
$nominee = Nominee::find($id);
|
||||||
$default_image = $nominee->image;
|
|
||||||
|
|
||||||
$nominee->name = $request->name;
|
$nominee->name = $request->name;
|
||||||
$nominee->unit = $request->unit;
|
$nominee->unit = $request->unit;
|
||||||
@@ -53,11 +52,13 @@ class UpdateController extends Controller
|
|||||||
$nominee->partylist_id = $request->partylist_id;
|
$nominee->partylist_id = $request->partylist_id;
|
||||||
$nominee->motto = $request->motto;
|
$nominee->motto = $request->motto;
|
||||||
$nominee->description = $request->description;
|
$nominee->description = $request->description;
|
||||||
$nominee->image = Util::getImagePath($request, config('app.nominee_directory'), $default_image);
|
|
||||||
$nominee->save();
|
|
||||||
|
|
||||||
if (!empty($request->image))
|
if ($request->hasFile('photo')) {
|
||||||
Util::deleteImage($default_image);
|
$imageData = file_get_contents($request->file('photo'));
|
||||||
|
$nominee->photo = $imageData;
|
||||||
|
}
|
||||||
|
|
||||||
|
$nominee->save();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+12
-2
@@ -14,8 +14,18 @@ class Nominee extends Model
|
|||||||
'position_id',
|
'position_id',
|
||||||
'partylist_id',
|
'partylist_id',
|
||||||
'election_id',
|
'election_id',
|
||||||
'image',
|
|
||||||
'description',
|
'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>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div id="imagePreview">
|
||||||
|
<img :src="imageUrl" v-if="imageUrl" alt="Preview">
|
||||||
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label for="image">Masukkan Gambar</label>
|
<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>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
@@ -70,6 +73,7 @@
|
|||||||
export default {
|
export default {
|
||||||
data: function () {
|
data: function () {
|
||||||
return {
|
return {
|
||||||
|
imageUrl: '',
|
||||||
loading: false
|
loading: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -88,6 +92,8 @@ export default {
|
|||||||
vm.$router.push({ name: 'Maklumat Calon' });
|
vm.$router.push({ name: 'Maklumat Calon' });
|
||||||
},
|
},
|
||||||
error: function (error) {
|
error: function (error) {
|
||||||
|
alert('Nominee with the same name has already registered');
|
||||||
|
location.reload();
|
||||||
$.notifyClose();
|
$.notifyClose();
|
||||||
vm.loading = false;
|
vm.loading = false;
|
||||||
vm.util.showResult(error, 'error', 'ajax');
|
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();
|
reader.onload = (e) => {
|
||||||
data.append('name', 'my-picture');
|
this.imageUrl = e.target.result; // Set the imageUrl to the data URL of the image
|
||||||
data.append('file', event.target.files[0]);
|
document.getElementById('imagePreview').style.display = 'block'; // Display the div
|
||||||
|
};
|
||||||
|
|
||||||
let config = {
|
reader.readAsDataURL(file); // Read the image data as a data URL
|
||||||
header: {
|
} else {
|
||||||
'Content-Type': 'image/png'
|
// 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';
|
||||||
axios.put(
|
}
|
||||||
URL,
|
}
|
||||||
data,
|
|
||||||
config
|
|
||||||
).then(
|
|
||||||
response => {
|
|
||||||
console.log('image upload response > ', response)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
@@ -137,3 +140,18 @@ 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,10 +10,6 @@
|
|||||||
@submit.prevent="edit()">
|
@submit.prevent="edit()">
|
||||||
<input type="hidden" name="_method" value="PUT"/>
|
<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="col-md-8">
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
@@ -23,12 +19,12 @@
|
|||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="student_id">No. Anggota</label>
|
<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>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="Unit">Unit</label>
|
<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>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
@@ -57,6 +53,14 @@
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</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">
|
<div class="form-group pull-right">
|
||||||
<input type="submit" value="Submit" class="btn btn-info">
|
<input type="submit" value="Submit" class="btn btn-info">
|
||||||
<router-link
|
<router-link
|
||||||
@@ -75,6 +79,7 @@
|
|||||||
export default{
|
export default{
|
||||||
data: function () {
|
data: function () {
|
||||||
return {
|
return {
|
||||||
|
imageUrl: '',
|
||||||
loading: false
|
loading: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -84,7 +89,38 @@ export default{
|
|||||||
this.$router.push({name:'Maklumat Calon'});
|
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: {
|
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 () {
|
edit: function () {
|
||||||
if (this.loading) return;
|
if (this.loading) return;
|
||||||
var vm = this;
|
var vm = this;
|
||||||
@@ -123,3 +159,18 @@ 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>
|
||||||
|
|||||||
@@ -43,7 +43,7 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
<tr v-for="nominee in nominees">
|
<tr v-for="nominee in nominees">
|
||||||
<td>
|
<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>
|
||||||
<td>{{ nominee.name}}</td>
|
<td>{{ nominee.name}}</td>
|
||||||
<td>{{ nominee.no_anggota}}</td>
|
<td>{{ nominee.no_anggota}}</td>
|
||||||
@@ -142,7 +142,11 @@ export default{
|
|||||||
if (partylists[i].id == id)
|
if (partylists[i].id == id)
|
||||||
return partylists[i]['name'];
|
return partylists[i]['name'];
|
||||||
return 'No partylists';
|
return 'No partylists';
|
||||||
}
|
},
|
||||||
|
|
||||||
|
createBase64ImageUrl: function(base64ImageData) {
|
||||||
|
return "data:image/png;base64," + base64ImageData;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
|
|||||||
@@ -53,7 +53,7 @@
|
|||||||
<tr v-for="(nominee,i) in data.nominees" v-if="nominee.position_id==position.id">
|
<tr v-for="(nominee,i) in data.nominees" v-if="nominee.position_id==position.id">
|
||||||
<td><img
|
<td><img
|
||||||
@click="view(i)"
|
@click="view(i)"
|
||||||
:src="data.storageURL+nominee.image"
|
:src="createBase64ImageUrl(nominee.photo)"
|
||||||
class="thumbnail"
|
class="thumbnail"
|
||||||
style="height: 60px;width: 60px;" />
|
style="height: 60px;width: 60px;" />
|
||||||
</td>
|
</td>
|
||||||
@@ -132,7 +132,12 @@ export default{
|
|||||||
view: function (index) {
|
view: function (index) {
|
||||||
this.x = this.data.nominees[index];
|
this.x = this.data.nominees[index];
|
||||||
this.util.showModal('#nominee-information-modal');
|
this.util.showModal('#nominee-information-modal');
|
||||||
}
|
},
|
||||||
|
|
||||||
|
createBase64ImageUrl: function(base64ImageData) {
|
||||||
|
return "data:image/png;base64," + base64ImageData;
|
||||||
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
|
|||||||
Reference in New Issue
Block a user