Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hisyam Khowarik #46

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .browserslistrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
> 1%
last 2 versions
not dead
17 changes: 17 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
module.exports = {
root: true,
env: {
node: true
},
'extends': [
'plugin:vue/essential',
'eslint:recommended'
],
parserOptions: {
parser: 'babel-eslint'
},
rules: {
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off'
}
}
2 changes: 2 additions & 0 deletions .firebase/hosting.cHVibGlj.cache
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
index.html,1607695492725,9c5f460146ac714ef1a9e2d08d1f20f772c5531e80747d55dd0aca8328b6166b
favicon.ico,1607695492725,e0535b2041a7a1721cdec785c903980d41fdf0d810a4ea9726b6ffd1371bbc28
5 changes: 5 additions & 0 deletions .firebaserc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"projects": {
"default": "ecommerce-cms-16"
}
}
104 changes: 104 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*

# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json

# Runtime data
pids
*.pid
*.seed
*.pid.lock

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage
*.lcov

# nyc test coverage
.nyc_output

# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# Bower dependency directory (https://bower.io/)
bower_components

# node-waf configuration
.lock-wscript

# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release

# Dependency directories
node_modules/
jspm_packages/

# TypeScript v1 declaration files
typings/

# TypeScript cache
*.tsbuildinfo

# Optional npm cache directory
.npm

# Optional eslint cache
.eslintcache

# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/

# Optional REPL history
.node_repl_history

# Output of 'npm pack'
*.tgz

# Yarn Integrity file
.yarn-integrity

# dotenv environment variables file
.env
.env.test

# parcel-bundler cache (https://parceljs.org/)
.cache

# Next.js build output
.next

# Nuxt.js build / generate output
.nuxt
dist

# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and *not* Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public

# vuepress build output
.vuepress/dist

# Serverless directories
.serverless/

# FuseBox cache
.fusebox/

# DynamoDB Local files
.dynamodb/

# TernJS port file
.tern-port
25 changes: 24 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,24 @@
# ecommerce_client_cms
# ecommerce_client_cms-1

## Project setup
```
npm install
```

### Compiles and hot-reloads for development
```
npm run serve
```

### Compiles and minifies for production
```
npm run build
```

### Lints and fixes files
```
npm run lint
```

### Customize configuration
See [Configuration Reference](https://cli.vuejs.org/config/).
5 changes: 5 additions & 0 deletions babel.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset'
]
}
16 changes: 16 additions & 0 deletions firebase.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"hosting": {
"public": "public",
"ignore": [
"firebase.json",
"**/.*",
"**/node_modules/**"
],
"rewrites": [
{
"source": "**",
"destination": "/index.html"
}
]
}
}
12,087 changes: 12,087 additions & 0 deletions package-lock.json

Large diffs are not rendered by default.

32 changes: 32 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"name": "ecommerce_client_cms-1",
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},
"dependencies": {
"axios": "^0.21.0",
"bootstrap": "^4.5.3",
"bootstrap-vue": "^2.20.1",
"core-js": "^3.6.5",
"sweetalert2": "^10.12.4",
"vue": "^2.6.11",
"vue-router": "^3.2.0",
"vue-sweetalert2": "^4.1.1",
"vuex": "^3.4.0"
},
"devDependencies": {
"@vue/cli-plugin-babel": "~4.5.0",
"@vue/cli-plugin-eslint": "~4.5.0",
"@vue/cli-plugin-router": "~4.5.0",
"@vue/cli-plugin-vuex": "~4.5.0",
"@vue/cli-service": "~4.5.0",
"babel-eslint": "^10.1.0",
"eslint": "^6.7.2",
"eslint-plugin-vue": "^6.2.2",
"vue-template-compiler": "^2.6.11"
}
}
Binary file added public/favicon.ico
Binary file not shown.
17 changes: 17 additions & 0 deletions public/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>
9 changes: 9 additions & 0 deletions src/App.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<template>
<div id="app">
<router-view/>
</div>
</template>

<style>
</style>
Binary file added src/assets/logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 8 additions & 0 deletions src/axios/axiosInstance.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import axios from 'axios'

const instance = axios.create({
// baseURL: 'http://localhost:3000'
baseURL: 'https://ecommerce-cms-16.herokuapp.com'
})

export default instance
34 changes: 34 additions & 0 deletions src/components/ListProducts.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<template>
<tr class="text-center">
<th scope="row">{{index+1}}</th>
<td>{{product.name}}</td>
<td><img :src="product.image_url" alt="" class="img-thumbnail" width="300px"></td>
<td>{{product.category}}</td>
<td>{{product.price}}</td>
<td>{{product.stock}}</td>
<td class="text-center d-flex flex-row">
<button type="button" class="btn btn-primary mr-2" v-on:click.prevent="toEditProduct(product.id)">Edit</button>
<button type="button" class="btn btn-danger" v-on:click.prevent="deleteProduct(product.id)">Delete</button>
</td>
</tr>
</template>

<script>
export default {
name: 'ListProducts',
props: ['product', 'index'],
methods: {
toEditProduct (productId) {
this.$router.push({ name: 'EditForm', params: { id: productId } })
},
deleteProduct (num) {
const productId = num
this.$store.dispatch('deleteProduct', productId)
}
}
}
</script>

<style>
</style>
39 changes: 39 additions & 0 deletions src/components/Navbar.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<template>
<nav class="navbar navbar-expand-lg navbar-light bg-info">
<a class="navbar-brand">ECOMMERCE-CMS APP</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" href="#"
v-on:click.prevent="home"
v-if="this.$route.name === 'AddForm' || this.$route.name === 'EditForm'">Home</a>
</li>
</ul>
</div>
<div>
<button type="button" class="btn btn-warning" v-on:click.prevent="logout" v-if="this.$route.name === 'Home'">Logout</button>
</div>
</nav>
</template>

<script>
export default {
name: 'Navbar',
methods: {
home () {
this.$router.push({ name: 'Home' })
},
logout () {
localStorage.clear()
this.$router.push({ name: 'Login' })
}
}
}
</script>

<style>
</style>
16 changes: 16 additions & 0 deletions src/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import 'bootstrap/dist/css/bootstrap.css'
import 'bootstrap-vue/dist/bootstrap-vue.css'
import store from './store'
import VueSweetalert2 from 'vue-sweetalert2'

Vue.config.productionTip = false

Vue.use(VueSweetalert2)
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
48 changes: 48 additions & 0 deletions src/router/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'
import Login from '../views/LoginForm.vue'
import AddForm from '../views/AddForm.vue'
import EditForm from '../views/EditForm'

Vue.use(VueRouter)
const routes = [
{
path: '/',
name: 'Login',
component: Login
},
{
path: '/home',
name: 'Home',
component: Home
},
{
path: '/add-product',
name: 'AddForm',
component: AddForm
},
{
path: '/edit-product/:id',
name: 'EditForm',
component: EditForm
}
]

const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})

router.beforeEach((to, from, next) => {
if ((to.name === 'Login') && localStorage.access_token) {
next({ path: '/home' })
} else if ((to.name !== 'Login') && !localStorage.access_token) {
next({ path: '/' })
} else {
next()
}
})

export default router
172 changes: 172 additions & 0 deletions src/store/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
import Vue from 'vue'
import Vuex from 'vuex'
import axios from '../axios/axiosInstance'
import router from '../router/index'

Vue.use(Vuex)

export default new Vuex.Store({
state: {
products: [],
product: {}
},
mutations: {
setProducts (state, payload) {
state.products = payload
},
setProduct (state, payload) {
state.product = payload
}
},
actions: {
login (context, payload) {
axios({
method: 'POST',
url: '/login',
data: payload
})
.then(({ data }) => {
console.log(data)
localStorage.setItem('access_token', data.access_token)
router.push({ name: 'Home' })
Vue.swal(
'Success!',
'Welcome',
'success'
)
})
.catch(err => {
console.log(err)
Vue.swal(
'Error!',
err.response.data.msg,
'error'
)
})
},
fetchProducts (context) {
axios({
method: 'GET',
url: '/products',
headers: {
access_token: localStorage.getItem('access_token')
}
})
.then(({ data }) => {
console.log(data)
context.commit('setProducts', data)
})
.catch(err => {
console.log(err)
Vue.swal(
'Error!',
err.response.data.msg,
'error'
)
})
},
addProduct (context, payload) {
axios({
method: 'POST',
url: '/products',
data: payload,
headers: {
access_token: localStorage.getItem('access_token')
}
})
.then(({ data }) => {
Vue.swal(
'Success!',
'Add Product!',
'success'
)
console.log(data)
router.push({ name: 'Home' })
})
.catch(err => {
console.log(err)
Vue.swal(
'Error!',
err.response.data.msg,
'error'
)
})
},
deleteProduct (context, payload) {
axios({
method: 'DELETE',
url: `/products/${payload}`,
headers: {
access_token: localStorage.getItem('access_token')
}
})
.then(({ data }) => {
console.log(data)
context.dispatch('fetchProducts')
Vue.swal(
'Success!',
'Delete Product Success',
'success'
)
})
.catch(err => {
console.log(err)
Vue.swal(
'Error!',
err.response.data.msg,
'error'
)
})
},
fetchProductsById (context, payload) {
axios({
method: 'GET',
url: `/products/${payload}`,
headers: {
access_token: localStorage.getItem('access_token')
}
})
.then(({ data }) => {
context.commit('setProduct', data)
})
.catch(err => {
console.log(err)
Vue.swal(
'Error!',
err.response.data.msg,
'error'
)
})
},
editProduct (context, payload) {
const productId = payload.id
axios({
method: 'PUT',
url: `/products/${productId}`,
data: payload,
headers: {
access_token: localStorage.getItem('access_token')
}
})
.then(({ data }) => {
console.log(data)
router.push({ name: 'Home' })
Vue.swal(
'Success!',
'Edit Product Success!',
'success'
)
})
.catch(err => {
console.log(err)
Vue.swal(
'Error!',
err.response.data.msg,
'error'
)
})
}
},
modules: {
}
})
5 changes: 5 additions & 0 deletions src/views/About.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<template>
<div class="about">
<h1>This is an about page</h1>
</div>
</template>
72 changes: 72 additions & 0 deletions src/views/AddForm.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<template>
<div>
<Navbar class="mb-3"></Navbar>
<h3 class="text-center">Add a new product</h3>
<div class="container my-5 py-3 border border-dark rounded bg-secondary text-white" style="width: 40%;">
<div class="container">
<form id="addForm" v-on:submit.prevent="addProduct">
<div class="form-group">
<label for="name">Name</label>
<input type="text" class="form-control" v-model="name">
</div>
<div class="form-group">
<label for="image_url">Image URL</label>
<textarea class="form-control" rows="1" v-model="image_url"></textarea>
</div>
<div class="form-group">
<label for="category">Category</label>
<input type="text" class="form-control" v-model="category">
</div>
<div class="form-group">
<label for="price">Price</label>
<input type="number" class="form-control" name="price" v-model="price">
</div>
<div class="form-group">
<label for="stock">Stock(s)</label>
<input type="number" class="form-control" v-model="stock">
</div>
<button type="submit" class="btn btn-primary" style="width: 100%;">Add</button>
<button type="button" class="btn btn-danger my-2" style="width: 100%;" v-on:click.prevent="home">Cancel</button>
</form>
</div>
</div>
</div>
</template>

<script>
import Navbar from '../components/Navbar'
export default {
name: 'AddForm',
components: {
Navbar
},
data () {
return {
name: '',
image_url: '',
price: '',
stock: '',
category: ''
}
},
methods: {
home () {
this.$router.push({ name: 'Home' })
},
addProduct () {
const product = {
name: this.name,
image_url: this.image_url,
price: this.price,
stock: this.stock,
category: this.category
}
this.$store.dispatch('addProduct', product)
}
}
}
</script>

<style>
</style>
92 changes: 92 additions & 0 deletions src/views/EditForm.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
<template>
<div>
<Navbar class="mb-3"></Navbar>
<h3 class="text-center">Edit a product</h3>
<div class="container my-5 py-3 border border-dark rounded bg-secondary text-white" style="width: 40%;">
<div class="container">
<form id="addForm" v-on:submit.prevent="editProduct">
<div class="form-group">
<label for="name">Name</label>
<input type="text" class="form-control" v-model="name">
</div>
<div class="form-group">
<label for="image_url">Image URL</label>
<textarea class="form-control" rows="1" v-model="image_url"></textarea>
</div>
<div class="form-group">
<label for="category">Category</label>
<input type="text" class="form-control" v-model="category">
</div>
<div class="form-group">
<label for="price">Price</label>
<input type="number" class="form-control" name="price" v-model="price">
</div>
<div class="form-group">
<label for="stock">Stock(s)</label>
<input type="number" class="form-control" v-model="stock">
</div>
<button type="submit" class="btn btn-primary" style="width: 100%;">Update</button>
<button type="button" class="btn btn-danger" style="width: 100%;" v-on:click.prevent="home">Cancel</button>
</form>
</div>
</div>
</div>
</template>

<script>
import Navbar from '../components/Navbar'
export default {
name: 'EditForm',
components: {
Navbar
},
data () {
return {
name: '',
image_url: '',
price: '',
stock: '',
category: ''
}
},
created () {
console.log(this.$route.params.id, '<<< INI ID DARI ROUTE')
this.$store.dispatch('fetchProductsById', this.$route.params.id)
},
computed: {
product () {
return this.$store.state.product
}
},
watch: {
product () {
this.name = this.product.name
this.image_url = this.product.image_url
this.price = this.product.price
this.stock = this.product.stock
this.category = this.product.category
}
},
methods: {
editProduct () {
const productId = this.$route.params.id
const updated = {
id: productId,
name: this.name,
image_url: this.image_url,
price: this.price,
stock: this.stock,
category: this.category
}
this.$store.dispatch('editProduct', updated)
},
home () {
this.$router.push({ name: 'Home' })
}
}
}
</script>

<style>
</style>
55 changes: 55 additions & 0 deletions src/views/Home.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<template>
<div>
<Navbar class="mb-3"></Navbar>
<h2 class="text-center">List Product</h2>
<div class="container">
<button type="button" class="btn btn-primary my-3 d-flex justify-content-end" v-on:click.prevent="toAddForm">Add Product</button>
<table class="table">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Name</th>
<th scope="col">Image</th>
<th scope="col">Category</th>
<th scope="col">Price</th>
<th scope="col">Stock</th>
<th scope="col" class="text-center">Action</th>
</tr>
</thead>
<tbody>
<ListProducts
v-for="(product, i) in products"
:key="i"
:product="product"
:index="i"></ListProducts>
</tbody>
</table>
</div>
</div>
</template>

<script>
import Navbar from '../components/Navbar'
import ListProducts from '../components/ListProducts'
export default {
name: 'Home',
components: {
Navbar,
ListProducts
},
methods: {
toAddForm () {
this.$router.push({ name: 'AddForm' })
}
},
created () {
this.$store.dispatch('fetchProducts')
},
computed: {
products () {
return this.$store.state.products
}
}
}
</script>
52 changes: 52 additions & 0 deletions src/views/LoginForm.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<template>
<div>
<Navbar class="mb-5"></Navbar>
<div class="container my-5 py-3 border border-dark rounded bg-secondary text-white" style="width: 35%;">
<div class="container" id="login">
<div class="container">
<h3 class="text-center my-3">Login</h3>
</div>
<form id="loginForm" class="my-3">
<div class="form-group">
<label for="input_email">Email address</label>
<input type="email" class="form-control" id="input_email" placeholder="Email" v-model="email">
</div>
<div class="form-group">
<label for="input_password">Password</label>
<input type="password" class="form-control" id="input_password" placeholder="Password" v-model="password">
</div>
<button type="submit" class="btn btn-primary" style="width: 100%;" v-on:click.prevent="login">Login</button>
</form>
</div>
</div>
</div>
</template>

<script>
import Navbar from '../components/Navbar'
export default {
name: 'Login',
data () {
return {
email: '',
password: ''
}
},
components: {
Navbar
},
methods: {
login () {
const admin = {
email: this.email,
password: this.password
}
this.$store.dispatch('login', admin)
}
}
}
</script>

<style>
</style>