@ -0,0 +1,55 @@ | |||||
package Seeder | |||||
import ( | |||||
"git.tovijaeschke.xyz/tovi/SuddenImpactRecords/Database" | |||||
"git.tovijaeschke.xyz/tovi/SuddenImpactRecords/Models" | |||||
) | |||||
func createPost(userData Models.User) (Models.Post, error) { | |||||
var ( | |||||
postData Models.Post | |||||
err error | |||||
) | |||||
postData = Models.Post{ | |||||
UserID: userData.ID, | |||||
Title: "Test post", | |||||
Content: "Test content", | |||||
FrontPage: true, | |||||
Order: 1, | |||||
PostLinks: []Models.PostLink{ | |||||
{ | |||||
Type: "Facebook", | |||||
Link: "http://facebook.com/", | |||||
}, | |||||
}, | |||||
} | |||||
err = Database.CreatePost(&postData) | |||||
return postData, err | |||||
} | |||||
func SeedPosts() { | |||||
var ( | |||||
userData Models.User | |||||
i int | |||||
err error | |||||
) | |||||
err = Database.DB. | |||||
Model(Models.User{}). | |||||
First(&userData). | |||||
Error | |||||
if err != nil { | |||||
panic(err) | |||||
} | |||||
for i = 0; i <= 20; i++ { | |||||
_, err = createPost(userData) | |||||
if err != nil { | |||||
panic(err) | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,61 @@ | |||||
<template> | |||||
<div class="row mb-3"> | |||||
<div class="col-12"> | |||||
<div class="page-nav-container"> | |||||
<div class="row"> | |||||
<div class="col-md-6 col-10"> | |||||
<div class="input-group"> | |||||
<input | |||||
type="text" | |||||
class="form-control" | |||||
placeholder="Search..." | |||||
ref="search" | |||||
> | |||||
<div class="input-group-append"> | |||||
<button | |||||
class="btn btn-dark" | |||||
type="button" | |||||
@click="searchFunction" | |||||
> | |||||
Search | |||||
</button> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
<div class="col-md-6 float-right col-2"> | |||||
<div class="btn-group float-right" role="group"> | |||||
<router-link :to="{ name: addNewTo }"> | |||||
<button | |||||
type="button" | |||||
class="btn btn-rounded btn-dark d-none d-md-inline-block" | |||||
> | |||||
<i class="fa-solid fa-plus"></i> | |||||
{{ addNewLabel }} | |||||
</button> | |||||
<button | |||||
type="button" | |||||
class="btn btn-rounded btn-dark d-inline-block d-md-none" | |||||
> | |||||
<i class="fa-solid fa-plus"></i> | |||||
</button> | |||||
</router-link> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
</template> | |||||
<script> | |||||
export default { | |||||
props: { | |||||
addNewTo: { type: String }, | |||||
addNewLabel: { type: String }, | |||||
searchFunction: { type: Function }, | |||||
} | |||||
} | |||||
</script> |
@ -0,0 +1,156 @@ | |||||
<template> | |||||
<div id="admin-page-container"> | |||||
<admin-navbar/> | |||||
<section class="container mt-5"> | |||||
<div class="row mb-3"> | |||||
<div class="col-12"> | |||||
<div class="page-nav-container"> | |||||
<div class="btn-group" role="group"> | |||||
<button | |||||
type="button" | |||||
class="btn btn-rounded" | |||||
:class="tab === 'details' ? 'btn-dark' : 'btn-outline-dark'" | |||||
@click="tab = 'details'" | |||||
> | |||||
Post Details | |||||
</button> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
<div class="card shadow-2-strong card-registration"> | |||||
<div class="card-body p-4 p-md-5" v-if="tab === 'details'"> | |||||
<h3 class="mb-4 pb-2 pb-md-0 mb-md-5">Update Post</h3> | |||||
<Form @submit="updatePost" v-slot="{ meta, errors }"> | |||||
<div class="row"> | |||||
<div class="col-md-8 mb-4"> | |||||
<div class="form-outline"> | |||||
<Field | |||||
v-model="post.title" | |||||
type="text" | |||||
id="title" | |||||
name="Title" | |||||
class="form-control form-control-lg" | |||||
:class="errors['Title'] ? 'invalid' : ''" | |||||
rules="required"/> | |||||
<label v-if="!errors['Title']" class="form-label" for="title">Title</label> | |||||
<ErrorMessage name="Title" as="label" class="form-label" for="title"/> | |||||
</div> | |||||
</div> | |||||
<div class="col-md-4 mb-4"> | |||||
<div class="form-outline"> | |||||
<select | |||||
v-model="post.front_page" | |||||
id="front_page" | |||||
name="Front Page" | |||||
class="form-control form-control-lg form-select"> | |||||
<option :value="true">Yes</option> | |||||
<option :value="false">No</option> | |||||
</select> | |||||
<label v-if="!errors['Front Page']" class="form-label" for="front_page">Front Page</label> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
<div class="row"> | |||||
<div class="col-md-4 mb-4 pb-2"> | |||||
<div class="form-outline"> | |||||
<date-picker | |||||
v-model="post.created_at" | |||||
format="dd/MM/yyyy, HH:mm" | |||||
disabled="disabled" | |||||
id="created_at"/> | |||||
<label class="form-label" for="created_at">Created At</label> | |||||
</div> | |||||
</div> | |||||
<div class="col-md-4 mb-4 pb-2"> | |||||
<div class="form-outline"> | |||||
<date-picker | |||||
v-model="post.updated_at" | |||||
format="dd/MM/yyyy, HH:mm" | |||||
disabled="disabled" | |||||
id="updated_at"/> | |||||
<label class="form-label" for="updated_at">Updated At</label> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
<div class="mt-2 pt-2 right-align"> | |||||
<button class="btn btn-danger btn-md" type="button"> | |||||
Delete | |||||
</button> | |||||
<button :disabled="!meta.touched || !meta.valid" class="btn btn-primary btn-md" type="submit"> | |||||
Update | |||||
</button> | |||||
</div> | |||||
</Form> | |||||
</div> | |||||
</div> | |||||
</section> | |||||
</div> | |||||
</template> | |||||
<script> | |||||
import AdminNavbar from '@/components/admin/components/navbar/AdminNavbar' | |||||
import { Form, Field, ErrorMessage } from 'vee-validate' | |||||
export default { | |||||
data() { | |||||
return { | |||||
tab: 'details', | |||||
post: {}, | |||||
} | |||||
}, | |||||
components: { | |||||
AdminNavbar, | |||||
Form, | |||||
Field, | |||||
ErrorMessage, | |||||
}, | |||||
mounted () { | |||||
this.getPost() | |||||
}, | |||||
methods: { | |||||
async getPost () { | |||||
try { | |||||
const response = await this.axios.get(`/post/${this.$route.params.id}`) | |||||
if (response.status === 200) { | |||||
this.post = response.data | |||||
} | |||||
} catch (error) { | |||||
this.$toast.error('An error occurred.') | |||||
} | |||||
}, | |||||
async updatePost () { | |||||
try { | |||||
let response = await this.axios.put( | |||||
`/user/${this.$route.params.id}`, | |||||
{ | |||||
first_name: this.user.first_name, | |||||
last_name: this.user.last_name, | |||||
email: this.user.email, | |||||
}, | |||||
) | |||||
if (response.status === 200) { | |||||
this.$toast.success('Successfully updated user details.'); | |||||
this.setPostFromResponse(response) | |||||
} | |||||
} catch (error) { | |||||
this.$toast.error('An error occured'); | |||||
} | |||||
}, | |||||
} | |||||
} | |||||
</script> |
@ -0,0 +1,139 @@ | |||||
<template> | |||||
<div id="admin-page-container"> | |||||
<admin-navbar/> | |||||
<div class="container table-responsive mt-5 pb-5"> | |||||
<admin-list-header | |||||
addNewTo="AdminUsersCreate" | |||||
addNewLabel="Add Post" | |||||
:searchFunction="searchPosts" | |||||
ref="listHeader" | |||||
/> | |||||
<div class="card shadow-2-strong card-registration"> | |||||
<table class="table table-striped"> | |||||
<thead class="thead-dark"> | |||||
<tr> | |||||
<th scope="col">Title</th> | |||||
<th scope="col">Front Page</th> | |||||
<th scope="col" class="d-none d-sm-table-cell">Created At</th> | |||||
<th scope="col" class="d-none d-sm-table-cell">Published At</th> | |||||
<th scope="col"></th> | |||||
</tr> | |||||
</thead> | |||||
<tbody> | |||||
<tr v-for="post in posts" :key="post.id"> | |||||
<td class="align-middle">{{ post.title }}</td> | |||||
<td class="align-middle">{{ post.front_page }}</td> | |||||
<td class="align-middle d-none d-sm-table-cell">{{ formatDate(post.created_at) }}</td> | |||||
<td v-if="post.published_at" class="align-middle d-none d-sm-table-cell">{{ formatDate(post.published_at) }}</td> | |||||
<td v-if="!post.published_at" class="align-middle d-none d-sm-table-cell">-</td> | |||||
<td class="align-middle"> | |||||
<router-link | |||||
:to="{ name: 'AdminPostsForm', params: { id: post.id } }" | |||||
> | |||||
<button | |||||
class="btn btn-outline-dark" | |||||
> | |||||
Open | |||||
</button> | |||||
</router-link> | |||||
</td> | |||||
</tr> | |||||
</tbody> | |||||
</table> | |||||
<p v-if="dataEnd" class="py-2 center-align text-muted">No more data</p> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
</template> | |||||
<script> | |||||
import AdminNavbar from '@/components/admin/components/navbar/AdminNavbar' | |||||
import AdminListHeader from '@/components/admin/components/list/AdminListHeader.vue' | |||||
export default { | |||||
data() { | |||||
return { | |||||
posts: [], | |||||
page: 0, | |||||
pageSize: 15, | |||||
search: '', | |||||
dataEnd: false, | |||||
} | |||||
}, | |||||
components: { | |||||
AdminNavbar, | |||||
AdminListHeader, | |||||
}, | |||||
beforeMount () { | |||||
this.getInitialPosts() | |||||
}, | |||||
mounted () { | |||||
this.axios.get('/admin/me') | |||||
this.getNextPosts() | |||||
}, | |||||
methods: { | |||||
formatDate (dateString) { | |||||
const d = new Date(dateString) | |||||
let hours = d.getHours(); | |||||
let minutes = d.getMinutes(); | |||||
const ampm = hours >= 12 ? 'pm' : 'am'; | |||||
hours = hours % 12; | |||||
hours = hours ? hours : 12; | |||||
minutes = minutes < 10 ? '0'+minutes : minutes; | |||||
const strTime = hours + ':' + minutes + ' ' + ampm; | |||||
return d.getDate() + "/" + (d.getMonth()+1) + "/" + d.getFullYear() + " " + strTime; | |||||
}, | |||||
async getInitialPosts () { | |||||
try { | |||||
const response = await this.axios.get( | |||||
`/post?page=${this.page}&pageSize=${this.pageSize}&search=${this.search}` | |||||
) | |||||
if (response.status === 200) { | |||||
this.posts = response.data | |||||
} | |||||
} catch (error) { | |||||
if (error.response.status === 404) { | |||||
this.posts = {} | |||||
this.dataEnd = true | |||||
} | |||||
} | |||||
}, | |||||
async getNextPosts () { | |||||
window.onscroll = async () => { | |||||
let bottomOfWindow = document.documentElement.scrollTop + window.innerHeight === document.documentElement.offsetHeight; | |||||
if (bottomOfWindow) { | |||||
try { | |||||
this.page += 1 | |||||
const response = await this.axios.get( | |||||
`/post?page=${this.page}&pageSize=${this.pageSize}&search=${this.search}` | |||||
) | |||||
if (response.status === 200) { | |||||
this.posts.push(...response.data) | |||||
} | |||||
} catch (error) { | |||||
console.log(error) | |||||
if (error.response.status === 404) { | |||||
this.dataEnd = true | |||||
} | |||||
} | |||||
} | |||||
} | |||||
}, | |||||
searchPosts () { | |||||
this.search = this.$refs.listHeader.$refs.search.value | |||||
this.getInitialPosts() | |||||
} | |||||
} | |||||
} | |||||
</script> |