diff --git a/addons/t9n/models/message.py b/addons/t9n/models/message.py index b08f60bfede03..2edd5f82f065d 100644 --- a/addons/t9n/models/message.py +++ b/addons/t9n/models/message.py @@ -1,4 +1,4 @@ -from odoo import api, fields, models +from odoo import fields, models class Message(models.Model): diff --git a/addons/t9n/models/project.py b/addons/t9n/models/project.py index 30a385c792b4c..da6362199d23e 100644 --- a/addons/t9n/models/project.py +++ b/addons/t9n/models/project.py @@ -1,4 +1,4 @@ -from odoo import fields, models, api, _ +from odoo import _, api, fields, models from odoo.exceptions import ValidationError @@ -32,3 +32,23 @@ def _check_source_and_target_languages(self): for record in self: if record.src_lang_id in record.target_lang_ids: raise ValidationError(_("A project's target languages must be different from its source language.")) + + @api.model + def get_projects(self): + projects_records = self.search([]) + return [{ + "id": record.id, + "name": record.name, + "src_lang": { + "id": record.src_lang_id.id, + "name": record.src_lang_id.name if record.src_lang_id.name else "", + }, + "resources": [{ + "id": resource.id, + "file_name": resource.file_name, + } for resource in record.resource_ids], + "target_langs": [{ + "id": lang.id, + "name": lang.name, + } for lang in record.target_lang_ids], + } for record in projects_records] diff --git a/addons/t9n/static/src/core/app.js b/addons/t9n/static/src/core/app.js index 79ff2d525fff2..f58178faf5a15 100644 --- a/addons/t9n/static/src/core/app.js +++ b/addons/t9n/static/src/core/app.js @@ -1,10 +1,11 @@ import { Component } from "@odoo/owl"; +import { ProjectList } from "@t9n/core/project_list"; /** * The "root", the "homepage" of the translation application. */ export class App extends Component { - static components = {}; + static components = { ProjectList }; static props = {}; static template = "t9n.App"; } diff --git a/addons/t9n/static/src/core/app.xml b/addons/t9n/static/src/core/app.xml index 737753809f19d..b792d771b8352 100644 --- a/addons/t9n/static/src/core/app.xml +++ b/addons/t9n/static/src/core/app.xml @@ -1,8 +1,8 @@ - + - Hello World! + diff --git a/addons/t9n/static/src/core/project_list.js b/addons/t9n/static/src/core/project_list.js new file mode 100644 index 0000000000000..7959beb12b0c6 --- /dev/null +++ b/addons/t9n/static/src/core/project_list.js @@ -0,0 +1,68 @@ +import { Component, useState } from "@odoo/owl"; + +import { useService } from "@web/core/utils/hooks"; + +export class ProjectList extends Component { + static props = {}; + static template = "t9n.ProjectList"; + + setup() { + this.action = useService("action"); + this.state = useState({ + filters: { + searchText: "", + }, + sorting: { + column: "name", + order: "asc", + }, + }); + this.store = useState(useService("t9n.store")); + this.store.fetchProjects(); + } + + get projects() { + const searchTerms = this.state.filters.searchText.trim().toUpperCase(); + const projects = searchTerms + ? this.store.projects.filter((p) => p.name.toUpperCase().includes(searchTerms)) + : [...this.store.projects]; + + projects.sort((p1, p2) => { + let p1Col = p1[this.state.sorting.column]; + let p2Col = p2[this.state.sorting.column]; + + if (this.state.sorting.column !== "resource_count") { + p1Col = p1Col.toLowerCase(); + p2Col = p2Col.toLowerCase(); + } + + if (p1Col < p2Col) { + return this.state.sorting.order === "asc" ? -1 : 1; + } + if (p1Col > p2Col) { + return this.state.sorting.order === "asc" ? 1 : -1; + } + return 0; + }); + return projects; + } + + onClickColumnName(column) { + if (this.state.sorting.column === column) { + this.state.sorting.order = this.state.sorting.order === "asc" ? "desc" : "asc"; + } else { + this.state.sorting.column = column; + this.state.sorting.order = "asc"; + } + } + + onClickProject(id) { + this.action.doAction({ + type: "ir.actions.act_window", + res_id: id, + res_model: "t9n.project", + views: [[false, "form"]], + target: "new", + }); + } +} diff --git a/addons/t9n/static/src/core/project_list.xml b/addons/t9n/static/src/core/project_list.xml new file mode 100644 index 0000000000000..443f2ea61f47a --- /dev/null +++ b/addons/t9n/static/src/core/project_list.xml @@ -0,0 +1,37 @@ + + + +
+
+
+ +
+
+ + + + + + + + + + + + + + + + +
Project Name + Source LanguageTarget LanguagesResource Count
+ + + + +
+
+
+
diff --git a/addons/t9n/static/src/core/project_model.js b/addons/t9n/static/src/core/project_model.js new file mode 100644 index 0000000000000..3cb421ba770b0 --- /dev/null +++ b/addons/t9n/static/src/core/project_model.js @@ -0,0 +1,15 @@ +import { formatList } from "@web/core/l10n/utils"; + +export class Project { + constructor(id, name, srcLang, targetLangs, resourceCount) { + this.id = id; + this.name = name; + this.srcLang = srcLang; + this.targetLangs = targetLangs; + this.resourceCount = resourceCount; + } + + get formattedTargetLanguages() { + return formatList(this.targetLangs.map(({ name }) => name)); + } +} diff --git a/addons/t9n/static/src/core/store.js b/addons/t9n/static/src/core/store.js new file mode 100644 index 0000000000000..766af9f1ae60c --- /dev/null +++ b/addons/t9n/static/src/core/store.js @@ -0,0 +1,30 @@ +import { reactive } from "@odoo/owl"; + +import { Project } from "@t9n/core/project_model"; + +import { registry } from "@web/core/registry"; + +export class Store { + constructor(env, { orm }) { + this.env = env; + this.orm = orm; + this.projects = []; + return reactive(this); + } + + async fetchProjects() { + const projects = await this.orm.call("t9n.project", "get_projects"); + this.projects = projects.map((p) => { + return new Project(p.id, p.name, p.src_lang.name, p.target_langs, p.resources.length); + }); + } +} + +export const storeService = { + dependencies: ["orm"], + start(env, deps) { + return new Store(env, deps); + }, +}; + +registry.category("services").add("t9n.store", storeService);