Commit c8b6574d authored by Le Dinh Trung's avatar Le Dinh Trung

Merge branch 'feature/manage-products' into 'dev'

Feature/manage products

See merge request !6
parents c2d2891c 1203f91e
<template>
<v-card class="mx-auto" height="400" width="256">
<v-navigation-drawer class="deep-purple accent-4" dark permanent>
<v-list>
<v-list-item v-for="item in items" :key="item.title" link>
<v-list-item-icon>
<v-icon>{{ item.icon }}</v-icon>
</v-list-item-icon>
<v-list-item-content>
<v-list-item-title>{{ item.title }}</v-list-item-title>
</v-list-item-content>
</v-list-item>
</v-list>
<template v-slot:append>
<div class="pa-2">
<v-btn block> Logout </v-btn>
</div>
</template>
</v-navigation-drawer>
</v-card>
</template>
<script>
export default {
name: "navigation",
data() {
return {
items: [
{ title: "Dashboard", icon: "mdi-view-dashboard" },
{ title: "Account", icon: "mdi-account-box" },
{ title: "Admin", icon: "mdi-gavel" },
],
};
},
};
</script>
<template>
<div class="notification is-danger bg-danger">
{{ message }}
</div>
</template>
<style>
.notification {
padding: 11px;
margin: 10px 0px;
}
</style>
<script>
export default {
name: "Notification",
props: ["message"],
};
</script>
...@@ -25,7 +25,7 @@ ...@@ -25,7 +25,7 @@
/> />
<label>Parent :</label> <label>Parent :</label>
<b-form-select v-model="parent_id"> <b-form-select v-model="parent_id">
<option v-for="item in categories" :value="item.id"> <option v-for="item in categories" :value="item.id" v-if="item.parent_id == null">
{{item.name}} {{item.name}}
</option> </option>
</b-form-select> </b-form-select>
...@@ -191,16 +191,12 @@ ...@@ -191,16 +191,12 @@
</template> </template>
<script> <script>
import Nav from "@/components/Nav"; import Nav from "@/components/Nav";
import Navigation from "@/components/Navigation";
import axios from "axios"; import axios from "axios";
import notification from "@/components/Notification";
import { BootstrapVue, BootstrapVueIcons } from 'bootstrap-vue' import { BootstrapVue, BootstrapVueIcons } from 'bootstrap-vue'
export default { export default {
layout: 'admin', layout: 'admin',
components: { Nav }, components: { Nav },
components: { Navigation },
components: { notification },
middleware: ["web"], middleware: ["web"],
data: () => { data: () => {
return { return {
...@@ -358,7 +354,6 @@ export default { ...@@ -358,7 +354,6 @@ export default {
console.log(this.image) console.log(this.image)
}, },
deleteCategory(ID,index) { deleteCategory(ID,index) {
const self = this
this.editedIndex = this.categories.indexOf(index); this.editedIndex = this.categories.indexOf(index);
if(confirm("Do you really want to delete?")){ if(confirm("Do you really want to delete?")){
try{ try{
......
<template> <template>
<div> <div>
<b-breadcrumb> <b-breadcrumb>
<b-breadcrumb-item href="/home"> <b-breadcrumb-item href="/home">
<b-icon icon="house-fill" scale="1.25" shift-v="1.25" aria-hidden="true"></b-icon> <b-icon icon="house-fill" scale="1.25" shift-v="1.25" aria-hidden="true"></b-icon>
Home Home
</b-breadcrumb-item> </b-breadcrumb-item>
<b-breadcrumb-item href="/home/categories">Product</b-breadcrumb-item> <b-breadcrumb-item href="/home/products">Product</b-breadcrumb-item>
</b-breadcrumb> </b-breadcrumb>
<div style="float: right">
<b-button class="text-white" v-b-modal.modal-create >New Product</b-button></div>
<b-modal id="modal-create" title="create Product" class="modal fade" >
<div class="w-full mt-4 p-10">
<form >
<label for="input-live">Name :</label>
<b-form-input
id="input-live"
type="text"
class="form-control mb-2"
placeholder="name"
aria-describedby="input-live-help input-live-feedback"
v-model="name"
max="255"
min="1"
trim
>
</b-form-input>
<b-form-invalid-feedback id="input-live-feedback">
Enter at least 3 letters
</b-form-invalid-feedback>
<label>Category ID :</label>
<b-form-select v-model="category_id">
<option v-for="item in categories" :value="item.id">
{{item.name}}
</option>
</b-form-select>
<label>Price :</label>
<input
type="text"
class="form-control mb-2"
placeholder="Ordering"
v-model="price"
size="sm"
required
/>
<label>Description :</label>
<input
type="text"
class="form-control mb-2"
placeholder="Description"
v-model="description"
size="sm"
required
/>
<label>Stock :</label>
<input
type="text"
class="form-control mb-2"
placeholder="Stock"
v-model="stock"
size="sm"
required
/>
<label>Image :</label>
<b-form-file
v-model="images"
multiple
:state="Boolean(images)"
placeholder="Choose a image or drop it here..."
drop-placeholder="Drop file here..."
></b-form-file>
<button
type="button"
class="btn-primary flex justify-start ml-2 mt-2 rounded-md border px-3 py-2 bg-pink-600 text-white"
@click="addMore()"
>
Variant (+)
</button>
<div v-for="(variant, index) in variants" :key=" 'B' +index">
<div class="flex justify-start ml-2 mt-4">
<input
v-model="variant.color"
placeholder="Color"
class="w-full py-2 border border-indigo-500 rounded"
/>
<input
v-model="variant.size"
placeholder="Size"
class="w-full py-2 border border-indigo-500 rounded"
/>
<input
v-model="variant.quantity"
placeholder="Quantity"
class="w-full py-2 border border-indigo-500 rounded"
/>
<button
type="button"
class="ml-2 rounded-md mt-2 "
@click="remove(index)"
v-show="index != 0"
>
<b-icon icon="dash-circle-fill" class="h5" variant="danger"></b-icon>
</button>
</div>
</div>
</form>
</div>
<template #modal-footer>
<button v-b-modal.modal-close_visit @click="$bvModal.hide('modal-create')" class="btn btn-danger btn-sm m-1">Close</button>
<button @click="createProduct()" v-b-modal.modal-close_visit class="btn btn-success btn-sm m-1">Submit</button>
</template>
</b-modal>
<!-- modal-show -->
<b-modal id="modal-show" title="PRODUCT" class="modal fade" >
<div class="w-full mt-4 p-10">
<form >
<label>Name :</label>
<input
type="text"
class="form-control mb-2"
v-model="sName"
size="sm"
disabled
/>
<label>Category ID :</label>
<input
type="text"
class="form-control mb-2"
v-model="sCategoryId"
size="sm"
disabled
/>
<label>Price :</label>
<input
type="text"
class="form-control mb-2"
v-model="sPrice"
size="sm"
disabled
/>
<label>Description :</label>
<input
type="text"
class="form-control mb-2"
placeholder="Description"
v-model="sDescription"
size="sm"
disabled
/>
<label>Stock :</label>
<input
type="text"
class="form-control mb-2"
placeholder="Stock"
v-model="sStock"
size="sm"
disabled
/>
<label>Image :</label>
<div v-for="(image, index) in sImages" :key="index">
<b-img :src="image" fluid alt="Fluid image"></b-img>
</div>
<label>Variant :</label>
<div v-for="(variant, index) in sVariants" :key="'A' +index">
<div class="flex justify-start ml-2 mt-4">
<input
v-model="variant.color"
placeholder="Color"
class="w-full py-2 border border-indigo-500 rounded"
disabled
/>
<input
v-model="variant.size"
placeholder="Size"
class="w-full py-2 border border-indigo-500 rounded"
disabled
/>
<input
v-model="variant.quantity"
placeholder="Quantity"
class="w-full py-2 border border-indigo-500 rounded"
disabled
/>
</div>
</div>
</form>
</div>
<template #modal-footer>
<button v-b-modal.modal-close_visit @click="$bvModal.hide('modal-show')" class="btn btn-danger btn-sm m-1">Close</button>
</template>
</b-modal>
<!-- modal-edit -->
<b-modal id="modal-edit" title="Edit Product" class="modal fade" >
<div class="w-full mt-4 p-10">
<form >
<label>Name :</label>
<input
type="text"
class="form-control mb-2"
placeholder="name"
v-model="eName"
max="255"
min="1"
size="sm"
required
/>
<label>Category ID :</label>
<b-form-select v-model="eCategoryId">
<option v-for="item in categories" :value="item.id">
{{item.name}}
</option>
</b-form-select>
<label>Price :</label>
<input
type="text"
class="form-control mb-2"
placeholder="Ordering"
v-model="ePrice"
size="sm"
required
/>
<label>Description :</label>
<input
type="text"
class="form-control mb-2"
placeholder="Description"
v-model="eDescription"
size="sm"
required
/>
<label>Stock :</label>
<input
type="text"
class="form-control mb-2"
placeholder="Stock"
v-model="eStock"
size="sm"
required
/>
<label>Image :</label>
<b-form-file
v-model="eImages"
multiple
:state="Boolean(eImages)"
placeholder="Choose a image or drop it here..."
drop-placeholder="Drop file here..."
></b-form-file>
<button
type="button"
class="btn-primary flex justify-start ml-2 mt-2 rounded-md border px-3 py-2 bg-pink-600 text-white"
@click="addMoreVariant()"
>
Variant (+)
</button>
<div v-for="(variant, index) in eVariants" :key="index">
<div class="flex justify-start ml-2 mt-4">
<input
v-model="variant.color"
placeholder="Color"
class="w-full py-2 border border-indigo-500 rounded"
/>
<input
v-model="variant.size"
placeholder="Size"
class="w-full py-2 border border-indigo-500 rounded"
/>
<input
v-model="variant.quantity"
placeholder="Quantity"
class="w-full py-2 border border-indigo-500 rounded"
/>
<button
type="button"
class="ml-2 rounded-md mt-2 "
@click="removeVariant(index)"
v-show="index != 0"
>
<b-icon icon="dash-circle-fill" class="h5" variant="danger"></b-icon>
</button>
</div>
</div>
</form>
</div>
<template #modal-footer>
<button v-b-modal.modal-close_visit @click="$bvModal.hide('modal-edit')" class="btn btn-danger btn-sm m-1">Close</button>
<button @click="updateProduct()" v-b-modal.modal-close_visit class="btn btn-success btn-sm m-1">Submit</button>
</template>
</b-modal>
<!-- table -->
<div>
<v-data-table
:headers="headers"
:items="products"
sort-by="calories"
class="elevation-1"
>
<template v-slot:top >
<v-toolbar flat>
<v-toolbar-title>Product Manage</v-toolbar-title>
<v-divider class="mx-4" inset vertical></v-divider>
<v-spacer></v-spacer>
</v-toolbar>
</template>
<template v-slot:item.actions="{ item }">
<v-icon small @click="editProduct(item)" :id ="item.id"> mdi-pencil </v-icon>
<v-icon small @click="deleteProduct(item.id,item)" :id ="item.id"> mdi-delete </v-icon>
<v-icon small @click="showProduct(item)" :id ="item.id"> mdi-account-details </v-icon>
</template>
<template v-slot:no-data>
<v-btn color="primary" @click="initialize"> Reset </v-btn>
</template>
</v-data-table>
</div>
</div> </div>
</template> </template>
<script> <script>
import { onMounted } from "vue";
import axios from "axios";
import VueAxios from "vue-axios";
import Nav from "@/components/Nav"; import Nav from "@/components/Nav";
import axios from "axios";
export default { export default {
layout: "admin", layout: 'admin',
components: { Nav }, name: "App",
middleware: ['web'], components: {
Nav,
},
middleware: ["web"],
data: () => {
return {
name: '',
id: '',
category_id: null,
price: '',
stock: '',
description: '',
images: [],
variants: [
{
color: "",
size: "",
quantity: "",
},
],
dialog: false,
dialogDelete: false,
options: [],
headers: [
{
text: "Name",
align: "start",
sortable: false,
value: "name",
},
{ text: "id", value: "id" },
{ text: "category ID", value: "category_id" },
{ text: "price", value: "price", sortable: false },
{ text: "description", value: "description" },
{ text: "stock", value: "stock" },
{ text: 'Actions', value: 'actions', sortable: false },
],
products: [],
product: [],
categories: [],
sName: '',
sCategoryId: '',
sPrice: '',
sDescription: '',
sStock: '',
sImages: null,
sVariants: [
{
color: "",
size: "",
quantity: "",
},
],
eId: '',
eName: '',
eCategoryId: '',
ePrice: '',
eDescription: '',
eStock: '',
eImages: null,
eVariants: [
{
color: "",
size: "",
quantity: "",
},
],
message: [],
editedIndex: -1,
editedItem: {
name: "",
id: "",
ordering: "",
status: "",
created_at: "",
updated_at: "",
},
defaultItem: {
name: "",
id: "",
ordering: "",
status: "",
created_at: "",
updated_at: "",
},
}
},
computed: {
formTitle() {
return this.editedIndex === -1 ? "New Item" : "Edit Item"
},
nameState() {
return this.name.length > 2 ? true : false
}
},
watch: {
dialog(val) {
val || this.close()
},
dialogDelete(val) {
val || this.closeDelete()
},
},
created() {
this.initialize(),
this.getProducts(),
this.getCategories()
},
methods: {
initialize() {
this.products= [],
this.product= [],
this.categories= [],
this.sName= '',
this.sCategoryId= '',
this.sPrice= '',
this.sDescription= '',
this.sStock= '',
this.sImages= null,
this.sVariants= [
{
color: "",
size: "",
quantity: "",
},
];
},
editItem(item) {
this.editedIndex = this.products.indexOf(item)
this.editedItem = Object.assign({}, item)
this.dialog = true
},
deleteItem(item) {
this.editedIndex = this.products.indexOf(item);
this.editedItem = Object.assign({}, item);
this.dialogDelete = true;
},
deleteItemConfirm() {
this.products.splice(this.editedIndex, 1);
this.closeDelete()
},
close() {
this.dialog = false
this.$nextTick(() => {
this.editedItem = Object.assign({}, this.defaultItem)
this.editedIndex = -1
});
},
closeDelete() {
this.dialogDelete = false
this.$nextTick(() => {
this.editedItem = Object.assign({}, this.defaultItem);
this.editedIndex = -1
});
},
save() {
if (this.editedIndex > -1) {
Object.assign(this.products[this.editedIndex], this.editedItem);
} else {
this.products.push(this.editedItem);
}
this.close()
},
getCategories() {
axios
.get("http://127.0.0.1:8000/api/categories/")
.then((response) => (this.categories = response.data.data))
.catch(function (error) {
console.log(error)
});
},
getProducts() {
axios
.get("http://127.0.0.1:8000/api/products/")
.then((response) => (this.products = response.data.data))
.catch(function (error) {
console.log(error)
});
},
createProduct() {
const self = this;
// const set = new Set([this.images]);
// const images = Array.from(set);
axios
.post('http://127.0.0.1:8000/api/products/',{
name: this.name,
price: this.price,
category_id: this.category_id,
description: this.description,
images: this.images,
variants: this.variants,
}, {
headers: {
"Content-Type": "multipart/form-data",
"Authorization": this.$auth.$storage.getUniversal("token")
}
})
.then(response => {
this.$bvModal.hide('modal-create')
self.$toast.success('Product created successfully!', {
duration: 3000
})
})
.catch(errors => {
this.$bvModal.hide('modal-create')
console.log(errors.response.data.message)
this.message = errors.response.data.message;
self.$toast.error('something went wrong while trying create!',{
duration: 3000
})
})
},
deleteProduct(ID,index) {
const self = this;
this.editedIndex = this.products.indexOf(index);
if(confirm("Do you really want to delete?")){
try{
axios
.delete(`http://127.0.0.1:8000/api/products/${ID}`)
.then(response => {
this.products.splice(this.editedIndex, 1);
self.$toast.success('Category deleted successfully!',{
duration: 3000
});
})
} catch(error){
console.log(error)
}
}
},
async showProduct(item) {
const ID = item.id
try {
const resp = await fetch(`http://127.0.0.1:8000/api/products/${ID}`, {
method: "GET",
headers: {
"Content-Type": "multipart/form-data",
"Authorization": this.$auth.$storage.getUniversal("token")
}
}).then((response) => {
return response.json()
});
this.sName = resp.data.name
this.sPrice = resp.data.price
this.sCategoryId = resp.data.category_id
this.sDescription = resp.data.description
this.sStock = resp.data.stock
this.sVariants = resp.data.variants
this.sImages = resp.data.images
console.log(this.sVariants)
console.log(this.sImages)
} catch (error) {
console.log(error)
}
this.$bvModal.show('modal-show')
},
editProduct(item) {
this.$bvModal.show('modal-edit')
this.eId = item.id
this.eName = item.name
this.eCategoryId = item.category_id
this.ePrice = item.price
this.eStock = item.stock
this.eDescription = item.description
this.eVariants = item.variants
},
updateProduct(ID) {
const self = this
try{
axios
.post(`http://127.0.0.1:8000/api/products/update/${this?.eId}`,{
name: this.eName,
price: this.ePrice,
category_id: this.eCategoryId,
description: this.eDescription,
images: this.eImages,
variants: this.eVariants,
}, {
headers: {
"Content-Type": "multipart/form-data",
"Authorization": this.$auth.$storage.getUniversal("token")
}
} )
.then(response => {
self.$toast.success('User updated successfully!',{
duration: 3000
});
console.log(response)
})
} catch(error){
console.log(error)
}
},
addMore() {
this.variants.push({
color: "",
size: "",
quantity: "",
});
},
addMoreVariant() {
this.eVariants.push({
color: "",
size: "",
quantity: "",
});
},
remove(index) {
this.variants.splice(index, 1)
},
removeVariant(index) {
this.eVariants.splice(index, 1)
},
},
}; };
</script> </script>
\ No newline at end of file
<style>
</style>
\ No newline at end of file
...@@ -183,8 +183,8 @@ ...@@ -183,8 +183,8 @@
</v-toolbar> </v-toolbar>
</template> </template>
<template v-slot:item.actions="{ item }"> <template v-slot:item.actions="{ item }">
<v-icon small class="mr-2" @click=" editUser(item.id);" :id ="item.id"> mdi-pencil </v-icon> <v-icon small class="mr-2" @click=" editUser(item);" :id ="item.id"> mdi-pencil </v-icon>
<v-icon small @click="deleteUser(item.id)" :id ="item.id"> mdi-delete </v-icon> <v-icon small @click="deleteUser(item.id, item)" :id ="item.id"> mdi-delete </v-icon>
</template> </template>
<template v-slot:no-data> <template v-slot:no-data>
<v-btn color="primary" @click="initialize"> Reset </v-btn> <v-btn color="primary" @click="initialize"> Reset </v-btn>
...@@ -194,17 +194,13 @@ ...@@ -194,17 +194,13 @@
</template> </template>
<script> <script>
import Nav from "@/components/Nav"; import Nav from "@/components/Nav";
import Navigation from "@/components/Navigation";
import axios from "axios"; import axios from "axios";
import notification from "@/components/Notification";
import Toasted from 'vue-toasted'; import Toasted from 'vue-toasted';
import { ModalPlugin } from 'bootstrap-vue'; import { ModalPlugin } from 'bootstrap-vue';
export default { export default {
layout: "admin", layout: "admin",
components: { Nav }, components: { Nav },
components: { Navigation },
components: { notification },
middleware: ["web"], middleware: ["web"],
data: () => { data: () => {
return { return {
...@@ -223,7 +219,7 @@ export default { ...@@ -223,7 +219,7 @@ export default {
}, },
{ text: "id", value: "id" }, { text: "id", value: "id" },
{ text: "email", value: "email" }, { text: "email", value: "email" },
{ text: "status", value: "status", sortable: false }, { text: "status", value: "id", sortable: false },
{ text: "created_at", value: "created_at" }, { text: "created_at", value: "created_at" },
{ text: "updated_at", value: "updated_at" }, { text: "updated_at", value: "updated_at" },
{ text: 'Actions', value: 'actions', sortable: false }, { text: 'Actions', value: 'actions', sortable: false },
...@@ -346,19 +342,25 @@ export default { ...@@ -346,19 +342,25 @@ export default {
}); });
}); });
}, },
deleteUser(userID) { deleteUser(userID, index) {
this.editedIndex = this.users.indexOf(index);
if(confirm("Do you really want to delete?")){ if(confirm("Do you really want to delete?")){
try{ try{
axios axios
.delete(`http://127.0.0.1:8000/api/users/${userID}`) .delete(`http://127.0.0.1:8000/api/users/${userID}`)
.then(response => {
this.users.splice(this.editedIndex, 1);
})
} catch(error){ } catch(error){
console.log(error) console.log(error)
} }
} }
}, },
editUser(userID) { editUser(user) {
this.$bvModal.show('modal-edit'); this.$bvModal.show('modal-edit');
this.eID = userID; this.eID = user.id;
this.eEmail = user.email;
this.eName = user.name;
console.log(this?.eID); console.log(this?.eID);
}, },
updateUser(userID) { updateUser(userID) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment