diff options
author | vnugent <public@vaughnnugent.com> | 2023-07-12 01:28:23 -0400 |
---|---|---|
committer | vnugent <public@vaughnnugent.com> | 2023-07-12 01:28:23 -0400 |
commit | f64955c69d91e578e580b409ba31ac4b3477da96 (patch) | |
tree | 16f01392ddf1abfea13d7d1ede3bfb0459fe8f0d /front-end/src/views/Blog/components/Posts |
Initial commit
Diffstat (limited to 'front-end/src/views/Blog/components/Posts')
-rw-r--r-- | front-end/src/views/Blog/components/Posts/PostEdit.vue | 155 | ||||
-rw-r--r-- | front-end/src/views/Blog/components/Posts/PostTable.vue | 62 |
2 files changed, 217 insertions, 0 deletions
diff --git a/front-end/src/views/Blog/components/Posts/PostEdit.vue b/front-end/src/views/Blog/components/Posts/PostEdit.vue new file mode 100644 index 0000000..4f7b52b --- /dev/null +++ b/front-end/src/views/Blog/components/Posts/PostEdit.vue @@ -0,0 +1,155 @@ +<template> + <div id="new-post-editor" class="flex flex-col w-full"> + <div class="my-4 ml-auto"> + <div class="button-group"> + <!-- Submit the post form --> + <button class="btn primary" form="post-edit-form">Save</button> + <button class="btn" @click="onClose">Cancel</button> + </div> + </div> + <div class="mx-auto"> + <h4 class="text-center">Edit Post</h4> + <p> + Add or edit a post to publish to your blog. + </p> + </div> + <div class="relative"> + <div class="absolute top-2 right-10"> + <button class="btn no-border" @click="setMeAsAuthor">@Me</button> + </div> + </div> + <dynamic-form + id="post-edit-form" + class="mx-auto" + :form="schema" + :disabled="false" + :validator="v$" + @submit="onSubmit" + /> + + <div id="post-content-editor" class="px-6" :class="{'invalid':v$.content.$invalid}"> + <Editor @change="onContentChanged" :blog="$props.blog" @load="onEditorLoad" /> + </div> + + <FeedFields :properties="postProperties" :blog="$props.blog" /> + + <div class="mx-auto my-4"> + <div class="button-group"> + <!-- Submit the post form --> + <button class="btn primary" form="post-edit-form">Save</button> + <button class="btn" @click="onClose">Cancel</button> + <button v-if="!isNew" class="btn red" @click="onDelete">Delete Forever</button> + </div> + </div> + </div> +</template> +<script setup lang="ts"> +import { computed } from 'vue'; +import { BlogState } from '../../blog-api'; +import { reactiveComputed } from '@vueuse/core'; +import { isNil, isString, split } from 'lodash'; +import { PostMeta, useXmlProperties } from '@vnuge/cmnext-admin'; +import { apiCall, useConfirm, useUser } from '@vnuge/vnlib.browser'; +import { getPostForm } from '../../form-helpers'; +import Editor from '../../ckeditor/Editor.vue'; +import FeedFields from '../FeedFields.vue'; + +const emit = defineEmits(['close', 'submit', 'delete']); +const props = defineProps<{ + blog: BlogState +}>() + +const { reveal } = useConfirm(); +const { getProfile } = useUser(); +const { schema, getValidator } = getPostForm(); + +const { posts, content } = props.blog; + +const isNew = computed(() => isNil(posts.selectedItem.value)); + +/* Post meta may load delayed from the api so it must be computed +and reactive, it may also be empty when a new post is created */ +const postBuffer = reactiveComputed<PostMeta>(() => { + return { + ...posts.selectedItem.value, + content: '' + } as PostMeta +}); + +const { v$, validate } = getValidator(postBuffer); + +//Wrap the post properties in an xml feed editor +const postProperties = useXmlProperties(posts.selectedItem); + +const onSubmit = async () =>{ + if(!await validate()){ + return; + } + + //get all properties + const p = postProperties.getCurrentProperties(); + + const post = { + ...postBuffer, + properties: p, + content: undefined + } + + //Remove the content from the post object + delete post.content; + + //Convert the tags string to an array of strings + post.tags = isString(post.tags) ? split(post.tags, ',') : post.tags; + + emit('submit', { post, content: v$.value.content.$model}); +} + +const onClose = () => emit('close'); + +const onContentChanged = (content: string) => { + //Set the validator content string + v$.value.content.$model = content; +} + +const onDelete = async () => { + //Show confirm + const { isCanceled } = await reveal({ + title: 'Delete Post?', + text: 'Are you sure you want to delete this post? This action cannot be undone.', + }) + if (isCanceled) { + return; + } + + if (!confirm('Are you sure you want to delete this post forever?')) { + return; + } + + //Emit the delete event with the original post + emit('delete', posts.selectedItem.value) +} + +const setMeAsAuthor = () => { + apiCall(async () => { + const { first, last } = await getProfile<{first?:string, last?:string, email:string}>(); + v$.value.author.$model = `${first} ${last}` + }) +} + +const onEditorLoad = async (editor : any) =>{ + + //Get the initial content + const postContent = await content.getSelectedPostContent(); + + //Set the initial content + if(!isNil(postContent)){ + onContentChanged(postContent); + editor.setData(postContent); + } +} + +</script> + +<style lang="scss"> + +</style>
\ No newline at end of file diff --git a/front-end/src/views/Blog/components/Posts/PostTable.vue b/front-end/src/views/Blog/components/Posts/PostTable.vue new file mode 100644 index 0000000..e5e45f2 --- /dev/null +++ b/front-end/src/views/Blog/components/Posts/PostTable.vue @@ -0,0 +1,62 @@ +<template> + <thead> + <tr> + <th>Title</th> + <th>Id</th> + <th>Date</th> + <th>Author</th> + <th>Summary</th> + <th></th> + </tr> + </thead> + <tbody> + <tr v-for="post in posts" :key="post.id" class="table-row"> + <td> + {{ post.title }} + </td> + <td> + {{ getPostId(post) }} + </td> + <td> + {{ getDateString(post.date) }} + </td> + <td> + {{ post.author }} + </td> + <td> + {{ getSummaryString(post.summary) }} + </td> + <td class="w-20"> + <button class="btn xs no-border" @click="copy(post.id)"> + <fa-icon icon="copy" /> + </button> + <button class="btn xs no-border" @click="openEdit(post)"> + <fa-icon icon="pencil" /> + </button> + </td> + </tr> + </tbody> +</template> + +<script setup lang="ts"> +import { toRefs } from 'vue'; +import { filter as _filter, truncate } from 'lodash'; +import { useClipboard } from '@vueuse/core'; +import { PostMeta } from '@vnuge/cmnext-admin'; + +const emit = defineEmits(['reload', 'open-edit']) + +const props = defineProps<{ + posts: PostMeta[], +}>() + +const { posts } = toRefs(props) + +const { copy } = useClipboard() + +const openEdit = async (post: PostMeta) => emit('open-edit', post) + +const getDateString = (time?: number) => new Date((time || 0) * 1000).toLocaleString(); +const getSummaryString = (summary?: string) => truncate(summary || '', { length: 40 }) +const getPostId = (post: PostMeta) => truncate(post.id || '', { length: 20 }) +</script>
\ No newline at end of file |