Skip to content

Commit 363c714

Browse files
committed
Update vue and laravel code with latest changes
1 parent 411dcfc commit 363c714

File tree

18 files changed

+207
-211
lines changed

18 files changed

+207
-211
lines changed

app/Http/Controllers/Api/ProductController.php

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,11 @@ public function store(ProductRequest $request)
4848
$data['created_by'] = $request->user()->id;
4949
$data['updated_by'] = $request->user()->id;
5050

51-
/** @var \Illuminate\Http\UploadedFile $image */
51+
/** @var \Illuminate\Http\UploadedFile[] $images */
5252
$images = $data['images'] ?? [];
5353

5454
$product = Product::create($data);
55+
5556
$this->saveImages($images, $product);
5657

5758
return new ProductResource($product);
@@ -84,9 +85,10 @@ public function update(ProductRequest $request, Product $product)
8485
$images = $data['images'] ?? [];
8586
$deletedImages = $data['deleted_images'] ?? [];
8687

87-
// Check if image was given and save on local file system
8888
$this->saveImages($images, $product);
89-
$this->deleteImages($deletedImages, $product);
89+
if (count($deletedImages) > 0) {
90+
$this->deleteImages($deletedImages, $product);
91+
}
9092

9193
$product->update($data);
9294

@@ -106,18 +108,24 @@ public function destroy(Product $product)
106108
return response()->noContent();
107109
}
108110

109-
111+
/**
112+
*
113+
*
114+
* @param UploadedFile[] $images
115+
* @return string
116+
* @throws \Exception
117+
* @author Zura Sekhniashvili <[email protected]>
118+
*/
110119
private function saveImages($images, Product $product)
111120
{
112121
foreach ($images as $i => $image) {
113122
$path = 'images/' . Str::random();
114123
if (!Storage::exists($path)) {
115-
Storage::makeDirectory($path);
124+
Storage::makeDirectory($path, 0755, true);
116125
}
117126
if (!Storage::putFileAS('public/' . $path, $image, $image->getClientOriginalName())) {
118127
throw new \Exception("Unable to save file \"{$image->getClientOriginalName()}\"");
119128
}
120-
121129
$relativePath = $path . '/' . $image->getClientOriginalName();
122130

123131
ProductImage::create([
@@ -131,14 +139,15 @@ private function saveImages($images, Product $product)
131139
}
132140
}
133141

134-
public function deleteImages($imageIds, Product $product)
142+
private function deleteImages($imageIds, Product $product)
135143
{
136144
$images = ProductImage::query()
137145
->where('product_id', $product->id)
138146
->whereIn('id', $imageIds)
139147
->get();
140148

141149
foreach ($images as $image) {
150+
// If there is an old image, delete it
142151
if ($image->path) {
143152
Storage::deleteDirectory('/public/' . dirname($image->path));
144153
}

app/Http/Controllers/CartController.php

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,8 @@ public function add(Request $request, Product $product)
2727
$quantity = $request->post('quantity', 1);
2828
$user = $request->user();
2929

30-
$totalCartCount = 0;
3130
$totalQuantity = 0;
32-
$cartItem = null;
33-
// Validate quantity against product->quantity
31+
3432
if ($user) {
3533
$cartItem = CartItem::where(['user_id' => $user->id, 'product_id' => $product->id])->first();
3634
if ($cartItem) {
@@ -52,12 +50,13 @@ public function add(Request $request, Product $product)
5250
$totalQuantity = $quantity;
5351
}
5452
}
53+
5554
if ($product->quantity !== null && $product->quantity < $totalQuantity) {
5655
return response([
5756
'message' => match ( $product->quantity ) {
5857
0 => 'The product is out of stock',
59-
1 => 'There is only 1 item left',
60-
default => 'There are only ' . $product->quantity . ' items left',
58+
1 => 'There is only one item left',
59+
default => 'There are only ' . $product->quantity . ' items left'
6160
}
6261
], 422);
6362
}
@@ -134,18 +133,18 @@ public function remove(Request $request, Product $product)
134133
public function updateQuantity(Request $request, Product $product)
135134
{
136135
$quantity = (int)$request->post('quantity');
136+
$user = $request->user();
137137

138138
if ($product->quantity !== null && $product->quantity < $quantity) {
139139
return response([
140140
'message' => match ( $product->quantity ) {
141141
0 => 'The product is out of stock',
142-
1 => 'There is only 1 item left',
143-
default => 'There are only ' . $product->quantity . ' items left',
142+
1 => 'There is only one item left',
143+
default => 'There are only ' . $product->quantity . ' items left'
144144
}
145145
], 422);
146146
}
147147

148-
$user = $request->user();
149148
if ($user) {
150149
CartItem::where(['user_id' => $request->user()->id, 'product_id' => $product->id])->update(['quantity' => $quantity]);
151150

app/Http/Resources/ProductListResource.php

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@ class ProductListResource extends JsonResource
1616
*/
1717
public function toArray($request)
1818
{
19-
/** @var \Illuminate\Support\Collection $images */
20-
$images = $this->images;
2119
return [
2220
'id' => $this->id,
2321
'title' => $this->title,

app/Http/Resources/ProductResource.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,12 @@ class ProductResource extends JsonResource
1818
*/
1919
public function toArray($request)
2020
{
21-
/** @var \Illuminate\Support\Collection $images */
22-
$images = $this->images;
2321
return [
2422
'id' => $this->id,
2523
'title' => $this->title,
2624
'slug' => $this->slug,
2725
'description' => $this->description,
26+
'image_url' => $this->image,
2827
'images' => $this->images,
2928
'price' => $this->price,
3029
'quantity' => $this->quantity,

app/Models/Product.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ class Product extends Model
1414
use HasSlug;
1515
use SoftDeletes;
1616

17-
protected $fillable = ['title', 'description', 'price', 'quantity', 'image', 'published', 'image_mime', 'image_size', 'created_by', 'updated_by'];
17+
protected $fillable = ['title', 'description', 'price', 'quantity', 'published', 'created_by', 'updated_by'];
1818

1919
/**
2020
* Get the options for generating the slug.

app/Models/ProductImage.php

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,14 @@ class ProductImage extends Model
99
{
1010
use HasFactory;
1111

12-
protected $fillable = ['product_id', 'path', 'url', 'mime', 'size', 'position'];
12+
protected $fillable = [
13+
'product_id',
14+
'path',
15+
'url',
16+
'mime',
17+
'size',
18+
'position',
19+
];
1320

1421
public function product()
1522
{

backend/src/assets/noimage.jpg

-8.21 KB
Binary file not shown.

backend/src/assets/noimage.png

58.3 KB
Loading
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
<template>
2+
<div class="flex flex-wrap gap-1">
3+
<div v-for="image of imageUrls"
4+
class="relative w-[120px] h-[120px] rounded border border-dashed flex items-center justify-center hover:border-purple-500 overflow-hidden">
5+
<img :src="image.url" class="max-w-full max-h-full" :class="image.deleted ? 'opacity-50' : ''">
6+
<span v-if="image.deleted" class="absolute left-0 bottom-0 right-0 py-1 px-2 bg-black w-100 text-white text-center flex">
7+
To be deleted
8+
</span>
9+
<span class="absolute top-1 right-1 cursor-pointer" @click="removeImage(image)">
10+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-5 h-5">
11+
<path
12+
d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z"/>
13+
</svg>
14+
</span>
15+
</div>
16+
<div
17+
class="relative w-[120px] h-[120px] rounded border border-dashed flex items-center justify-center hover:border-purple-500 overflow-hidden">
18+
<span>
19+
Upload
20+
</span>
21+
<input type="file" class="absolute left-0 top-0 bottom-0 right-0 w-full h-full opacity-0"
22+
@change="onFileChange" multiple>
23+
</div>
24+
</div>
25+
</template>
26+
27+
<script setup>
28+
// Imports
29+
import {v4 as uuidv4} from 'uuid';
30+
import {onMounted, ref, watch} from "vue";
31+
32+
// Uses
33+
34+
// Refs
35+
36+
const files = ref([])
37+
const imageUrls = ref([])
38+
const deletedImages = ref([])
39+
40+
// Props & Emit
41+
const props = defineProps(['modelValue', 'deletedImages', 'images'])
42+
const emit = defineEmits(['update:modelValue', 'update:deletedImages'])
43+
44+
// Computed
45+
46+
// Methods
47+
function onFileChange($event) {
48+
const chosenFiles = [...$event.target.files];
49+
files.value = [...files.value, ...chosenFiles];
50+
$event.target.value = ''
51+
for (let file of chosenFiles) {
52+
file.id = uuidv4()
53+
readFile(file)
54+
.then(url => {
55+
imageUrls.value.push({
56+
url,
57+
id: file.id
58+
})
59+
})
60+
}
61+
emit('update:modelValue', files.value)
62+
}
63+
64+
function readFile(file) {
65+
return new Promise((resolve, reject) => {
66+
const fileReader = new FileReader()
67+
fileReader.readAsDataURL(file)
68+
fileReader.onload = () => {
69+
resolve(fileReader.result)
70+
}
71+
fileReader.onerror = reject
72+
})
73+
}
74+
75+
function removeImage(image) {
76+
if (image.isProp) {
77+
deletedImages.value.push(image.id)
78+
image.deleted = true;
79+
80+
emit('update:deletedImages', deletedImages.value)
81+
} else {
82+
files.value = files.value.filter(f => f.id !== image.id)
83+
imageUrls.value = imageUrls.value.filter(f => f.id !== image.id)
84+
85+
emit('update:modelValue', files.value)
86+
}
87+
}
88+
89+
// Hooks
90+
watch('props.images', () => {
91+
console.log(props.images)
92+
imageUrls.value = [
93+
...imageUrls.value,
94+
...props.images.map(im => ({
95+
...im,
96+
isProp: true
97+
}))
98+
]
99+
}, {immediate: true, deep: true})
100+
onMounted(() => {
101+
emit('update:modelValue', [])
102+
emit('update:deletedImages', deletedImages.value)
103+
})
104+
105+
</script>
106+
107+
<style scoped>
108+
109+
</style>

backend/src/components/core/CustomInput.vue

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<template>
22
<div>
33
<label class="sr-only">{{ label }}</label>
4-
<div class="mt-1 flex rounded-md">
4+
<div class="mt-1 flex rounded-md shadow-sm">
55
<span v-if="prepend"
66
class="inline-flex items-center px-3 rounded-l-md border border-r-0 border-gray-300 bg-gray-50 text-gray-500 text-sm">
77
{{ prepend }}
@@ -24,13 +24,12 @@
2424
:placeholder="label"></textarea>
2525
</template>
2626
<template v-else-if="type === 'richtext'">
27-
<ckeditor :name="name"
27+
<ckeditor :editor="editor"
2828
:required="required"
29-
:editor="editor"
3029
:model-value="props.modelValue"
3130
@input="onChange"
3231
:class="inputClasses"
33-
:config="props.editorConfig"></ckeditor>
32+
:config="editorConfig"></ckeditor>
3433
</template>
3534
<template v-else-if="type === 'file'">
3635
<input :type="type"
@@ -75,7 +74,7 @@
7574
import {computed, ref} from "vue";
7675
import ClassicEditor from "@ckeditor/ckeditor5-build-classic";
7776
78-
const editor = ClassicEditor
77+
const editor = ClassicEditor;
7978
8079
const props = defineProps({
8180
modelValue: [String, Number, File],
@@ -101,11 +100,8 @@ const props = defineProps({
101100
},
102101
editorConfig: {
103102
type: Object,
104-
default: () => ({
105-
// toolbar: ['bold', 'italic', 'link', 'bulletedList', 'numberedList'],
106-
})
107-
},
108-
103+
default: () => ({})
104+
}
109105
})
110106
111107
const id = computed(() => {
@@ -140,7 +136,7 @@ function onChange(value) {
140136
</script>
141137

142138
<style scoped>
143-
/deep/ .ck.ck-editor {
139+
/deep/ .ck-editor {
144140
width: 100%;
145141
}
146142

0 commit comments

Comments
 (0)