Skip to content

Commit 614d370

Browse files
committed
Implement product update
Delete old image on product update
1 parent fd50a85 commit 614d370

File tree

4 files changed

+103
-26
lines changed

4 files changed

+103
-26
lines changed

app/Http/Controllers/Api/ProductController.php

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,9 +79,27 @@ public function show(Product $product)
7979
* @param \App\Models\Product $product
8080
* @return \Illuminate\Http\Response
8181
*/
82-
public function update(Request $request, Product $product)
82+
public function update(ProductRequest $request, Product $product)
8383
{
84-
$product->update($request->validated());
84+
$data = $request->validated();
85+
$data['updated_by'] = $request->user()->id;
86+
87+
/** @var \Illuminate\Http\UploadedFile $image */
88+
$image = $data['image'] ?? null;
89+
// Check if image was given and save on local file system
90+
if ($image) {
91+
$relativePath = $this->saveImage($image);
92+
$data['image'] = $relativePath;
93+
$data['image_mime'] = $image->getClientMimeType();
94+
$data['image_size'] = $image->getSize();
95+
96+
// If there is an old image, delete it
97+
if ($product->image) {
98+
Storage::deleteDirectory('/public/' . dirname($product->image));
99+
}
100+
}
101+
102+
$product->update($data);
85103

86104
return new ProductResource($product);
87105
}
@@ -105,7 +123,7 @@ private function saveImage(UploadedFile $image)
105123
if (!Storage::exists($path)) {
106124
Storage::makeDirectory($path, 0755, true);
107125
}
108-
if (!Storage::putFileAS('public/'.$path, $image, $image->getClientOriginalName())) {
126+
if (!Storage::putFileAS('public/' . $path, $image, $image->getClientOriginalName())) {
109127
throw new \Exception("Unable to save file \"{$image->getClientOriginalName()}\"");
110128
}
111129

backend/src/store/actions.js

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@ export function getProducts({commit, state}, {url = null, search = '', per_page,
4646
})
4747
}
4848

49+
export function getProduct({commit}, id) {
50+
return axiosClient.get(`/products/${id}`)
51+
}
52+
4953
export function createProduct({commit}, product) {
5054
if (product.image instanceof File) {
5155
const form = new FormData();
@@ -58,6 +62,21 @@ export function createProduct({commit}, product) {
5862
return axiosClient.post('/products', product)
5963
}
6064

65+
export function updateProduct({commit}, product) {
66+
const id = product.id
67+
if (product.image instanceof File) {
68+
const form = new FormData();
69+
form.append('id', product.id);
70+
form.append('title', product.title);
71+
form.append('image', product.image);
72+
form.append('description', product.description);
73+
form.append('price', product.price);
74+
form.append('_method', 'PUT');
75+
product = form;
76+
}
77+
return axiosClient.post(`/products/${id}`, product)
78+
}
79+
6180
export function deleteProduct({commit}, id) {
62-
axiosClient.delete(`/products/${id}`)
81+
return axiosClient.delete(`/products/${id}`)
6382
}

backend/src/views/AddNewProduct.vue renamed to backend/src/views/ProductModal.vue

Lines changed: 50 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,11 @@
1616
leave-to="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95">
1717
<DialogPanel
1818
class="relative bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:max-w-lg sm:w-full">
19-
<Spinner v-if="loading" class="absolute left-0 top-0 bg-white right-0 bottom-0 flex items-center justify-center"/>
19+
<Spinner v-if="loading"
20+
class="absolute left-0 top-0 bg-white right-0 bottom-0 flex items-center justify-center"/>
2021
<header class="py-3 px-4 flex justify-between items-center">
21-
<DialogTitle as="h3" class="text-lg leading-6 font-medium text-gray-900"> Create new Product
22+
<DialogTitle as="h3" class="text-lg leading-6 font-medium text-gray-900">
23+
{{ product.id ? `Update product: "${props.product.title}"` : 'Create new Product' }}
2224
</DialogTitle>
2325
<button
2426
@click="closeModal()"
@@ -67,24 +69,30 @@
6769
</template>
6870

6971
<script setup>
70-
import {computed, ref} from 'vue'
72+
import {computed, onUpdated, ref} from 'vue'
7173
import {Dialog, DialogPanel, DialogTitle, TransitionChild, TransitionRoot} from '@headlessui/vue'
7274
import {ExclamationIcon} from '@heroicons/vue/outline'
7375
import CustomInput from "../components/core/CustomInput.vue";
7476
import store from "../store/index.js";
7577
import Spinner from "../components/core/Spinner.vue";
7678
7779
const product = ref({
78-
title: null,
79-
image: null,
80-
description: null,
81-
price: null
80+
id: props.product.id,
81+
title: props.product.title,
82+
image: props.product.image,
83+
description: props.product.description,
84+
price: props.product.price
8285
})
8386
8487
const loading = ref(false)
8588
8689
const props = defineProps({
8790
modelValue: Boolean,
91+
product: {
92+
required: false,
93+
type: Object,
94+
default: {}
95+
}
8896
})
8997
9098
const emit = defineEmits(['update:modelValue'])
@@ -94,24 +102,46 @@ const show = computed({
94102
set: (value) => emit('update:modelValue', value)
95103
})
96104
105+
onUpdated(() => {
106+
product.value = {
107+
id: props.product.id,
108+
title: props.product.title,
109+
image: props.product.image,
110+
description: props.product.description,
111+
price: props.product.price
112+
}
113+
})
114+
97115
function closeModal() {
98116
show.value = false
99117
}
100118
101119
function onSubmit() {
102120
loading.value = true
103-
store.dispatch('createProduct', product.value)
104-
.then(response => {
105-
loading.value = false;
106-
if (response.status === 201) {
107-
// TODO show notification
108-
store.dispatch('getProducts')
109-
closeModal()
110-
}
111-
})
112-
.catch(err => {
113-
loading.value = false;
114-
debugger;
115-
})
121+
if (product.value.id) {
122+
store.dispatch('updateProduct', product.value)
123+
.then(response => {
124+
loading.value = false;
125+
if (response.status === 200) {
126+
// TODO show notification
127+
store.dispatch('getProducts')
128+
closeModal()
129+
}
130+
})
131+
} else {
132+
store.dispatch('createProduct', product.value)
133+
.then(response => {
134+
loading.value = false;
135+
if (response.status === 201) {
136+
// TODO show notification
137+
store.dispatch('getProducts')
138+
closeModal()
139+
}
140+
})
141+
.catch(err => {
142+
loading.value = false;
143+
debugger;
144+
})
145+
}
116146
}
117147
</script>

backend/src/views/Products.vue

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@
106106
active ? 'bg-indigo-600 text-white' : 'text-gray-900',
107107
'group flex w-full items-center rounded-md px-2 py-2 text-sm',
108108
]"
109+
@click="editProduct(product)"
109110
>
110111
<PencilIcon
111112
:active="active"
@@ -172,7 +173,7 @@
172173
</nav>
173174
</div>
174175
</div>
175-
<AddNewProduct v-model="showProductModal"/>
176+
<ProductModal v-model="showProductModal" :product="product"/>
176177
</template>
177178

178179
<script setup>
@@ -181,16 +182,17 @@ import store from "../store";
181182
import Spinner from "../components/core/Spinner.vue";
182183
import {PRODUCTS_PER_PAGE} from "../constants";
183184
import TableHeaderCell from "../components/core/Table/TableHeaderCell.vue";
184-
import AddNewProduct from "./AddNewProduct.vue";
185185
import {Menu, MenuButton, MenuItem, MenuItems} from "@headlessui/vue";
186186
import {DotsVerticalIcon, PencilIcon, TrashIcon} from '@heroicons/vue/outline'
187+
import ProductModal from "./ProductModal.vue";
187188
188189
const perPage = ref(PRODUCTS_PER_PAGE);
189190
const search = ref('');
190191
const products = computed(() => store.state.products);
191192
const sortField = ref('updated_at');
192193
const sortDirection = ref('desc')
193194
195+
const product = ref({})
194196
const showProductModal = ref(false);
195197
196198
onMounted(() => {
@@ -245,6 +247,14 @@ function deleteProduct(product) {
245247
store.dispatch('getProducts')
246248
})
247249
}
250+
251+
function editProduct(p) {
252+
store.dispatch('getProduct', p.id)
253+
.then(({data}) => {
254+
product.value = data
255+
showAddNewModal();
256+
})
257+
}
248258
</script>
249259

250260
<style scoped>

0 commit comments

Comments
 (0)