Merge branch 'key-autocomplete' into 'main'

(terms)(sources) add autocomplete & convention explanation for the key field

See merge request PronounsPage/PronounsPage!500
This commit is contained in:
Andrea Vos 2024-08-11 12:09:35 +00:00
commit 0d0fda899e
26 changed files with 93 additions and 38 deletions

View File

@ -5,7 +5,7 @@
v-model="filter" v-model="filter"
type="text" type="text"
class="form-control" class="form-control"
:placeholder="$t('profile.timezone.placeholder')" v-bind="$attrs"
@focus="show" @focus="show"
@blur="hide" @blur="hide"
@keydown="filterKeydown" @keydown="filterKeydown"
@ -26,9 +26,11 @@
<script> <script>
export default { export default {
inheritAttrs: false,
props: { props: {
value: {}, value: {},
options: { required: true }, options: { required: true },
freeform: { type: Boolean },
}, },
data() { data() {
return { return {
@ -47,6 +49,11 @@ export default {
this.filter = this.options[this.value] || this.value; this.filter = this.options[this.value] || this.value;
this.highlighted = -1; this.highlighted = -1;
}, },
filter() {
if (this.freeform) {
this.$emit('input', this.filter);
}
},
}, },
methods: { methods: {
select(option) { select(option) {

View File

@ -140,15 +140,16 @@
</div> </div>
<div v-if="$isGranted('sources')" class="form-group"> <div v-if="$isGranted('sources')" class="form-group">
<label for="key"><T>sources.submit.key</T></label> <label for="key"><T>sources.submit.key</T></label>
<input <AutocompleteSelect
id="key" id="key"
v-model="form.key" v-model="form.key"
type="text"
class="form-control"
maxlength="255" maxlength="255"
> :options="keys"
freeform
/>
<p class="small text-muted"> <p class="small text-muted">
<T>sources.submit.keyInfo</T> <T>sources.submit.keyInfo</T>
<T>sources.submit.keyInfoConvention</T>
</p> </p>
</div> </div>
<div v-if="form.base" class="alert alert-info"> <div v-if="form.base" class="alert alert-info">
@ -199,6 +200,7 @@ interface Data {
submitting: boolean; submitting: boolean;
afterSubmit: boolean; afterSubmit: boolean;
pronounLibrary: PronounLibrary; pronounLibrary: PronounLibrary;
keys: Record<string, string>;
} }
export default Vue.extend({ export default Vue.extend({
@ -226,8 +228,13 @@ export default Vue.extend({
afterSubmit: false, afterSubmit: false,
pronounLibrary, pronounLibrary,
keys: {},
}; };
}, },
async mounted() {
this.keys = await this.$axios.$get('/sources/keys');
},
methods: { methods: {
async submit(): Promise<void> { async submit(): Promise<void> {
this.submitting = true; this.submitting = true;

View File

@ -53,9 +53,10 @@
<div class="row"> <div class="row">
<div class="col-12 col-lg-4"> <div class="col-12 col-lg-4">
<label for="key"><strong><T>sources.submit.key</T></strong></label> <label for="key"><strong><T>sources.submit.key</T></strong></label>
<input id="key" v-model="form.key" type="text" class="form-control" maxlength="255"> <AutocompleteSelect v-model="form.key" :options="keys" maxlength="255" freeform />
<p class="small text-muted"> <p class="small text-muted">
<T>sources.submit.keyInfo</T> <T>sources.submit.keyInfo</T>
<T>sources.submit.keyInfoConvention</T>
</p> </p>
</div> </div>
<div class="col-12 col-lg-4"> <div class="col-12 col-lg-4">
@ -122,6 +123,7 @@ interface Data {
}; };
submitting: boolean; submitting: boolean;
afterSubmit: boolean; afterSubmit: boolean;
keys: Record<string, string>,
} }
export default Vue.extend({ export default Vue.extend({
@ -139,8 +141,12 @@ export default Vue.extend({
}, },
submitting: false, submitting: false,
afterSubmit: false, afterSubmit: false,
keys: {},
}; };
}, },
async mounted() {
this.keys = await this.$axios.$get('/terms/keys');
},
methods: { methods: {
async submit(): Promise<void> { async submit(): Promise<void> {
this.submitting = true; this.submitting = true;

View File

@ -5,7 +5,7 @@
<Icon v="location" /> <Icon v="location" />
<T>profile.timezone.detect</T> <T>profile.timezone.detect</T>
</button> </button>
<AutocompleteSelect v-model="timezone" :options="timezones" /> <AutocompleteSelect v-model="timezone" :options="timezones" :placeholder="$t('profile.timezone.placeholder')" />
<button v-if="timezone" class="btn btn-outline-danger" type="button" @click="timezone = null"> <button v-if="timezone" class="btn btn-outline-danger" type="button" @click="timezone = null">
<Icon v="times" /> <Icon v="times" />
</button> </button>

View File

@ -139,7 +139,8 @@ sources:
another: 'Submit another one' another: 'Submit another one'
moderation: 'Submissions will have to get approved before getting published.' moderation: 'Submissions will have to get approved before getting published.'
key: 'Key' key: 'Key'
keyInfo: 'Identifier for linking sources between language versions and linking with the dictionary' keyInfo: 'Identifier for linking sources between language versions and linking with the dictionary.'
keyInfoConvention: 'Ideally: the simplest form of the term, in English, in lower-case.'
images: 'Images' images: 'Images'
spoiler: 'Spoiler' spoiler: 'Spoiler'
otherVersions: 'In other languages' otherVersions: 'In other languages'

View File

@ -151,7 +151,7 @@ sources:
another: 'Ein weiteres Beispiel einreichen' another: 'Ein weiteres Beispiel einreichen'
moderation: 'Einreichungen müssen erst genehmigt werden, bevor sie veröffentlicht werden.' moderation: 'Einreichungen müssen erst genehmigt werden, bevor sie veröffentlicht werden.'
key: 'Schlüssel' key: 'Schlüssel'
keyInfo: 'Kennzeichen, um Quellen zwischen Sprachversionen und dem Wörterbuch zu verbinden' keyInfo: 'Kennzeichen, um Quellen zwischen Sprachversionen und dem Wörterbuch zu verbinden.'
images: 'Bilder' images: 'Bilder'
spoiler: 'Spoiler' spoiler: 'Spoiler'
otherVersions: 'In anderen Sprachen' otherVersions: 'In anderen Sprachen'

View File

@ -137,7 +137,8 @@ sources:
another: 'Submit another one' another: 'Submit another one'
moderation: 'Submissions will have to get approved before getting published.' moderation: 'Submissions will have to get approved before getting published.'
key: 'Key' key: 'Key'
keyInfo: 'Identifier for linking sources between language versions and linking with the dictionary' keyInfo: 'Identifier for linking sources between language versions and linking with the dictionary.'
keyInfoConvention: 'Ideally: the simplest form of the term, in English, in lower-case.'
images: 'Images' images: 'Images'
spoiler: 'Spoiler' spoiler: 'Spoiler'
otherVersions: 'In other languages' otherVersions: 'In other languages'

View File

@ -101,7 +101,7 @@ sources:
another: 'Alsendi pluan' another: 'Alsendi pluan'
moderation: 'Alsendaĵoj estas aprobendaj antaŭ ol publikigo.' moderation: 'Alsendaĵoj estas aprobendaj antaŭ ol publikigo.'
key: 'Ŝlosilo' key: 'Ŝlosilo'
keyInfo: 'Identigilo por ligi fontojn inter lingvoversiojn kaj ligado al la vortaro' keyInfo: 'Identigilo por ligi fontojn inter lingvoversiojn kaj ligado al la vortaro.'
images: 'Bildoj' images: 'Bildoj'
otherVersions: 'En aliaj lingvoj' otherVersions: 'En aliaj lingvoj'
referenced: 'Ekzemploj de uzo' referenced: 'Ekzemploj de uzo'

View File

@ -128,7 +128,7 @@ sources:
another: 'Enviar otro' another: 'Enviar otro'
moderation: 'Los envíos deben ser aprobados antes de ser publicados.' moderation: 'Los envíos deben ser aprobados antes de ser publicados.'
key: 'Clave' key: 'Clave'
keyInfo: 'Identificador para vincular fuentes entre versiones en varios idiomas y vincularlas con el diccionario' keyInfo: 'Identificador para vincular fuentes entre versiones en varios idiomas y vincularlas con el diccionario.'
images: 'Imágenes' images: 'Imágenes'
spoiler: 'Spoiler' spoiler: 'Spoiler'
fragmentsInfo: > fragmentsInfo: >

View File

@ -100,7 +100,7 @@ sources:
another: 'Esita veel üks' another: 'Esita veel üks'
moderation: 'Esitatud viited kinnitatakse enne avaldamist moderaatori poolt.' moderation: 'Esitatud viited kinnitatakse enne avaldamist moderaatori poolt.'
key: 'Võti' key: 'Võti'
keyInfo: 'Identifikaator allikate sidumiseks keeleversioonide vahel ning sõnastikuga' keyInfo: 'Identifikaator allikate sidumiseks keeleversioonide vahel ning sõnastikuga.'
images: 'Pildid' images: 'Pildid'
spoiler: 'Spoiler' spoiler: 'Spoiler'
otherVersions: 'Teistes keeltes' otherVersions: 'Teistes keeltes'

View File

@ -118,7 +118,7 @@ sources:
thanks: 'Merci de votre contribution !' thanks: 'Merci de votre contribution !'
another: 'Soumettre un autre exemple' another: 'Soumettre un autre exemple'
moderation: 'Les soumissions devront être approuvées avant dêtre publiées.' moderation: 'Les soumissions devront être approuvées avant dêtre publiées.'
keyInfo: 'Identifiant pour relier les sources entre les versions linguistiques et établir des liens avec le dictionnaire' keyInfo: 'Identifiant pour relier les sources entre les versions linguistiques et établir des liens avec le dictionnaire.'
images: 'Images' images: 'Images'
spoiler: 'Spoiler' spoiler: 'Spoiler'
otherVersions: 'Dans dautres langues' otherVersions: 'Dans dautres langues'

View File

@ -115,7 +115,7 @@ sources:
another: 'Enviar outro' another: 'Enviar outro'
moderation: 'Os envios devem ser aprovados antes de serem publicados.' moderation: 'Os envios devem ser aprovados antes de serem publicados.'
key: 'Chave' key: 'Chave'
keyInfo: 'Identificador para conectar fontes entre versões e conectar com dicionário' keyInfo: 'Identificador para conectar fontes entre versões e conectar com dicionário.'
images: 'Imagens' images: 'Imagens'
spoiler: 'Spoiler' # TODO spoiler: 'Spoiler' # TODO
otherVersions: 'Em outros idiomas' otherVersions: 'Em outros idiomas'

View File

@ -128,7 +128,7 @@ sources:
another: 'Inviane un altro' another: 'Inviane un altro'
moderation: 'Il materiale deve essere approvato prima della sua pubblicazione.' moderation: 'Il materiale deve essere approvato prima della sua pubblicazione.'
key: 'Chiave' key: 'Chiave'
keyInfo: 'Identificativo per collegare fonti tra lingue diverse e collegarle con il dizionario' keyInfo: 'Identificativo per collegare fonti tra lingue diverse e collegarle con il dizionario.'
images: 'Immagini' images: 'Immagini'
otherVersions: 'In altre lingue' otherVersions: 'In altre lingue'
referenced: 'Esempi di utilizzo' referenced: 'Esempi di utilizzo'

View File

@ -101,7 +101,7 @@ sources:
another: 'もう一つ送信' another: 'もう一つ送信'
moderation: '承認されるまで投稿は公開されません。' moderation: '承認されるまで投稿は公開されません。'
key: 'キー' key: 'キー'
keyInfo: '言語間、及び辞書とリンクするのための識別子' keyInfo: '言語間、及び辞書とリンクするのための識別子'
images: '画像' images: '画像'
spoiler: 'ネタバレ' spoiler: 'ネタバレ'
otherVersions: '他の言語で' otherVersions: '他の言語で'

View File

@ -118,7 +118,7 @@ sources:
another: '제출 더하기' another: '제출 더하기'
moderation: '제출물은 먼저 승인받아야합니다.' moderation: '제출물은 먼저 승인받아야합니다.'
key: '키' key: '키'
keyInfo: '언어 버전 간 소스 연결 및 사전 연결 식별자' keyInfo: '언어 버전 간 소스 연결 및 사전 연결 식별자.'
images: '이미지' images: '이미지'
spoiler: '스포주의' spoiler: '스포주의'
otherVersions: '다른 언어' otherVersions: '다른 언어'

View File

@ -117,7 +117,7 @@ sources:
another: 'Embiar otruno' another: 'Embiar otruno'
moderation: 'Los embios deven ser aseptados antes de ser publikados.' moderation: 'Los embios deven ser aseptados antes de ser publikados.'
key: 'Yave' key: 'Yave'
keyInfo: 'Identifiador para atar manaderos entre versiones en varias linguas i atarlas kon el diksionaryo' keyInfo: 'Identifiador para atar manaderos entre versiones en varias linguas i atarlas kon el diksionaryo.'
images: 'Imajes' images: 'Imajes'
spoiler: 'Spoiler' spoiler: 'Spoiler'
otherVersions: 'En otras linguas' otherVersions: 'En otras linguas'

View File

@ -116,7 +116,7 @@ sources:
another: 'Dien nog een voorbeeld in' another: 'Dien nog een voorbeeld in'
moderation: 'Inzendingen zullen moeten worden goedgekeurd alvorens te worden gepubliceerd.' moderation: 'Inzendingen zullen moeten worden goedgekeurd alvorens te worden gepubliceerd.'
key: 'Sleutel' key: 'Sleutel'
keyInfo: 'Identifier om bronnen te linken tussen verschillende vertalingen van de website en om met het woordenboek te linken' keyInfo: 'Identifier om bronnen te linken tussen verschillende vertalingen van de website en om met het woordenboek te linken.'
images: 'Afbeeldingen' images: 'Afbeeldingen'
spoiler: 'Spoiler' spoiler: 'Spoiler'
otherVersions: 'In andere talen' otherVersions: 'In andere talen'

View File

@ -110,7 +110,7 @@ sources:
another: 'Legg til en til' another: 'Legg til en til'
moderation: 'Bidrag må bli vurdert og godtatt før de blir lagt ut.' moderation: 'Bidrag må bli vurdert og godtatt før de blir lagt ut.'
key: 'Nøkkel' key: 'Nøkkel'
keyInfo: 'identifiserer for å linke kilder mellom språkversjoner og linker med ordboken' keyInfo: 'identifiserer for å linke kilder mellom språkversjoner og linker med ordboken.'
images: 'Bilder' images: 'Bilder'
spoiler: 'Sladd' spoiler: 'Sladd'
otherVersions: 'På andre språk' otherVersions: 'På andre språk'

View File

@ -141,7 +141,8 @@ sources:
another: 'Zgłoś kolejne źródło' another: 'Zgłoś kolejne źródło'
moderation: 'Propozycje będą musiały zostać zatwierdzone przed opublikowaniem.' moderation: 'Propozycje będą musiały zostać zatwierdzone przed opublikowaniem.'
key: 'Klucz' key: 'Klucz'
keyInfo: 'Identyfikator do łączenia tekstów między wersjami językowymi i łączenia ze słownikiem' keyInfo: 'Identyfikator do łączenia tekstów między wersjami językowymi i łączenia ze słownikiem.'
keyInfoConvention: 'Najlepiej: najprostsza forma wpisu, po angielsku, małymi literami.'
images: 'Obrazki' images: 'Obrazki'
spoiler: 'Spoiler' spoiler: 'Spoiler'
otherVersions: 'W innych językach' otherVersions: 'W innych językach'

View File

@ -126,7 +126,7 @@ sources:
another: 'Enviar outro' another: 'Enviar outro'
moderation: 'Os envios devem ser aprovados antes de serem publicados.' moderation: 'Os envios devem ser aprovados antes de serem publicados.'
key: 'Chave' key: 'Chave'
keyInfo: 'Identificador para conectar fontes entre versões e conectar com dicionário' keyInfo: 'Identificador para conectar fontes entre versões e conectar com dicionário.'
images: 'Imagens' images: 'Imagens'
spoiler: 'Spoiler' spoiler: 'Spoiler'
fragmentsInfo: > fragmentsInfo: >

View File

@ -122,7 +122,7 @@ sources:
another: 'Добавить ещё' another: 'Добавить ещё'
moderation: 'Предложения должны быть одобрены перед публикацией.' moderation: 'Предложения должны быть одобрены перед публикацией.'
key: 'Ключ' key: 'Ключ'
keyInfo: 'Идентификатор, используемый для связки источников из разных языковых версий и связки со словарём' keyInfo: 'Идентификатор, используемый для связки источников из разных языковых версий и связки со словарём.'
images: 'Изображения' images: 'Изображения'
spoiler: 'Спойлер' spoiler: 'Спойлер'
otherVersions: 'На других языках' otherVersions: 'На других языках'

View File

@ -122,7 +122,7 @@ sources:
another: 'Skicka in en till' another: 'Skicka in en till'
moderation: 'Bidragen måste godkännas innan de publiceras.' moderation: 'Bidragen måste godkännas innan de publiceras.'
key: 'Nyckel' key: 'Nyckel'
keyInfo: 'Identifierare för att länka källor mellan språkversioner och länka till ordbok' keyInfo: 'Identifierare för att länka källor mellan språkversioner och länka till ordbok.'
images: 'Bilder' images: 'Bilder'
spoiler: 'Spoiler' # Swedish just takes the word spoiler directly spoiler: 'Spoiler' # Swedish just takes the word spoiler directly
otherVersions: 'På andra språk' otherVersions: 'På andra språk'

View File

@ -128,7 +128,7 @@ sources:
another: 'Gửi thêm' another: 'Gửi thêm'
moderation: 'Các ví dụ sẽ phải được phê duyệt trước khi được thêm vào.' moderation: 'Các ví dụ sẽ phải được phê duyệt trước khi được thêm vào.'
key: 'Mã định danh' key: 'Mã định danh'
keyInfo: 'Mã định danh để liên kết các nguồn ở các phiên bản ngôn ngữ khác và liên kết với từ điển' keyInfo: 'Mã định danh để liên kết các nguồn ở các phiên bản ngôn ngữ khác và liên kết với từ điển.'
images: 'Ảnh' images: 'Ảnh'
spoiler: 'Spoiler' spoiler: 'Spoiler'
otherVersions: 'Trong các ngôn ngữ khác' otherVersions: 'Trong các ngôn ngữ khác'

View File

@ -119,7 +119,7 @@ sources:
another: '提交另一個' another: '提交另一個'
moderation: '提交的內容必須先獲得批准才能發布。' moderation: '提交的內容必須先獲得批准才能發布。'
key: '鍵' # TODO key: '鍵' # TODO
keyInfo: '用於在語言版本之間連接源並與字典連接的標誌符' keyInfo: '用於在語言版本之間連接源並與字典連接的標誌符'
images: '意象' images: '意象'
spoiler: '劇透內容' spoiler: '劇透內容'
otherVersions: '在其他語言' otherVersions: '在其他語言'

View File

@ -95,17 +95,6 @@ router.get('/sources', handleErrorAsync(async (req, res) => {
return res.json(await linkOtherVersions(req, await req.db.all(sql))); return res.json(await linkOtherVersions(req, await req.db.all(sql)));
})); }));
router.get('/sources/:id', handleErrorAsync(async (req, res) => {
return res.json(await linkOtherVersions(req, await req.db.all(SQL`
SELECT s.*, u.username AS submitter FROM sources s
LEFT JOIN users u ON s.submitter_id = u.id
WHERE s.locale = ${global.config.locale}
AND s.deleted = 0
AND s.approved >= ${req.isGranted('sources') ? 0 : 1}
AND s.id = ${req.params.id}
`)));
}));
router.post('/sources/submit', handleErrorAsync(async (req, res) => { router.post('/sources/submit', handleErrorAsync(async (req, res) => {
if (!req.user || !await req.isUserAllowedToPost()) { if (!req.user || !await req.isUserAllowedToPost()) {
return res.status(401).json({ error: 'Unauthorised' }); return res.status(401).json({ error: 'Unauthorised' });
@ -176,4 +165,31 @@ router.post('/sources/remove/:id', handleErrorAsync(async (req, res) => {
return res.json('ok'); return res.json('ok');
})); }));
router.get('/sources/keys', handleErrorAsync(async (req, res) => {
const keys = await req.db.all<{ key: string }>(SQL`
SELECT trim(key) AS key
FROM sources
WHERE key IS NOT NULL
AND deleted = 0
AND approved = 1
GROUP BY key
ORDER BY key
`);
return res.json(
Object.fromEntries(keys.map((k) => [k.key, k.key])),
);
}));
router.get('/sources/:id', handleErrorAsync(async (req, res) => {
return res.json(await linkOtherVersions(req, await req.db.all(SQL`
SELECT s.*, u.username AS submitter FROM sources s
LEFT JOIN users u ON s.submitter_id = u.id
WHERE s.locale = ${global.config.locale}
AND s.deleted = 0
AND s.approved >= ${req.isGranted('sources') ? 0 : 1}
AND s.id = ${req.params.id}
`)));
}));
export default router; export default router;

View File

@ -174,4 +174,20 @@ router.post('/terms/remove/:id', handleErrorAsync(async (req, res) => {
return res.json('ok'); return res.json('ok');
})); }));
router.get('/terms/keys', handleErrorAsync(async (req, res) => {
const keys = await req.db.all<{ key: string }>(SQL`
SELECT trim(key) AS key
FROM terms
WHERE key IS NOT NULL
AND deleted = 0
AND approved = 1
GROUP BY key
ORDER BY key
`);
return res.json(
Object.fromEntries(keys.map((k) => [k.key, k.key])),
);
}));
export default router; export default router;