mirror of
https://github.com/Floriansylvain/RenewCMS.git
synced 2026-08-19 11:43:22 +02:00
Article creation overhaul
This commit is contained in:
@@ -65,7 +65,8 @@ a,
|
|||||||
input,
|
input,
|
||||||
label,
|
label,
|
||||||
button,
|
button,
|
||||||
span {
|
span,
|
||||||
|
li {
|
||||||
font-family: 'Hind', sans-serif;
|
font-family: 'Hind', sans-serif;
|
||||||
color: var(--neutral-dark);
|
color: var(--neutral-dark);
|
||||||
}
|
}
|
||||||
@@ -99,6 +100,26 @@ span {
|
|||||||
border-radius: var(--radius);
|
border-radius: var(--radius);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.label-input .tags {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
margin: 0;
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label-input .tags li {
|
||||||
|
background-color: var(--neutral-verylight);
|
||||||
|
border-radius: var(--radius);
|
||||||
|
|
||||||
|
padding: 4px 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label-input .tags li::marker {
|
||||||
|
content: '';
|
||||||
|
}
|
||||||
|
|
||||||
.label-input-error input {
|
.label-input-error input {
|
||||||
border: solid 1.75px var(--error);
|
border: solid 1.75px var(--error);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
import { useAuthStore } from "@/stores/AuthStore"
|
import { useAuthStore } from "@/stores/AuthStore"
|
||||||
|
|
||||||
export interface Article {
|
export interface Article {
|
||||||
idName: string,
|
titleID: string,
|
||||||
|
title: string,
|
||||||
date: number,
|
date: number,
|
||||||
content: {
|
content: {
|
||||||
html: string
|
html: string
|
||||||
},
|
},
|
||||||
pageId: string,
|
tags: Array<string>,
|
||||||
online: boolean
|
online: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -24,7 +25,7 @@ export async function getArticle(id: string) : Promise<Article> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function postArticle(article: Article) : Promise<object> {
|
export async function postArticle(article: Article) : Promise<object> {
|
||||||
return await fetch(`${baseURL}/articles/${article.idName}`, {
|
return await fetch(`${baseURL}/articles/${article.titleID}`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { "Authorization": `Bearer ${useAuthStore().token}` },
|
headers: { "Authorization": `Bearer ${useAuthStore().token}` },
|
||||||
body: JSON.stringify(article)
|
body: JSON.stringify(article)
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import Editor from '@tinymce/tinymce-vue';
|
|||||||
import { onMounted, ref, type Ref } from 'vue';
|
import { onMounted, ref, type Ref } from 'vue';
|
||||||
import { useRoute } from 'vue-router';
|
import { useRoute } from 'vue-router';
|
||||||
|
|
||||||
let article: Ref<Article | void> = ref()
|
const article: Ref<Article | void> = ref()
|
||||||
const editorData: Ref<string> = ref('')
|
const editorData: Ref<string> = ref('')
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
|
|||||||
@@ -7,33 +7,42 @@ const router = useRouter()
|
|||||||
|
|
||||||
const defaultData = `<h1>Bienvenue</h1><p>Vous êtes en mode <em>édition</em> d'article.</p><p>Celui-ci semble encore neuf ! Supprimez ces lignes et laissez libre cours à votre imagination :)</p><p>Pour plus d'infos, rendez-vous sur la <a title="Attention, rickroll incoming" href="https:/www.youtube.com/watch?v=dQw4w9WgXcQ" target="_blank" rel="noopener">page d'aide</a>.</p>`
|
const defaultData = `<h1>Bienvenue</h1><p>Vous êtes en mode <em>édition</em> d'article.</p><p>Celui-ci semble encore neuf ! Supprimez ces lignes et laissez libre cours à votre imagination :)</p><p>Pour plus d'infos, rendez-vous sur la <a title="Attention, rickroll incoming" href="https:/www.youtube.com/watch?v=dQw4w9WgXcQ" target="_blank" rel="noopener">page d'aide</a>.</p>`
|
||||||
const title = ref('')
|
const title = ref('')
|
||||||
const page = ref('')
|
const rawTags = ref('')
|
||||||
|
const tags: Ref<string[]> = ref([])
|
||||||
|
|
||||||
function isIdValid(): boolean {
|
const regexAccents = /[\u0300-\u036f]/g
|
||||||
const regex = /^[a-zA-Z0-9]+$/
|
const regexSymbols = /([-!$%^&*()_+|~=`{}\[\]:";'<>?,.\/])+/g
|
||||||
return regex.test(title.value)
|
|
||||||
|
function updateTags(): void {
|
||||||
|
tags.value = rawTags.value.split(/[,\s]+/g)
|
||||||
|
tags.value = tags.value.filter(x => x !== '')
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateTitleID(title: string): string {
|
||||||
|
return title.toLowerCase().normalize('NFD')
|
||||||
|
.replace(regexAccents, '')
|
||||||
|
.replace(regexSymbols, '')
|
||||||
|
.replace(/\s/g, '-')
|
||||||
|
.replace(/-$/g, '')
|
||||||
}
|
}
|
||||||
|
|
||||||
function isFormEmpty(): boolean {
|
function isFormEmpty(): boolean {
|
||||||
return title.value === '' || page.value === ''
|
return title.value === '' || rawTags.value === ''
|
||||||
}
|
|
||||||
|
|
||||||
function isFormValid(): boolean {
|
|
||||||
return isIdValid() && !isFormEmpty()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function createArticle() {
|
function createArticle() {
|
||||||
const article: Article = {
|
const article: Article = {
|
||||||
idName: title.value,
|
titleID: generateTitleID(title.value),
|
||||||
|
title: title.value,
|
||||||
date: new Date().getTime(),
|
date: new Date().getTime(),
|
||||||
content: {
|
content: {
|
||||||
html: defaultData
|
html: defaultData
|
||||||
},
|
},
|
||||||
online: false,
|
online: false,
|
||||||
pageId: page.value
|
tags: tags.value
|
||||||
}
|
}
|
||||||
postArticle(article)
|
postArticle(article)
|
||||||
router.push(`/articles/edit/${article.idName}`)
|
router.push(`/articles/edit/${article.titleID}`)
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -42,21 +51,23 @@ function createArticle() {
|
|||||||
<h2>Créer un nouvel article</h2>
|
<h2>Créer un nouvel article</h2>
|
||||||
<form @submit.prevent="createArticle()">
|
<form @submit.prevent="createArticle()">
|
||||||
<div class="inputs-group">
|
<div class="inputs-group">
|
||||||
<div :class="`label-input${isIdValid() || title === '' ? '' : '-error'}`">
|
<div class="label-input">
|
||||||
<label for="title">Identifiant de l'article</label>
|
<label for="title">Titre de l'article</label>
|
||||||
<input id="title" name="title" placeholder="Identifiant de l'article" type="text" v-model="title">
|
<input id="title" name="title" placeholder="Titre de l'article" type="text" v-model="title">
|
||||||
<p>⚠️ Doit être unique et composé de caractères alphanumériques, sans espaces !
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="label-input">
|
<div class="label-input">
|
||||||
<label for="page">Page</label>
|
<label for="tags">Tags</label>
|
||||||
<input id="page" name="page" placeholder="Page" type="text" v-model="page">
|
<input id="tags" name="tags" placeholder="Tags" type="text" v-model="rawTags" @input="updateTags">
|
||||||
|
<p>Spéparez les mots-clés par des virgules ou espaces.</p>
|
||||||
|
<ul class="tags" v-if="tags.length !== 0">
|
||||||
|
<li v-for="tag in tags">{{ tag }}</li>
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="buttons-group">
|
<div class="buttons-group">
|
||||||
<RouterLink class="button-secondary" type="submit" to="/articles">Annuler</RouterLink>
|
<RouterLink class="button-secondary" type="submit" to="/articles">Annuler</RouterLink>
|
||||||
<button :class="`button-${!isFormValid() ? 'disabled' : 'primary'}`" type="submit"
|
<button :class="`button-${isFormEmpty() ? 'disabled' : 'primary'}`" type="submit"
|
||||||
:disabled="!isFormValid()">Créer</button>
|
:disabled="isFormEmpty()">Créer</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</main>
|
</main>
|
||||||
@@ -73,7 +84,7 @@ main {
|
|||||||
display: flex;
|
display: flex;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin: 32px 0;
|
margin: 16px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.buttons-group a,
|
.buttons-group a,
|
||||||
|
|||||||
Reference in New Issue
Block a user