| @ -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> | |||