#236 [pl][terms] add a key

This commit is contained in:
Avris 2021-07-30 12:51:12 +02:00
parent 1e25f58b19
commit ff698a26a4
6 changed files with 130 additions and 65 deletions

View File

@ -56,9 +56,9 @@
<ul class="list-inline">
<li v-for="category in s.el.categories" class="list-inline-item">
<button class="badge bg-primary text-white" @click="filter = ':' + category">
<a :href="`#:${category}`" class="badge bg-primary text-white" @click.prevent="filter = ':' + category">
{{category}}
</button>
</a>
</li>
</ul>

61
components/Term.vue Normal file
View File

@ -0,0 +1,61 @@
<template>
<div>
<p>
<strong><LinkedTextMultiple :texts="term.term" noicons/></strong>
<span v-if="term.original.length">(<LinkedTextMultiple :texts="term.original" glue="; " noicons/>)</span>
<LinkedText :text="term.definition" noicons/>
</p>
<ul class="list-inline">
<li v-for="category in term.categories" class="list-inline-item">
<a v-if="categoryLink" :href="`#:${category}`" class="badge bg-primary text-white" @click.prevent="filter = ':' + category">
{{category}}
</a>
<span v-else="" class="badge bg-primary text-white">
{{category}}
</span>
</li>
</ul>
<p v-if="flags && (term.flags.length || term.images.length)" class="text-center">
<img v-for="flag in term.flags" :src="`/flags/${flag}.png`" class="flag m-1"/>
<img v-for="image in term.images" :src="buildImageUrl(image, 'big')" class="flag m-1"/>
</p>
<div v-if="versions && term.versions.length" class="my-3 mx-2">
<p>
<button :class="['btn', versionsShown ? 'btn-primary' : 'btn-outline-primary', 'btn-sm']" @click="versionsShown = !versionsShown">
<Icon v="language"/>
<T>sources.otherVersions</T>
<Icon :v="versionsShown ? 'caret-up' : 'caret-down'"/>
</button>
</p>
<ul v-if="versionsShown">
<li v-for="version in term.versions" v-if="locales[version.locale] !== undefined">
<h4 class="h6 mb-2">
<strong>
<a :href="`${locales[version.locale].url}`" target="_blank" rel="noopener">{{locales[version.locale].name}}</a>:
</strong>
</h4>
<Term :term="version"/>
</li>
</ul>
</div>
</div>
</template>
<script>
export default {
props: {
term: { required: true },
categoryLink: { type: Boolean },
flags: { type: Boolean },
versions: { type: Boolean },
},
data() {
return {
versionsShown: false,
}
}
}
</script>

View File

@ -39,53 +39,12 @@
<template v-slot:row="s"><template v-if="s">
<td class="cell-wide">
<p>
<strong><LinkedTextMultiple :texts="s.el.term" noicons/></strong>
<span v-if="s.el.original.length">(<LinkedTextMultiple :texts="s.el.original" glue="; " noicons/>)</span>
<LinkedText :text="s.el.definition" noicons/>
<template v-if="s.el.category">
<br/>
<span class="badge bg-primary text-white">
{{s.el.category}}
</span>
</template>
</p>
<ul class="list-inline">
<li v-for="category in s.el.categories" class="list-inline-item">
<button class="badge bg-primary text-white" @click="filter = ':' + category">
{{category}}
</button>
</li>
</ul>
<p v-if="s.el.flags.length || s.el.images.length" class="text-center">
<img v-for="flag in s.el.flags" :src="`/flags/${flag}.png`" class="flag m-1"/>
<img v-for="image in s.el.images" :src="buildImageUrl(image, 'big')" class="flag m-1"/>
</p>
<Term :term="s.el" categoryLink flags versions/>
<div class="small" v-if="s.el.base && entries[s.el.base]">
<p><strong><T>nouns.edited</T>:</strong></p>
<p>
<strong><LinkedTextMultiple :texts="entries[s.el.base].term" noicons/></strong>
<span v-if="entries[s.el.base].original.length">(<LinkedTextMultiple :texts="entries[s.el.base].original" glue="; " noicons/>)</span>
<LinkedText :text="entries[s.el.base].definition" noicons/>
<template v-if="entries[s.el.base].category">
<br/>
<span class="badge bg-primary text-white">
{{entries[s.el.base].category}}
</span>
</template>
</p>
<ul class="list-inline">
<li v-for="category in entries[s.el.base].categories" class="list-inline-item">
<span class="badge bg-primary text-white">
{{category}}
</span>
</li>
</ul>
<Term :term="entries[s.el.base]" flags/>
</div>
</td>
<td>

View File

@ -49,7 +49,12 @@
</div>
<div class="row" v-if="$isGranted('terms')">
<div class="col-12 col-lg-6">
<div class="col-12 col-lg-4">
<label for="key"><strong><T>sources.submit.key</T></strong></label>
<input type="text" id="key" class="form-control" v-model="form.key" maxlength="255"/>
<p class="small text-muted"><T>sources.submit.keyInfo</T></p>
</div>
<div class="col-12 col-lg-4">
<div class="form-group">
<label class="text-nowrap"><strong>
<T>profile.flags</T>
@ -57,7 +62,7 @@
<ListInput v-model="form.flags" v-slot="s"/>
</div>
</div>
<div class="col-12 col-lg-6">
<div class="col-12 col-lg-4">
<div class="form-group">
<label class="text-nowrap"><strong>
<T>nouns.terms.images</T>
@ -101,6 +106,7 @@
form: {
term: [''],
original: [],
key: '',
definition: '',
categories: [],
flags: [],
@ -121,6 +127,7 @@
this.form = {
term: [''],
original: [],
key: '',
definition: '',
categories: [],
flags: [],
@ -135,6 +142,7 @@
this.form = {
term: word.term,
original: word.original,
key: word.key,
definition: word.definition,
categories: word.categories,
flags: word.flags,

View File

@ -21,30 +21,64 @@ const approve = async (db, id) => {
await caches.terms.invalidate();
}
const linkOtherVersions = async (req, terms) => {
const keys = new Set(terms.filter(s => !!s && s.key).map(s => `'` + s.key + `'`));
const otherVersions = await req.db.all(SQL`
SELECT t.*, u.username AS author FROM terms t
LEFT JOIN users u ON t.author_id = u.id
WHERE t.locale != ${global.config.locale}
AND t.deleted = 0
AND t.approved >= ${req.isGranted('terms') ? 0 : 1}
AND t.key IN (`.append([...keys].join(',')).append(SQL`)
`));
const otherVersionsMap = {};
otherVersions.forEach(version => {
if (otherVersionsMap[version.key] === undefined) {
otherVersionsMap[version.key] = [];
}
otherVersionsMap[version.key].push(version);
});
return terms.map(t => {
t.versions = t.key ? otherVersionsMap[t.key] || [] : [];
return t;
});
};
const router = Router();
router.get('/terms', handleErrorAsync(async (req, res) => {
return res.json(await caches.terms.fetch(async () => {
return sortClearedLinkedText(await req.db.all(SQL`
SELECT i.*, u.username AS author FROM terms i
LEFT JOIN users u ON i.author_id = u.id
WHERE i.locale = ${global.config.locale}
AND i.approved >= ${req.isGranted('terms') ? 0 : 1}
AND i.deleted = 0
`), 'term');
return await linkOtherVersions(
req,
sortClearedLinkedText(await req.db.all(SQL`
SELECT i.*, u.username AS author FROM terms i
LEFT JOIN users u ON i.author_id = u.id
WHERE i.locale = ${global.config.locale}
AND i.approved >= ${req.isGranted('terms') ? 0 : 1}
AND i.deleted = 0
`), 'term'),
);
}, !req.isGranted('terms')));
}));
router.get('/terms/search/:term', handleErrorAsync(async (req, res) => {
const term = '%' + req.params.term + '%';
return res.json(sortClearedLinkedText(await req.db.all(SQL`
SELECT i.*, u.username AS author FROM terms i
LEFT JOIN users u ON i.author_id = u.id
WHERE i.locale = ${global.config.locale}
AND i.approved >= ${req.isGranted('terms') ? 0 : 1}
AND i.deleted = 0
AND (i.term like ${term} OR i.original like ${term})
`)), 'term');
return res.json(
await linkOtherVersions(
req,
sortClearedLinkedText(await req.db.all(SQL`
SELECT i.*, u.username AS author FROM terms i
LEFT JOIN users u ON i.author_id = u.id
WHERE i.locale = ${global.config.locale}
AND i.approved >= ${req.isGranted('terms') ? 0 : 1}
AND i.deleted = 0
AND (i.term like ${term} OR i.original like ${term})
`), 'term'),
)
);
}));
router.post('/terms/submit', handleErrorAsync(async (req, res) => {
@ -58,10 +92,10 @@ router.post('/terms/submit', handleErrorAsync(async (req, res) => {
const id = ulid();
await req.db.get(SQL`
INSERT INTO terms (id, term, original, definition, approved, base_id, locale, author_id, category, flags, images)
INSERT INTO terms (id, term, original, key, definition, approved, base_id, locale, author_id, category, flags, images)
VALUES (
${id},
${req.body.term.join('|')}, ${req.body.original.join('|')}, ${req.body.definition},
${req.body.term.join('|')}, ${req.body.original.join('|')}, ${req.body.key || null}, ${req.body.definition},
0, ${req.body.base}, ${global.config.locale}, ${req.user ? req.user.id : null},
${req.body.categories.join(',')}, ${JSON.stringify(req.body.flags)}, ${req.body.images}
)

View File

@ -719,10 +719,11 @@ export class InclusiveEntry {
}
export class TermsEntry {
constructor({id, term, original, definition, author, category = null, flags = '[]', images = '', approved = true, base_id = null}) {
constructor({id, term, original, key = null, definition, author, category = null, flags = '[]', images = '', approved = true, base_id = null, locale, versions = []}) {
this.id = id;
this.term = term.split('|');
this.original = original ? original.split('|') : [];
this.key = key || null;
this.definition = definition;
this.author = author;
this.categories = category ? category.split(',') : [];
@ -730,6 +731,8 @@ export class TermsEntry {
this.images = images ? images.split(',') : [];
this.approved = !!approved;
this.base = base_id;
this.locale = locale;
this.versions = versions.map(v => new TermsEntry(v));
}
matches(filter) {