Skip to content
Open
Show file tree
Hide file tree
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
76 changes: 75 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,75 @@
# mini-wp
# mini-wp

#API Documentation

| Title | URL | Method | Request Body | Request Header | Response Success | Response Error |
| ---------- | --- | ------ | ------------ | -------------- | ---------------- | -------------- |
| user register | /user/register | POST | username(string)<br>email(string)<br>password(string) | none |username(string)<br>email(string)<br>password(string) | **internal server error (500)** |
| user login | /user/login | POST | email(string)<br>password(string) | none | email(string)<br>password(string) | **internal server error (500)** |
|google login / signup | /user/googleSignIn | POST | token(string) | none | username(string)<br>email(string)<br>password(string) | **internal server error (500)** |
| display articles | /articles | GET | none | none | Array of object<br>title(string)<br>content(string)<br>created_at(date)<br>author(string)<br>featured_image(string)<br>UserId(number)<br>tags(array) | **internal server error (500)** |
| create new article | /articles | POST | title(string)<br>content(string)<br>created_at(date)<br>author(string)<br>featured_image(string)<br>UserId(string) | token | title(string)<br>content(string)<br>created_at(date)<br>author(string)<br>featured_image(string)<br>UserId(string) | **internal server error (500)** |
| get all my stories | /articles/stories | GET | none | token | array of obj article | **internal server error (500)** |
| read article | /articles/:articleId | GET | none | token | article details<br>title(string)<br>content(string)<br>created_at(date)<br>author(string)<br>featured_image(string)<br>UserId(string) | **internal server error (500)** |
| update article | /articles/:articleId | POST | title(string)<br>content<br>featured_image<br> | userid, token | updated data<br>title(string)<br>content<br>featured_image<br> | **internal server error (500)** |
| delete article | /articles/:articleId | DELETE | none | token, userid | deleted data<br>title(string)<br>content(string)<br>created_at(date)<br>author(string)<br>featured_image(string)<br>UserId(string) | **internal server error (500)** |


##Mini WordPress
Buatlah content management system sederhana menggunakan Client-server model dengan spesifikasi sebagai berikut:

**API Documentation**
yang meliputi : URLs, HTTP method, request, response (success dan error case)

**CRUD endpoints**
untuk Article (title, content, created_at, author, featured_image)
Boleh menambahkan attribute lain, misal: slug

**Upload featured_image**
untuk setiap article

**Register**

**Login**
menggunakan email & password (menggunakan JWT)

**Sign in with 3rd APIs** (Google/Twitter/Facebook/GitHub)
Validasi sehingga hanya authenticated user yang bisa melakukan CRUD Article, baik dari sisi client maupun server

**NO alert();!**



**EXTRAS**
(Wajib untuk anak ngulang):

Article bisa memiliki tags untuk mempermudah pencarian.
tags bisa lebih dari 1, misal: Technology, JavaScript, Machine Learning.
Implementasikan fitur untuk mencari article berdasarkan tag, misal: Saat klik tag JavaScript maka akan menampilkan semua article yang memiliki tag JavaScript.


#Kompetensi Backend:

##API Documentation

Authentication

MongoDB + Mongoose

Upload to Google Cloud Storage

##Kompetensi Client:

Vue.js (Components/Single File Components)

SPA (Single Page Application)

##Deadline:

Week 3 - Senin 09:00

##Submission:

Fork dari organization, lalu open pull request dengan title NAMA LENGKAP KAMU (ex: Dimitri Wahyudiputra) jika sudah selesai. Tambahkan comment yang berisi environment variables yang dipakai (beserta valuenya), link deploy, fitur uniknya apa dan kendala saat mengerjakan.

Collapse
50 changes: 50 additions & 0 deletions client/assets/components/create_new_article.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
Vue.component(`new_article`, {
data: function () {
return {
title: '',
content: '',
featured_image: '',
}
},
components: {
wysiwyg: vueWysiwyg.default.component,
},
methods: {
getFile(e) {
this.featured_image = e.target.files[0]
},
createArticle() {
let newData = new FormData()
newData.append(`title`, this.title)
newData.append(`content`, this.content)
newData.append(`image`, this.featured_image)

axios.post(`${server}/articles`, newData, {
headers: {'token': localStorage.getItem(`token`)}
})
.then(({data}) => {
console.log(data)
})
.catch(err => {
console.log(err)
swal('Oops!', err, 'error')
})
}
},
template: `
<div id="create" class="mt-5 mt-5 w-70">
<form v-on:submit.prevent="createArticle">
<div class="form-group">
<input type="text" class="form-control" id="exampleInputEmail1" aria-describedby="emailHelp" placeholder="Title" v-model="title">
</div>
</form>
<div>
<wysiwyg v-model="content" />
</div>
<br>
<input type="file" class="btn btn-elegant" name="avatar" @change="getFile" required/>
<br>
<button type="submit" class="btn btn-success" v-on:click.prevent="createArticle">Save</button>
</div>
`
})
70 changes: 70 additions & 0 deletions client/assets/components/events_of_the_day.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
Vue.component(`events_of_the_day`, {
props: ['all_articles'],
data: function() {
return {

}
},
mounted() {

},
methods: {
readFullArticle(id) {
axios.get(`${server}/articles/${id}`, {
headers: {'token': localStorage.getItem('token')}
})
.then(({data}) => {
this.$emit(`event`, {
article: data,
page_status: 'read_more'
})
})
.catch(err => {
console.log(err)
swal('Oops!', `You need to sign in`, 'error')
})
}
},
template: `
<section class="my-5 m-5">

<h2 class="h3-responsive font-weight-thin my-5">Recent Posts</h2>

<div v-for="article in all_articles">
<!-- Grid row -->
<div class="row">
<!-- Grid column -->
<div class="col-lg-5 col-xl-4">
<!-- Featured image -->
<div class="view overlay rounded z-depth-1-half mb-lg-0 mb-4" v-if="article.featured_image">
<img class="img-fluid" :src="article.featured_image" alt="image">
<a>
<div class="mask rgba-white-slight"></div>
</a>
</div>

</div>
<!-- Grid column -->
<!-- Grid column -->
<div class="col-lg-7 col-xl-8">

<!-- Post title -->
<h3 class="font-weight-bold mb-3"><strong>{{ article.title }}</strong></h3>
<!-- Excerpt -->
<p class="dark-grey-text text-truncate" style="max-width: 500px;" v-html="article.content"></p>
<!-- Post data -->
<p>By <a class="font-weight-bold">{{ article.author }}</a>, {{ article.created_at }}</p>
<!-- Read more button -->
<a class="btn btn-primary btn-md" @click.prevent="readFullArticle(article._id)">Read more</a>
</div>
<!-- Grid column -->
</div>
<!-- Grid row -->
<hr class="my-5">
</div>
<!-- Grid row -->
</section>
<!-- Section: Blog v.3 -->
`

})
Empty file.
58 changes: 58 additions & 0 deletions client/assets/components/login.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
Vue.component(`login`, {
data: function() {
return {
email: '',
password: ''
}
},
methods: {
loginSuccess() {
axios.post(`${server}/user/login`, {
email: this.email,
password: this.password
})
.then(({data}) => {
localStorage.setItem('token', data)
console.log(`login successful`, data)
this.$emit(`page_status`, '')
})
.catch(err => {
this.$swal('Oops!', err, 'error')
})
}
},
template: `
<!-- Default form login -->
<div class="row d-flex justify-content-center mt-5">
<div class="col-md-6">
<form class="text-center border border-light p-5 mt-5">

<p class="h4 mb-4">SIGN IN</p>

<!-- Email -->
<input type="email" id="defaultLoginFormEmail" class="form-control mb-4" placeholder="E-mail" v-model="email" required>

<!-- Password -->
<input type="password" id="defaultLoginFormPassword" class="form-control mb-4" placeholder="Password" v-model="password" required>

<div class="d-flex justify-content-around">
<div>
<!-- Remember me -->
<div class="custom-control custom-checkbox">
<input type="checkbox" class="custom-control-input" id="defaultLoginFormRemember">
<label class="custom-control-label" for="defaultLoginFormRemember">Remember me</label>
</div>
</div>
</div>

<!-- Sign up button -->
<button class="btn btn-info btn-block my-4" type="button" @click.prevent="loginSuccess">Login</button>

<!-- Social login -->
</form>
<br>
</div>
</div>
<!-- Default form login -->
`
})
60 changes: 60 additions & 0 deletions client/assets/components/my_stories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
Vue.component(`stories`, {
props: ['my_stories'],
data: function() {
return {

}
},
mounted: function() {
console.log(this.my_stories)
},
methods: {
renderPage(stat, article) {
this.$emit(`page_status`, {
status: stat,
data: article
})
}
},
template: `
<div class="my-5 m-5">
<div v-for="article in my_stories">
<!-- Grid row -->
<div class="row">
<!-- Grid column -->
<div class="col-lg-4 col-xl-3">
<!-- Featured image -->
<div class="view overlay rounded z-depth-1-half mb-lg-0 mb-4 w-50" v-if="article.featured_image">
<img class="img-fluid" :src="article.featured_image" alt="image">
<a>
<div class="mask rgba-white-slight"></div>
</a>
</div>

</div>
<!-- Grid column -->
<!-- Grid column -->
<div class="col-lg-7 col-xl-8">

<!-- Post title -->
<h3 class="font-weight-bold mb-3"><strong>{{ article.title }}</strong></h3>
<!-- Excerpt -->
<p class="dark-grey-text text-truncate" style="max-width: 1000px;" v-html="article.content"></p>
<!-- Post data -->
<p>Created: <a class="font-weight-bold">{{ article.created_at }}</a></p>
</div>
<!-- Grid column -->

<!-- Icons -->
<a href="" @click.prevent="renderPage('update', article)"><i class="fas fa-pen-square fa-xs fa-sm fa-lg fa-2x mr-3"></i></a>
<a href="" @click.prevent="renderPage('delete', article)"><i class="fas fa-trash-alt fa-xs fa-sm fa-lg fa-2x"></i></a>

</div>
<!-- Grid row -->
<hr class="my-5">
</div>
<!-- Grid row -->
</div>
<!-- Section: Blog v.3 -->
`
})
44 changes: 44 additions & 0 deletions client/assets/components/navbar_af_login.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
Vue.component(`navbar_af_login`, {
data: function() {
return {
sidenav_status: false
}
},
methods: {
renderPage(status) {
this.$emit(`page_status`, status)
},
},

template: `
<header>
<!-- Navbar -->
<nav class="navbar fixed-top navbar-toggleable-md navbar-expand-lg scrolling-navbar">
<!-- Breadcrumb-->
<div class="navbar-brand">
<a @click.prevent="renderPage('')"><h4>JURVIO</h4></a>
</div>

<ul class="nav justify-content-center">
<li class="nav-item">
<a class="nav-link waves-effect waves-light" @click.prevent="renderPage('stories')"><span class="clearfix d-none d-sm-inline-block">Stories</span></a>
</li>
</ul>

<ul class="nav navbar-nav nav-flex-icons ml-auto">
<form class="form-inline active-cyan-3 active-cyan-4">
<i class="fas fa-search" aria-hidden="true"></i>
<input class="form-control form-control-sm ml-3 w-75" type="text" placeholder="Search" aria-label="Search">
</form>
<li class="nav-item border border-white">
<a class="nav-link waves-effect waves-light" @click.prevent="renderPage('write')"><i class="far fa-edit"></i> <span class="clearfix d-none d-sm-inline-block">Write</span></a>
</li>
<li class="nav-item border border-white ml-2">
<a class="nav-link waves-effect waves-light" @click.prevent="renderPage('logout')"><span class="clearfix d-none d-sm-inline-block">Logout</span></a>
</li>
</ul>
</nav>
<!-- /.Navbar -->
</header>
`
})
Loading