From 148368dce150bb67176181768da32f041847ab21 Mon Sep 17 00:00:00 2001 From: gregortokarev Date: Mon, 29 Jul 2024 09:05:28 +0300 Subject: [PATCH] Translate `projects` page --- .../SelectModals/ProjectStatusModal.vue | 74 +++-- .../app/src/components/cards/ProjectRow.vue | 149 ++++----- packages/app/src/main.ts | 25 +- packages/app/src/views/office/projects.vue | 313 +++++++++--------- 4 files changed, 281 insertions(+), 280 deletions(-) diff --git a/packages/app/src/components/SelectModals/ProjectStatusModal.vue b/packages/app/src/components/SelectModals/ProjectStatusModal.vue index e2fe02c..5ab8221 100644 --- a/packages/app/src/components/SelectModals/ProjectStatusModal.vue +++ b/packages/app/src/components/SelectModals/ProjectStatusModal.vue @@ -3,59 +3,69 @@ import { useProjectStatusModalStore } from "@store/select-modal.ts"; import { useProjectStatusStore } from "@store/project-status.ts"; import { computed, ref } from "vue"; import SelectModal from "./SelectModal.vue"; +import { useI18n } from "vue-i18n"; const projectStatusModalStore = useProjectStatusModalStore(); const projectStatusStore = useProjectStatusStore(); const query = ref(""); +const { t } = useI18n({ + messages: { + en: { + 'not started': "not started", + "in progress": "in progress", + 'finished': "finished" + }, + ru: { + 'not started': "Не начат", + "in progress": "В прогрессе", + 'finished': "Завершен" + } + } +}) + const projectOptions = computed(() => { - const searchIndex = projectStatusStore.getIndex; - const searchResults = searchIndex.search(query.value); + const searchIndex = projectStatusStore.getIndex; + const searchResults = searchIndex.search(query.value); - const result = query.value - ? searchResults.map((r) => r.item) - : projectStatusStore.projectStatuses; + const result = query.value + ? searchResults.map((r) => r.item) + : projectStatusStore.projectStatuses; - return result.map((project) => ({ - id: project.id, - text: project.title, - icon: project.icon, - })); + return result.map((project) => ({ + id: project.id, + text: t(project.title), + icon: project.icon, + })); }); function onSubmit(id: string) { - projectStatusModalStore.resolveFn && projectStatusModalStore.resolveFn(id); + projectStatusModalStore.resolveFn && projectStatusModalStore.resolveFn(id); } const open = computed({ - get() { - return projectStatusModalStore.open; - }, - set(value: boolean) { - if (!value) { - projectStatusModalStore.close(); - query.value = ""; - } - }, + get() { + return projectStatusModalStore.open; + }, + set(value: boolean) { + if (!value) { + projectStatusModalStore.close(); + query.value = ""; + } + }, }); const checkedIndex = computed(() => { - return projectOptions.value.findIndex( - (p) => p.id === projectStatusModalStore.modalOptions?.statusId, - ); + return projectOptions.value.findIndex( + (p) => p.id === projectStatusModalStore.modalOptions?.statusId, + ); }); @store/select-modal diff --git a/packages/app/src/components/cards/ProjectRow.vue b/packages/app/src/components/cards/ProjectRow.vue index c76a9ff..b97090c 100644 --- a/packages/app/src/components/cards/ProjectRow.vue +++ b/packages/app/src/components/cards/ProjectRow.vue @@ -6,21 +6,39 @@ import { onClickOutside, useDebounceFn } from "@vueuse/core"; import Checkbox from "@components/UI/Checkbox.vue"; import { useProjectStatusStore } from "@store/project-status.ts"; import Icon from "@components/Icon.vue"; +import { useI18n } from "vue-i18n"; const projectStore = useProjectStore(); const projectStatusStore = useProjectStatusStore(); const attrs = useAttrs(); + +const { t } = useI18n({ + messages: { + en: { + 'not started': "not started", + "in progress": "in progress", + 'finished': "finished" + }, + ru: { + 'not started': "Не начат", + "in progress": "В прогрессе", + 'finished': "Завершен" + } + } +}) + + const props = defineProps<{ - project: Project; - selected: boolean; + project: Project; + selected: boolean; }>(); const emit = defineEmits<{ - (e: "update", value: Partial): void; - (e: "update:selected", value: boolean): void; - (e: "setStatus", value: void): void; + (e: "update", value: Partial): void; + (e: "update:selected", value: boolean): void; + (e: "setStatus", value: void): void; }>(); const showColorPicker = ref(false); @@ -28,103 +46,78 @@ const colorPicker = ref(null); const color = ref(null); onClickOutside(colorPicker, () => { - showColorPicker.value = false; + showColorPicker.value = false; }); const colorBound = computed(() => { - if (!color.value) return; + if (!color.value) return; - return color.value.getBoundingClientRect(); + return color.value.getBoundingClientRect(); }); const projectStatus = computed(() => { - if (!props.project.statusId) return; + if (!props.project.statusId) return; - return projectStatusStore.projectStatuses.find( - (ps) => ps.id === props.project.statusId, - ); + return projectStatusStore.projectStatuses.find( + (ps) => ps.id === props.project.statusId, + ); }); function editDraft(event: Event) { - const target = event.target as HTMLElement; - const value = target.textContent; + const target = event.target as HTMLElement; + const value = target.textContent; - value && emit("update", { title: value }); + value && emit("update", { title: value }); } const onEditDraft = useDebounceFn(editDraft, 1000); function onPickColor(color: string) { - emit("update", { color }); + emit("update", { color }); - showColorPicker.value = false; + showColorPicker.value = false; } function onEnter(event: Event) { - const target = event.currentTarget as HTMLElement; - target.blur(); + const target = event.currentTarget as HTMLElement; + target.blur(); } diff --git a/packages/app/src/main.ts b/packages/app/src/main.ts index f73efe9..28032c0 100644 --- a/packages/app/src/main.ts +++ b/packages/app/src/main.ts @@ -111,27 +111,18 @@ app }) .use(router); - - - inject() // injecting analytics -function customRule(choice: number, choicesLength: number) { - if (choice === 0) { - return 0 - } +function customRule(choice: number, _choicesLength: number) { + const calcValue = Math.abs(choice) % 100; + const num = calcValue % 10; - const teen = choice > 10 && choice < 20 - const endsWithOne = choice % 10 === 1 - if (!teen && endsWithOne) { - return 1 - } - if (!teen && choice % 10 >= 2 && choice % 10 <= 4) { - return 2 - } - - return choicesLength < 4 ? 2 : 3 + if (calcValue > 10 && calcValue < 20) return 3; + if (num > 1 && num < 5) return 2; + if (num === 1) return 1; + return 3; } + const i18n = createI18n({ locale: "ru", fallbackLocale: "en", diff --git a/packages/app/src/views/office/projects.vue b/packages/app/src/views/office/projects.vue index 7fdf372..c6b4e6d 100644 --- a/packages/app/src/views/office/projects.vue +++ b/packages/app/src/views/office/projects.vue @@ -11,136 +11,168 @@ import hotkeys from "hotkeys-js"; import { useDeleteModalStore } from "@store/delete-modal"; import { nanoid } from "nanoid"; import { onMounted, onUnmounted, ref } from "vue"; +import { useI18n } from "vue-i18n"; const projectStore = useProjectStore(); const projectStatusModalStore = useProjectStatusModalStore(); +const { t } = useI18n({ + messages: { + en: { + title: "Projects", + create: "New project", + updateStatus: "Update {length} projects?", + column: { + title: "Title", + status: "Status" + }, + deleteModal: { + multipleDelete: `Are you sure you want to delete {length} projects?`, + warning: "Delete would be permanent, you can’t restore issues later.", + spesificDelete: `Are you sure you want to delete “{title}”?` + }, + }, + + ru: { + title: "Проекты", + create: "Новый проект", + updateStatus: "Обновить {length} проект? | Обновить {length} проекта? | Обновить {length} проектов?", + column: { + title: "Название", + status: "Статус" + }, + deleteModal: { + multipleDelete: `Вы уверенны, что хотите удалить {length} проект? | Вы уверенны, что хотите удалить {length} проекта? | Вы уверенны, что хотите удалить {length} проектов?`, + warning: "Удаление будет пермонентным, вы не сможете восстановить проекты", + spesificDelete: `Вы уверены, что хотети удалить “{title}”?` + }, + } + } +}) + const rowsContainer = ref(null); onMounted(async () => { - await projectStore.loadProjects(); + await projectStore.loadProjects(); - hotkeys("C", onCreateProject); + hotkeys("C", onCreateProject); - hotkeys("x", () => { - if (hoveredProjectId.value) { - updateSelection(hoveredProjectId.value); - } - }); + hotkeys("x", () => { + if (hoveredProjectId.value) { + updateSelection(hoveredProjectId.value); + } + }); - hotkeys("cmd+backspace", () => { - removeProject(); - }); + hotkeys("cmd+backspace", () => { + removeProject(); + }); - hotkeys("S", () => { - onSetStatus(); - }); + hotkeys("S", () => { + onSetStatus(); + }); - hotkeys("esc", () => { - selectedProjectsIds.value = []; - }); + hotkeys("esc", () => { + selectedProjectsIds.value = []; + }); }); onUnmounted(() => { - hotkeys.unbind("C", "all", onCreateProject); + hotkeys.unbind("C", "all", onCreateProject); }); function onUpdateProject(id: string, project: Partial) { - if (project?.title === "") return; + if (project?.title === "") return; - projectStore.edit(id, project); + projectStore.edit(id, project); } function onCreateProject() { - projectStore.create(""); + projectStore.create(""); - setTimeout(() => { - if (!rowsContainer.value) return; + setTimeout(() => { + if (!rowsContainer.value) return; - const addedElement = Array.from(rowsContainer.value.children).find((el) => { - const editable = el.querySelector("[contenteditable]")!; - return editable.textContent === ""; + const addedElement = Array.from(rowsContainer.value.children).find((el) => { + const editable = el.querySelector("[contenteditable]")!; + return editable.textContent === ""; + }); + + addedElement?.scrollIntoView(); + addedElement && + focusOnEditableElement( + addedElement.querySelector("[contenteditable]") as HTMLElement, + ); }); - - addedElement?.scrollIntoView(); - addedElement && - focusOnEditableElement( - addedElement.querySelector("[contenteditable]") as HTMLElement, - ); - }); } const selectedProjectsIds = ref([]); function updateSelection(projectId: string) { - if (selectedProjectsIds.value.includes(projectId)) { - const idx = selectedProjectsIds.value.findIndex((id) => id === projectId); - selectedProjectsIds.value.splice(idx, 1); - return; - } + if (selectedProjectsIds.value.includes(projectId)) { + const idx = selectedProjectsIds.value.findIndex((id) => id === projectId); + selectedProjectsIds.value.splice(idx, 1); + return; + } - selectedProjectsIds.value.push(projectId); + selectedProjectsIds.value.push(projectId); } - async function onSetStatus() { - if (selectedProjectsIds.value.length <= 0) { - if (!hoveredProjectId.value) return; + if (selectedProjectsIds.value.length <= 0) { + if (!hoveredProjectId.value) return; - const project = projectStore.getOne(hoveredProjectId.value); - if (!project) return; + const project = projectStore.getOne(hoveredProjectId.value); + if (!project) return; - selectedProjectsIds.value = []; + selectedProjectsIds.value = []; + + const statusId = await projectStatusModalStore.use({ + hintText: project.title, + statusId: project.statusId, + }); + + projectStore.edit(project.id, { statusId }); + + return; + } const statusId = await projectStatusModalStore.use({ - hintText: project.title, - statusId: project.statusId, + hintText: t("updateStatus", { length: selectedProjectsIds.value.length }, selectedProjectsIds.value.length), + statusId: null, }); - projectStore.edit(project.id, { statusId }); + selectedProjectsIds.value.forEach((id) => { + projectStore.edit(id, { statusId }); + }); - return; - } - - const statusId = await projectStatusModalStore.use({ - hintText: `Update ${selectedProjectsIds.value.length} projects`, - statusId: null, - }); - - selectedProjectsIds.value.forEach((id) => { - projectStore.edit(id, { statusId }); - }); - - selectedProjectsIds.value = []; + selectedProjectsIds.value = []; } const deleteModalStore = useDeleteModalStore(); async function removeProject() { - if (selectedProjectsIds.value.length) { + if (selectedProjectsIds.value.length) { + const res = await deleteModalStore.use({ + titleText: t("deleteModal.multipleDelete", { length: selectedProjectsIds.value.length }, selectedProjectsIds.value.length), + descriptionText: t("deleteModal.warning"), + }); + if (!res) return; + + projectStore.remove(selectedProjectsIds.value); + selectedProjectsIds.value = []; + + return; + } + + const draft = hoveredProjectId.value + ? projectStore.getOne(hoveredProjectId.value) + : null; + if (!draft) return; + const res = await deleteModalStore.use({ - titleText: `Are you sure you want to delete ${selectedProjectsIds.value.length} drafts?`, - descriptionText: - "Delete would be permanent, you can’t restore projects later.", + titleText: t("deleteModal.spesificDelete", { title: draft.title }), + descriptionText: t("deleteModal.warning"), }); - if (!res) return; - projectStore.remove(selectedProjectsIds.value); - selectedProjectsIds.value = []; - - return; - } - - const draft = hoveredProjectId.value - ? projectStore.getOne(hoveredProjectId.value) - : null; - if (!draft) return; - - const res = await deleteModalStore.use({ - titleText: `Are you sure you want to delete “${draft.title}”?`, - descriptionText: - "Delete would be permanent, you can’t restore project later.", - }); - - res && projectStore.remove(draft.id); + res && projectStore.remove(draft.id); } const contextMenuOpen = ref(false); @@ -151,88 +183,63 @@ const clickY = ref(0); const clickX = ref(0); function onOpenContextMenu(evt: MouseEvent) { - clickX.value = evt.clientX; - clickY.value = evt.clientY; + clickX.value = evt.clientX; + clickY.value = evt.clientY; - contextMenuOpen.value = true; - setScrolling(false); + contextMenuOpen.value = true; + setScrolling(false); } function onListLeave() { - if (!contextMenuOpen.value) { - hoveredProjectId.value = null; - } + if (!contextMenuOpen.value) { + hoveredProjectId.value = null; + } } function onSelectOption(action: string) { - if (action === "stat") { - onSetStatus(); - } else if (action === "del") { - removeProject(); - } + if (action === "stat") { + onSetStatus(); + } else if (action === "del") { + removeProject(); + } - contextMenuOpen.value = false; + contextMenuOpen.value = false; } @store/select-modal