before suspend
This commit is contained in:
		
							
								
								
									
										24
									
								
								.eslintrc
									
									
									
									
									
								
							
							
						
						
									
										24
									
								
								.eslintrc
									
									
									
									
									
								
							| @@ -11,7 +11,8 @@ | ||||
| 	}, | ||||
| 	"env": { | ||||
| 		"es6": true, | ||||
| 		"browser": true | ||||
| 		"browser": true, | ||||
| 		"vue/setup-compiler-macros": true | ||||
| 	}, | ||||
| 	"rules": { | ||||
| 		"semi": [ | ||||
| @@ -23,11 +24,22 @@ | ||||
| 			"error", | ||||
| 			"tab" | ||||
| 		], | ||||
| 		"vue/html-closing-bracket-spacing": ["error", { | ||||
| 			"startTag": "never", | ||||
| 			"endTag": "never", | ||||
| 			"selfClosingTag": "never" | ||||
| 		}], | ||||
| 		"vue/html-closing-bracket-spacing": [ | ||||
| 			"error", | ||||
| 			{ | ||||
| 				"startTag": "never", | ||||
| 				"endTag": "never", | ||||
| 				"selfClosingTag": "never" | ||||
| 			} | ||||
| 		], | ||||
| 		"vue/component-api-style": [ | ||||
| 			"error", | ||||
| 			[ | ||||
| 				"script-setup", | ||||
| 				"composition" | ||||
| 			] | ||||
| 			// "script-setup", "composition", "composition-vue2", or "options" | ||||
| 		], | ||||
| 		"indent": [ | ||||
| 			"error", | ||||
| 			"tab" | ||||
|   | ||||
							
								
								
									
										8
									
								
								TODO
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								TODO
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| - use quill vue | ||||
| - add linenumbering | ||||
| - add yntax highlighning | ||||
|  | ||||
| - pinia | ||||
| - harden Api-Platform | ||||
| - learn tailwind | ||||
|  | ||||
| @@ -2,20 +2,18 @@ | ||||
| 	<div class="container-fluid"> | ||||
| 		<div class="row"> | ||||
| 			<div class="col"> | ||||
| 				<nav-bar | ||||
| 					:user="user" | ||||
| 					@invalidate-user="onInvalidateUser" | ||||
| 				<the-navbar | ||||
| 					@invalidate-user="onUserInvalidate" | ||||
| 				/> | ||||
| 			</div> | ||||
| 		</div> | ||||
|  | ||||
| 		<div class="row mt-5 main-content"> | ||||
| 			<div class="col-xl-3"> | ||||
| 				<sidebar/> | ||||
| 				<the-sidebar/> | ||||
| 			</div> | ||||
| 			<div class="col-xl-9"> | ||||
| 				<router-view | ||||
| 					:quote="quote" | ||||
| 					@user-authenticated="onUserAuthenticated" | ||||
| 				/> | ||||
| 			</div> | ||||
| @@ -24,58 +22,41 @@ | ||||
|  | ||||
| 		<div class="row"> | ||||
| 			<div class="col"> | ||||
| 				<footer-component/> | ||||
| 				<the-footer/> | ||||
| 			</div> | ||||
| 		</div> | ||||
| 	</div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| <script setup> | ||||
| import axios from 'axios' | ||||
| import { useRoute } from 'vue-router' | ||||
| //import RouterView from 'vue-router' | ||||
| import TheNavbar from '@/components/TheNavbar' | ||||
| import TheSidebar from '@/components/TheSidebar' | ||||
| import TheFooter from '@/components/TheFooter' | ||||
|  | ||||
| import routerView from 'vue-router' | ||||
| import NavBar from '@/components/TheNavbar' | ||||
| import Sidebar from '@/components/TheSidebar' | ||||
| import FooterComponent from '@/components/TheFooter' | ||||
|  | ||||
| export default { | ||||
| 	name: 'App', | ||||
| 	components: { | ||||
| 		routerView, | ||||
| 		Sidebar, | ||||
| 		NavBar, | ||||
| 		FooterComponent | ||||
| 	}, | ||||
| 	data() { | ||||
| 		return { | ||||
| 			user: null, | ||||
| 			quote: null | ||||
| 		} | ||||
| 	}, | ||||
| 	mounted() { | ||||
| 		if (window.user) { | ||||
| 			this.user = window.user | ||||
| 			console.log(this.user) | ||||
| 		} | ||||
| 		if (window.quote) { | ||||
| 			this.quote = window.quote | ||||
| 		} | ||||
| 	}, | ||||
| 	methods: { | ||||
| 		onUserAuthenticated(userUri) { | ||||
| 			console.log('authenticated') | ||||
| 			axios | ||||
| 				.get(userUri) | ||||
| 				.then((response) => { | ||||
| 					this.user = response.data | ||||
| 				}) | ||||
| 			this.$router.push('/') | ||||
| 		}, | ||||
| 		onInvalidateUser() { | ||||
| 			console.log('invalidated') | ||||
| 			this.user = null | ||||
| 			window.user = null | ||||
| 		} | ||||
| 	} | ||||
| if (window.user) { | ||||
| 	const { user } = window | ||||
| } | ||||
| if (window.quote) { | ||||
| 	const { quote } = window | ||||
| } | ||||
|  | ||||
| const onUserAuthenticated = (userUri) => { | ||||
| 	//console.log('authenticated') | ||||
| 	axios | ||||
| 		.get(userUri) | ||||
| 		.then((response) => { | ||||
| 			this.user = response.data | ||||
| 		}) | ||||
| 	this.$router.push('/') | ||||
| } | ||||
|  | ||||
| const onUserInvalidate = () => { | ||||
| 	//console.log('invalidated') | ||||
| 	this.user = null | ||||
| 	window.user = null | ||||
| } | ||||
|  | ||||
| </script> | ||||
|   | ||||
| @@ -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> | ||||
|   | ||||
							
								
								
									
										29
									
								
								assets/js/composables/useResource.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								assets/js/composables/useResource.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,29 @@ | ||||
| import { ref } from 'vue' | ||||
| import axios from 'axios' | ||||
|  | ||||
| export default function useResource(resource) { | ||||
| 	console.log('useBlog') | ||||
| 	const items = ref([]) | ||||
| 	const item = ref(null) | ||||
| 	const fetchAll = async () => { | ||||
| 		await axios | ||||
| 			.get(resource) | ||||
| 			.then((response) => { | ||||
| 				items.value = response.data | ||||
| 			}) | ||||
| 	} | ||||
| 	const fetchOne = async (id) => { | ||||
| 		console.log('fetchOne', id) | ||||
| 		await axios | ||||
| 			.get(`${resource}/${id}`) | ||||
| 			.then((response) => { | ||||
| 				item.value = response.data | ||||
| 			}) | ||||
| 	} | ||||
| 	return { | ||||
| 		items, | ||||
| 		fetchAll, | ||||
| 		item, | ||||
| 		fetchOne | ||||
| 	} | ||||
| } | ||||
| @@ -11,10 +11,11 @@ import 'bootstrap/dist/js/bootstrap' | ||||
|  | ||||
| import { createPinia } from 'pinia' | ||||
|  | ||||
| import VueHighLightJS from 'vue3-highlightjs' | ||||
| import 'vue3-highlightjs/styles/dracula.css' | ||||
| //import { QuillEditor } from '@vueup/vue-quill' | ||||
| //import '@vueup/vue-quill/dist/vue-quill.snow.css' | ||||
| //import '@vueup/vue-quill/dist/vue-quill.bubble.css' | ||||
|  | ||||
| import Router from '@/router' | ||||
| import router from '@/router' | ||||
| import AppLink from '@/components/AppLink' | ||||
| import App from '@/App' | ||||
|  | ||||
| @@ -48,7 +49,7 @@ _paq.push(['enableLinkTracking']); | ||||
|  | ||||
| createApp(App) | ||||
| 	.component('AppLink', AppLink) | ||||
| //	.component('QuillEditor', QuillEditor) | ||||
| 	.use(createPinia()) | ||||
| 	.use(VueHighLightJS) | ||||
| 	.use(Router) | ||||
| 	.use(router) | ||||
| 	.mount('#app') | ||||
|   | ||||
| @@ -1,11 +1,14 @@ | ||||
| import { createRouter, createWebHistory } from 'vue-router' | ||||
| import LoginForm from '@/components/LoginForm' | ||||
| import Quotes from '@/components/quotes' | ||||
| import Pages from '@/components/pages' | ||||
| import Pages from '@/components/pages/PagesIndex' | ||||
| import PagesEdit from '@/components/pages/edit' | ||||
| import ProjectsList from '@/components/projects' | ||||
| import ProjectsDetails from '@/components/projects/ProjectDetails' | ||||
| import BlogIndex from '@/components/blog/BlogIndex' | ||||
| import BlogPost from '@/components/blog/BlogPost' | ||||
| import ProfileView from '@/components/users/ProfileView' | ||||
| import TheAbout from '@/components/TheAbout' | ||||
| import NotFound from '@/components/NotFound' | ||||
|  | ||||
| const routes = [ | ||||
| @@ -14,6 +17,11 @@ const routes = [ | ||||
| 		name: 'Home', | ||||
| 		component: Quotes | ||||
| 	}, | ||||
| 	{ | ||||
| 		path: '/about', | ||||
| 		name: 'About', | ||||
| 		component: TheAbout | ||||
| 	}, | ||||
| 	{ | ||||
| 		path: '/form_login', | ||||
| 		name: 'LoginForm', | ||||
| @@ -29,6 +37,16 @@ const routes = [ | ||||
| 		name: 'ProjectDetails', | ||||
| 		component: ProjectsDetails | ||||
| 	}, | ||||
| 	{ | ||||
| 		path: '/blog', | ||||
| 		name: 'Blog', | ||||
| 		component: BlogIndex | ||||
| 	}, | ||||
| 	{ | ||||
| 		path: '/blog/post/:id', | ||||
| 		name: 'BlogPost', | ||||
| 		component: BlogPost | ||||
| 	}, | ||||
| 	{ | ||||
| 		path: '/pages/:slug', | ||||
| 		name: 'Pages', | ||||
| @@ -57,10 +75,12 @@ const router = createRouter({ | ||||
| 	routes | ||||
| }) | ||||
|  | ||||
| /* eslint-disable */ | ||||
| /* | ||||
| router.beforeEach((to) => { | ||||
| 	if (to.meta.requiredAuth && !window.user) { | ||||
| 		return { name: 'LoginForm' } | ||||
| 	} | ||||
| }) | ||||
|  | ||||
|  */ | ||||
| export default router | ||||
|   | ||||
| @@ -68,6 +68,11 @@ $mango: #FF8040; | ||||
|   height: 650px !important; | ||||
| } | ||||
|  | ||||
| .no-overflow { | ||||
|   overflow-y: visible !important; | ||||
|   height: auto !important; | ||||
| } | ||||
|  | ||||
| .ql-syntax { | ||||
|   background-color: #222222; | ||||
|   border: 1px solid #888; | ||||
| @@ -85,11 +90,37 @@ $mango: #FF8040; | ||||
|   word-wrap: break-word; | ||||
| } | ||||
|  | ||||
|  | ||||
| .ql-editor { | ||||
|   counter-reset: line; | ||||
|   padding-left: 0; | ||||
| } | ||||
|  | ||||
| .ql-editor div:before { | ||||
|   counter-increment: line; | ||||
|   content: counter(line); | ||||
|   display: inline-block; | ||||
|   border-right: 1px solid red; | ||||
|   padding: 0 .5em; | ||||
|   margin-right: .5em; | ||||
|   color: #888 | ||||
| } | ||||
|  | ||||
| .ql-syntax pre:before { | ||||
|   counter-increment: line; | ||||
|   content: counter(line); | ||||
|   display: inline-block; | ||||
|   border-right: 1px solid #ddd; | ||||
|   padding: 2px .5em; | ||||
|   margin-right: .5em; | ||||
|   color: #888 | ||||
| } | ||||
| /* | ||||
| pre span.hljs-tag::before, span.hljs-attr::before { | ||||
|   content: "xxx"; | ||||
|   display: inline-block; | ||||
| } | ||||
|  | ||||
| */ | ||||
|  | ||||
| .wrapper { | ||||
|   clear: both; | ||||
|   | ||||
							
								
								
									
										14
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								package.json
									
									
									
									
									
								
							| @@ -26,28 +26,26 @@ | ||||
|     }, | ||||
|     "dependencies": { | ||||
|         "@fortawesome/fontawesome-free": "^6.1.1", | ||||
|         "@vueup/vue-quill": "^1.0.0-beta.8", | ||||
|         "axios": "^0.27.1", | ||||
|         "bootsdark": "^1.0.16", | ||||
|         "bootstrap": "^5.1.3", | ||||
|         "ckeditor": "^4.12.1", | ||||
|         "ckeditor4": "^4.18.0", | ||||
|         "eslint": "^8.14.0", | ||||
|         "eslint": "^8.15.0", | ||||
|         "eslint-config-airbnb-base": "^15.0.0", | ||||
|         "eslint-plugin-import": "^2.26.0", | ||||
|         "eslint-plugin-vue": "^8.7.1", | ||||
|         "fork-awesome": "^1.2.0", | ||||
|         "highlightjs": "^9.16.2", | ||||
|         "highlightjs-line-numbers.js": "^2.8.0", | ||||
|         "highlight.js": "^11.5.1", | ||||
|         "husky": "^7.0.4", | ||||
|         "less": "^4.1.2", | ||||
|         "marked": "^4.0.15", | ||||
|         "pinia": "^2.0.14", | ||||
|         "popper.js": "^1.16.1", | ||||
|         "primevue": "^3.12.6", | ||||
|         "quill": "^1.3.7", | ||||
|         "quill-html-edit-button": "^2.2.12", | ||||
|         "vue": "3", | ||||
|         "vue-navigation-bar": "^5.0.0", | ||||
|         "vue-router": "4", | ||||
|         "vue3-editor": "^0.1.1", | ||||
|         "vue3-highlightjs": "^1.0.5" | ||||
|         "vue-router": "4" | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -35,7 +35,7 @@ Encore | ||||
| 		styles: path.resolve(__dirname, 'assets', 'styles') | ||||
| 	}) | ||||
| 	.enableSassLoader() | ||||
| 	.enableVueLoader() | ||||
| 	.enableVueLoader(() => {}, { runtimeCompilerBuild: false }) | ||||
| 	.enableTypeScriptLoader() | ||||
| 	.autoProvidejQuery() | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user