before suspend
This commit is contained in:
@@ -17,7 +17,7 @@
|
||||
</div>
|
||||
<router-link
|
||||
v-else
|
||||
v-bind="$props"
|
||||
:to="{ name: props.to }"
|
||||
>
|
||||
<span
|
||||
:class="`fa fa-lg fa-fw ${fa}`"
|
||||
@@ -29,21 +29,20 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
import { RouterLink } from 'vue-router'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
...RouterLink.props,
|
||||
fa: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
const props = defineProps({
|
||||
to: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
computed: {
|
||||
isExternal() {
|
||||
return typeof this.to === 'string' && this.to.startsWith('http')
|
||||
}
|
||||
fa: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const isExternal = computed(() => props.to.startsWith('http'))
|
||||
</script>
|
||||
|
@@ -2,10 +2,10 @@
|
||||
<div class="container box">
|
||||
<form @submit.prevent="handleSubmit">
|
||||
<div
|
||||
v-if="error"
|
||||
class="alert alert-danger"
|
||||
v-if="errorMessage"
|
||||
class="alert alert-danger mt-2"
|
||||
>
|
||||
{{ error }}
|
||||
{{ errorMessage }}
|
||||
</div>
|
||||
<div class="form-group mt-2">
|
||||
<label for="exampleInputEmail1">Username</label>
|
||||
@@ -52,57 +52,44 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { ref, defineEmits } from 'vue'
|
||||
import axios from 'axios'
|
||||
|
||||
export default {
|
||||
name: 'LoginForm',
|
||||
/*
|
||||
props: {
|
||||
user: {
|
||||
Type: Object
|
||||
}
|
||||
},
|
||||
*/
|
||||
data() {
|
||||
return {
|
||||
username: '',
|
||||
password: '',
|
||||
error: '',
|
||||
isLoading: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleSubmit() {
|
||||
console.log('handle submit')
|
||||
this.isLoading = true
|
||||
this.error = ''
|
||||
const username = ref('')
|
||||
const password = ref('')
|
||||
const errorMessage = ref('')
|
||||
const isLoading = ref(false)
|
||||
const emit = defineEmits(['user-authenticated'])
|
||||
|
||||
axios
|
||||
.post('/login', {
|
||||
username: this.username,
|
||||
password: this.password
|
||||
})
|
||||
.then((response) => {
|
||||
console.log(response.headers)
|
||||
const handleSubmit = () => {
|
||||
//console.log('handle submit')
|
||||
isLoading.value = true
|
||||
|
||||
this.$emit('user-authenticated', response.headers.location)
|
||||
this.username = ''
|
||||
this.password = ''
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error.response.data)
|
||||
if (error.response.data.error) {
|
||||
this.error = error.response.data.error
|
||||
} else {
|
||||
this.error = 'Unknown error'
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.isLoading = false
|
||||
})
|
||||
}
|
||||
}
|
||||
axios
|
||||
.post('/login', {
|
||||
username: username.value,
|
||||
password: password.value
|
||||
})
|
||||
.then((response) => {
|
||||
//console.log(response.headers)
|
||||
|
||||
emit('user-authenticated', response.headers.location)
|
||||
username.value = ''
|
||||
password.value = ''
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error)
|
||||
console.log(error.response.data)
|
||||
if (error.response.data.error) {
|
||||
errorMessage.value = error.response.data.error
|
||||
} else {
|
||||
errorMessage.value = 'Unknown error'
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
isLoading.value = false
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
|
14
assets/js/components/TheAbout.vue
Normal file
14
assets/js/components/TheAbout.vue
Normal file
@@ -0,0 +1,14 @@
|
||||
<template>
|
||||
<div>
|
||||
Bio:
|
||||
|
||||
Symfonycast
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
name: 'TheAbout'
|
||||
}
|
||||
</script>
|
@@ -14,6 +14,14 @@
|
||||
alt="Spookie"
|
||||
>
|
||||
</router-link>
|
||||
<div>
|
||||
<router-link
|
||||
:to="{ name: 'About'}"
|
||||
class="d-inline-block mx-auto"
|
||||
>
|
||||
About Me
|
||||
</router-link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col md-4 text-left">
|
||||
|
@@ -116,37 +116,30 @@
|
||||
</nav>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import axios from 'axios'
|
||||
|
||||
import 'vue-navigation-bar/dist/vue-navigation-bar.css'
|
||||
|
||||
export default {
|
||||
name: 'TheNavbar',
|
||||
components: {},
|
||||
props: {
|
||||
user: {
|
||||
type: Object,
|
||||
default: null
|
||||
const props = defineProps({
|
||||
user: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {
|
||||
username: 'tracer'
|
||||
}
|
||||
}
|
||||
},
|
||||
data: () => ({}),
|
||||
computed: {
|
||||
isLoggedIn() {
|
||||
return !!this.user
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
logout() {
|
||||
console.log('logout')
|
||||
axios
|
||||
.get('/logout')
|
||||
.then(this.$emit('invalidate-user'))
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
})
|
||||
|
||||
<style>
|
||||
</style>
|
||||
const isLoggedIn = () => (!!this.user)
|
||||
|
||||
/*
|
||||
const logout = () => ({
|
||||
axios
|
||||
.get('/logout')
|
||||
.then(this.$emit('invalidate-user'))
|
||||
|
||||
})
|
||||
|
||||
*/
|
||||
</script>
|
||||
|
@@ -30,6 +30,11 @@ export default {
|
||||
url: 'Projects',
|
||||
fa: 'fa-file-code-o',
|
||||
},
|
||||
{
|
||||
name: 'Blog',
|
||||
url: 'Blog',
|
||||
fa: 'fa-file-text-o',
|
||||
},
|
||||
{
|
||||
name: 'Gitea',
|
||||
url: 'https://git.24unix.net',
|
||||
|
38
assets/js/components/blog/BlogCard.vue
Normal file
38
assets/js/components/blog/BlogCard.vue
Normal file
@@ -0,0 +1,38 @@
|
||||
<template>
|
||||
<div
|
||||
class="
|
||||
flex
|
||||
m-2
|
||||
gap-2
|
||||
items-center
|
||||
shadow-md
|
||||
w-1/4
|
||||
flex-grow
|
||||
rounded
|
||||
overflow-hidden
|
||||
"
|
||||
style="border: 1px solid #eee"
|
||||
>
|
||||
<img
|
||||
src="https://via.placeholder.com/150"
|
||||
style="background: #cccccc"
|
||||
width="150"
|
||||
height="150"
|
||||
alt="placeholder"
|
||||
>
|
||||
<router-link :to="{ name: 'BlogPost', params: { id: post.id } }">
|
||||
{{ post.title }}<br>
|
||||
User: {{ post.userId }}
|
||||
</router-link>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
console.log('BlogCard')
|
||||
const props = defineProps({
|
||||
post: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
})
|
||||
const post = { ...props.post }
|
||||
</script>
|
34
assets/js/components/blog/BlogIndex.vue
Normal file
34
assets/js/components/blog/BlogIndex.vue
Normal file
@@ -0,0 +1,34 @@
|
||||
<template>
|
||||
<div
|
||||
class="
|
||||
flex
|
||||
m-2
|
||||
gap-2
|
||||
items-center
|
||||
shadow-md
|
||||
w-1/4
|
||||
flex-grow
|
||||
rounded
|
||||
overflow-hidden
|
||||
"
|
||||
style="border: 1px solid #eee"
|
||||
>
|
||||
BlogIndex
|
||||
|
||||
<BlogCard
|
||||
v-for="post in blogPosts"
|
||||
:key="post.id"
|
||||
:post="post"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import useResource from '@/composables/useResource'
|
||||
//import useUser from '@/composables/useUser'
|
||||
import BlogCard from '@/components/blog/BlogCard'
|
||||
|
||||
const { items: blogPosts, fetchAll } = useResource('https://jsonplaceholder.typicode.com/posts')
|
||||
fetchAll()
|
||||
|
||||
</script>
|
51
assets/js/components/blog/BlogPost.vue
Normal file
51
assets/js/components/blog/BlogPost.vue
Normal file
@@ -0,0 +1,51 @@
|
||||
<template>
|
||||
<div
|
||||
v-if="blogPost"
|
||||
class="
|
||||
flex
|
||||
m-2
|
||||
gap-2
|
||||
items-center
|
||||
shadow-md
|
||||
w-1/4
|
||||
flex-grow
|
||||
rounded
|
||||
overflow-hidden
|
||||
"
|
||||
style="border: 1px solid #eee"
|
||||
>
|
||||
BlogIPost
|
||||
<img
|
||||
src="https://via.placeholder.com/150"
|
||||
style="background: #cccccc"
|
||||
width="150"
|
||||
height="150"
|
||||
alt="placeholder"
|
||||
>
|
||||
user {{ blogPost.userId }}:
|
||||
<div v-if="user">
|
||||
"{{ user.name }}
|
||||
</div>
|
||||
<br>
|
||||
{{ blogPost.title }}<br>
|
||||
{{ blogPost.body }}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { watch } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import useResource from '@/composables/useResource'
|
||||
|
||||
const route = useRoute()
|
||||
|
||||
const { item: blogPost, fetchOne: fetchOneBlog } = useResource('https://jsonplaceholder.typicode.com/posts')
|
||||
fetchOneBlog(route.params.id)
|
||||
|
||||
const { item: user, fetchOne: fetchOneUser } = useResource('https://jsonplaceholder.typicode.com/users')
|
||||
watch(
|
||||
() => ({ ...blogPost.value }),
|
||||
() => fetchOneUser(blogPost.value.userId)
|
||||
)
|
||||
|
||||
</script>
|
60
assets/js/components/pages/PagesIndex.vue
Normal file
60
assets/js/components/pages/PagesIndex.vue
Normal file
@@ -0,0 +1,60 @@
|
||||
<template>
|
||||
<div class="box ma7 p-5">
|
||||
<h2
|
||||
v-if="page"
|
||||
v-html="page.name"
|
||||
/>
|
||||
<div
|
||||
v-if="page"
|
||||
v-html="page.content"
|
||||
/>
|
||||
|
||||
<router-link
|
||||
:to="editTarget"
|
||||
variant="outline"
|
||||
>
|
||||
<button>
|
||||
<i class="fa fa-2x fa-fw fa-edit"/>
|
||||
</button>
|
||||
</router-link>
|
||||
<hr>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive, watchEffect, computed } from 'vue'
|
||||
import axios from 'axios'
|
||||
//import h from 'highlightjs'
|
||||
//import hi from 'highlightjs-line-numbers.js'
|
||||
|
||||
//hi.highlightAll()
|
||||
//hi.initLineNumbersOnLoad()
|
||||
|
||||
const props = defineProps({
|
||||
slug: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
})
|
||||
|
||||
let page = reactive({ name: '', slug: '' })
|
||||
|
||||
const editTarget = computed(() => {
|
||||
if (page) {
|
||||
return `/pages/edit/${page.slug}`
|
||||
}
|
||||
return ''
|
||||
})
|
||||
|
||||
const getPagesDetails = async (slug) => {
|
||||
console.log('get page details')
|
||||
await axios.get(`/api/pages?slug=${slug}`)
|
||||
.then((response) => {
|
||||
page = response.data['hydra:member']
|
||||
console.log(page)
|
||||
})
|
||||
}
|
||||
|
||||
// watch fpr slug
|
||||
watchEffect(() => getPagesDetails(props.slug))
|
||||
</script>
|
@@ -6,28 +6,57 @@
|
||||
<tabs-group @tabActivated="tabActivated">
|
||||
<tab-content title="Visual Editor">
|
||||
<div class="editor mt-2">
|
||||
<VueEditor
|
||||
v-model="page.content"
|
||||
:editor-options="editorOptions"
|
||||
<QuillEditor
|
||||
ref="editor"
|
||||
theme="snow"
|
||||
:content="JSON.parse(page.content)"
|
||||
:options="editorOptions"
|
||||
:disabled="isVueEditorDisabled"
|
||||
/>
|
||||
<button
|
||||
class="btn btn-primary mt-3 mb-2"
|
||||
@click="saveContent"
|
||||
>
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
</tab-content>
|
||||
<tab-content title="Raw Content">
|
||||
<tab-content title="Preview">
|
||||
<div class="preview mt-2 no-overflow">
|
||||
<QuillEditor
|
||||
ref="preview"
|
||||
theme="bubble"
|
||||
class="no-overflow"
|
||||
:content="JSON.parse(page.content)"
|
||||
:options="editorOptions"
|
||||
:disabled="true"
|
||||
/>
|
||||
</div>
|
||||
<!--
|
||||
<div>
|
||||
<textarea
|
||||
v-model="page.content"
|
||||
class="p-fluid pages-editor-raw"
|
||||
/>
|
||||
</div>
|
||||
-->
|
||||
</tab-content>
|
||||
<tab-content title="Delta">
|
||||
<div class="editor mt-2">
|
||||
<textarea
|
||||
ref="delta"
|
||||
v-model="page.content"
|
||||
class="p-fluid pages-editor-raw"
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
class="btn btn-primary mt-3 mb-2"
|
||||
@click="saveDelta"
|
||||
>
|
||||
Save
|
||||
</button>
|
||||
</tab-content>
|
||||
</tabs-group>
|
||||
<button
|
||||
class="btn btn-primary mt-3 mb-2"
|
||||
@click="saveContent"
|
||||
>
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
<div v-else>
|
||||
<i class="fa fa-spinner fa-spin fa-3x fa-fw"/>
|
||||
@@ -39,20 +68,31 @@
|
||||
|
||||
import axios from 'axios'
|
||||
|
||||
// Basic Use - Covers most scenarios
|
||||
// https://github.com/davidroyer/vue2-editor => docs apply for version 3, too
|
||||
import { VueEditor } from 'vue3-editor'
|
||||
import 'highlight.js/styles/tomorrow-night-blue.css'
|
||||
import hljs from 'highlight.js'
|
||||
|
||||
import hljs from 'highlightjs'
|
||||
import 'highlightjs/styles/dracula.css'
|
||||
//import Quill from 'quill'
|
||||
import { Quill, QuillEditor } from '@vueup/vue-quill'
|
||||
import '@vueup/vue-quill/dist/vue-quill.snow.css'
|
||||
import '@vueup/vue-quill/dist/vue-quill.bubble.css'
|
||||
|
||||
import htmlEditButton from 'quill-html-edit-button'
|
||||
|
||||
import TabsGroup from '@/components/tabs/TabsGroup'
|
||||
import TabContent from '@/components/tabs/TabContent'
|
||||
|
||||
Quill
|
||||
// .register('modules/syntax', syntax, false)
|
||||
.register('modules/htmlEditButton', htmlEditButton)
|
||||
|
||||
hljs.configure({
|
||||
languages: ['javascript', 'php', 'swift']
|
||||
})
|
||||
|
||||
export default {
|
||||
name: 'PagesEdit',
|
||||
components: {
|
||||
VueEditor,
|
||||
QuillEditor,
|
||||
TabsGroup,
|
||||
TabContent
|
||||
},
|
||||
@@ -62,12 +102,15 @@ export default {
|
||||
isVueEditorDisabled: false,
|
||||
editorOptions: {
|
||||
modules: {
|
||||
htmlEditButton: {
|
||||
debug: true
|
||||
},
|
||||
syntax: {
|
||||
highlight: (text) => hljs.highlightAuto(text).value
|
||||
}
|
||||
},
|
||||
theme: 'snow'
|
||||
}
|
||||
}
|
||||
},
|
||||
theme: 'snow'
|
||||
}),
|
||||
beforeMount() {
|
||||
const { slug } = this.$route.params
|
||||
@@ -81,13 +124,24 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
saveContent() {
|
||||
this.page.content = JSON.stringify(this.$refs.editor.getContents())
|
||||
axios
|
||||
.put(`/api/pages/${this.page.id}`, this.page)
|
||||
.then(() => {
|
||||
this.$router.push({
|
||||
name: 'Pages',
|
||||
params: { slug: this.page.slug }
|
||||
})
|
||||
//this.$router.push({
|
||||
// name: 'Pages',
|
||||
// params: { slug: this.page.slug }
|
||||
//})
|
||||
})
|
||||
},
|
||||
saveDelta() {
|
||||
axios
|
||||
.put(`/api/pages/${this.page.id}`, this.page)
|
||||
.then(() => {
|
||||
//this.$router.push({
|
||||
// name: 'Pages',
|
||||
// params: { slug: this.page.slug }
|
||||
//})
|
||||
})
|
||||
},
|
||||
tabActivated(name) {
|
||||
|
@@ -1,69 +0,0 @@
|
||||
<template>
|
||||
<div class="box ma7 p-5">
|
||||
<h2
|
||||
v-if="page"
|
||||
v-html="page.name"
|
||||
/>
|
||||
<div
|
||||
v-if="page"
|
||||
v-html="page.content"
|
||||
/>
|
||||
|
||||
<router-link
|
||||
:to="editTarget"
|
||||
variant="outline"
|
||||
>
|
||||
<button>
|
||||
<i class="fa fa-2x fa-fw fa-edit"/>
|
||||
</button>
|
||||
</router-link>
|
||||
<hr>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import axios from 'axios'
|
||||
//import h from 'highlightjs'
|
||||
//import hi from 'highlightjs-line-numbers.js'
|
||||
|
||||
//hi.highlightAll()
|
||||
//hi.initLineNumbersOnLoad()
|
||||
|
||||
export default {
|
||||
name: 'PagesDisplay',
|
||||
props: {
|
||||
slug: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data: () => ({
|
||||
page: null
|
||||
}),
|
||||
computed: {
|
||||
editTarget() {
|
||||
if (this.page) {
|
||||
return `/pages/edit/${this.page.slug}`
|
||||
}
|
||||
return ''
|
||||
}
|
||||
},
|
||||
beforeMount() {
|
||||
this.getPagesDetails()
|
||||
this.$watch(
|
||||
() => this.slug,
|
||||
() => {
|
||||
this.getPagesDetails()
|
||||
}
|
||||
)
|
||||
},
|
||||
methods: {
|
||||
async getPagesDetails() {
|
||||
await axios.get(`/api/pages?slug=${this.slug}`)
|
||||
.then((response) => {
|
||||
[this.page] = response.data['hydra:member']
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
@@ -5,6 +5,7 @@
|
||||
v-for="title in tabTitles"
|
||||
:key="title"
|
||||
:class="{ selected: title === selectedTitle }"
|
||||
class="btn"
|
||||
@click="handleClick(title)"
|
||||
>
|
||||
{{ title }}
|
||||
@@ -39,7 +40,9 @@ export default {
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
<style lang="scss">
|
||||
|
||||
@import "~styles/app.scss";
|
||||
.tabs {
|
||||
width: auto;
|
||||
margin: 0 auto;
|
||||
@@ -56,15 +59,20 @@ export default {
|
||||
text-align: center;
|
||||
padding: 10px 20px;
|
||||
margin-right: 10px;
|
||||
background-color: #ddd;
|
||||
background-color: #2e2e2e;
|
||||
border-radius: 5px;
|
||||
color: black;
|
||||
color: #999999;
|
||||
cursor: pointer;
|
||||
transition: 0.4s all ease-out;
|
||||
}
|
||||
|
||||
.tabs-header li.selected {
|
||||
background-color: #0984e3;
|
||||
color: white;
|
||||
background-color: $primary;
|
||||
color: $jet-black;
|
||||
}
|
||||
|
||||
.tabs-header li.selected:hover {
|
||||
border-color: $body-color;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
@@ -50,36 +50,38 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { ref, reactive } from 'vue'
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
import axios from 'axios'
|
||||
|
||||
export default {
|
||||
name: 'ProfileView',
|
||||
data: () => ({
|
||||
user: null,
|
||||
isLoading: true
|
||||
}),
|
||||
computed: {
|
||||
getUserEndpoint() {
|
||||
if (this.$route.params.username) {
|
||||
return `/api/users?username=${this.$route.params.username}`
|
||||
}
|
||||
return '/api/users?username=tracer'
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
axios
|
||||
.get(this.getUserEndpoint)
|
||||
.then((response) => {
|
||||
console.log(response);
|
||||
[this.user] = response.data['hydra:member']
|
||||
const user = reactive(null)
|
||||
const isLoading = ref(true)
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
|
||||
console.log(this.user)
|
||||
this.isLoading = false
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error)
|
||||
})
|
||||
}
|
||||
// if there is no param, we go to our own profile, elso to the login
|
||||
|
||||
if (route.params.username) {
|
||||
console.log('we have the username')
|
||||
} else if (window.user) {
|
||||
console.log(window.user)
|
||||
} else {
|
||||
router.push({ name: 'LoginForm' })
|
||||
}
|
||||
|
||||
const userEndpoint = '/api/users?username=tracer'
|
||||
axios
|
||||
.get(userEndpoint)
|
||||
.then((response) => {
|
||||
//console.log(response);
|
||||
[user.value] = response.data['hydra:member']
|
||||
|
||||
//console.log(this.user)
|
||||
isLoading.value = false
|
||||
})
|
||||
.catch(() => {
|
||||
//console.log(error)
|
||||
})
|
||||
|
||||
</script>
|
||||
|
Reference in New Issue
Block a user