diff --git a/backend/controllers/vocabController.js b/backend/controllers/vocabController.js
index 71f9abd..d4d4e44 100644
--- a/backend/controllers/vocabController.js
+++ b/backend/controllers/vocabController.js
@@ -16,6 +16,7 @@ class VocabController {
this.listChapters = this._wrapWithUser((userId, req) => this.service.listChapters(userId, req.params.languageId));
this.createChapter = this._wrapWithUser((userId, req) => this.service.createChapter(userId, req.params.languageId, req.body), { successStatus: 201 });
this.listLanguageVocabs = this._wrapWithUser((userId, req) => this.service.listLanguageVocabs(userId, req.params.languageId));
+ this.searchVocabs = this._wrapWithUser((userId, req) => this.service.searchVocabs(userId, req.params.languageId, req.query));
this.getChapter = this._wrapWithUser((userId, req) => this.service.getChapter(userId, req.params.chapterId));
this.listChapterVocabs = this._wrapWithUser((userId, req) => this.service.listChapterVocabs(userId, req.params.chapterId));
diff --git a/backend/routers/vocabRouter.js b/backend/routers/vocabRouter.js
index 78ce1c4..2c31f01 100644
--- a/backend/routers/vocabRouter.js
+++ b/backend/routers/vocabRouter.js
@@ -16,6 +16,7 @@ router.get('/languages/:languageId', vocabController.getLanguage);
router.get('/languages/:languageId/chapters', vocabController.listChapters);
router.post('/languages/:languageId/chapters', vocabController.createChapter);
router.get('/languages/:languageId/vocabs', vocabController.listLanguageVocabs);
+router.get('/languages/:languageId/search', vocabController.searchVocabs);
router.get('/chapters/:chapterId', vocabController.getChapter);
router.get('/chapters/:chapterId/vocabs', vocabController.listChapterVocabs);
diff --git a/backend/services/vocabService.js b/backend/services/vocabService.js
index 534f2b2..4a2fcb9 100644
--- a/backend/services/vocabService.js
+++ b/backend/services/vocabService.js
@@ -408,6 +408,52 @@ export default class VocabService {
return { languageId: access.id, isOwner: access.isOwner, vocabs: rows };
}
+ async searchVocabs(hashedUserId, languageId, { learning = '', motherTongue = '' } = {}) {
+ const user = await this._getUserByHashedId(hashedUserId);
+ const access = await this._getLanguageAccess(user.id, languageId);
+
+ const learningTerm = typeof learning === 'string' ? learning.trim() : '';
+ const motherTerm = typeof motherTongue === 'string' ? motherTongue.trim() : '';
+ if (!learningTerm && !motherTerm) {
+ const err = new Error('Missing search term');
+ err.status = 400;
+ throw err;
+ }
+
+ const learningLike = learningTerm ? `%${learningTerm}%` : null;
+ const motherLike = motherTerm ? `%${motherTerm}%` : null;
+
+ const rows = await sequelize.query(
+ `
+ SELECT
+ cl.id,
+ c.id AS "chapterId",
+ c.title AS "chapterTitle",
+ l1.text AS "learning",
+ l2.text AS "motherTongue"
+ FROM community.vocab_chapter_lexeme cl
+ JOIN community.vocab_chapter c ON c.id = cl.chapter_id
+ JOIN community.vocab_lexeme l1 ON l1.id = cl.learning_lexeme_id
+ JOIN community.vocab_lexeme l2 ON l2.id = cl.reference_lexeme_id
+ WHERE c.language_id = :languageId
+ AND (:learningLike IS NULL OR l1.text ILIKE :learningLike)
+ AND (:motherLike IS NULL OR l2.text ILIKE :motherLike)
+ ORDER BY l2.text ASC, l1.text ASC, c.title ASC
+ LIMIT 200
+ `,
+ {
+ replacements: {
+ languageId: access.id,
+ learningLike,
+ motherLike,
+ },
+ type: sequelize.QueryTypes.SELECT,
+ }
+ );
+
+ return { languageId: access.id, results: rows };
+ }
+
async addVocabToChapter(hashedUserId, chapterId, { learning, reference }) {
const user = await this._getUserByHashedId(hashedUserId);
const ch = await this._getChapterAccess(user.id, chapterId);
diff --git a/frontend/src/dialogues/socialnetwork/VocabSearchDialog.vue b/frontend/src/dialogues/socialnetwork/VocabSearchDialog.vue
new file mode 100644
index 0000000..6383b83
--- /dev/null
+++ b/frontend/src/dialogues/socialnetwork/VocabSearchDialog.vue
@@ -0,0 +1,170 @@
+
+
+
+
+
+
+
+
+ {{ $t('socialnetwork.vocab.search.motherTongue') }}
+ {{ learningLabel }}
+ {{ $t('socialnetwork.vocab.search.lesson') }}
+
+
+
+ {{ r.motherTongue }}
+ {{ r.learning }}
+ {{ r.chapterTitle }}
+