diff --git a/src/components/SelectActivity.vue b/src/components/SelectActivity.vue index a57c4475..7cc916f6 100644 --- a/src/components/SelectActivity.vue +++ b/src/components/SelectActivity.vue @@ -35,7 +35,8 @@ {{ aiplan.UserName(m.actor_detail).join(' ') + ' ' }} - + + {{ @@ -57,8 +58,12 @@
- У этой задачи пока нет активностей - Для этого документа еще нет активности + У этой задачи пока нет активностей + Для этого документа еще нет активности
@@ -76,7 +81,10 @@ import { useWorkspaceStore } from 'stores/workspace-store'; import aiplan from 'src/utils/aiplan'; import { formatDateTime } from 'src/utils/time'; import { setIntervalFunction } from 'src/utils/helpers'; -import { getIcon, getHistoryText } from 'src/utils/strings'; +import { + getIcon, + entityActivityRenderer, +} from 'src/components/activity/entityActivity'; //components - icon import AvatarImage from './AvatarImage.vue'; @@ -86,10 +94,10 @@ defineProps({ type: Object, required: true, }, - type: { - type: String, - required: true, - } + type: { + type: String, + required: true, + }, }); const emits = defineEmits<{ diff --git a/src/components/activity/entityActivity/index.ts b/src/components/activity/entityActivity/index.ts new file mode 100644 index 00000000..eb44c96a --- /dev/null +++ b/src/components/activity/entityActivity/index.ts @@ -0,0 +1,69 @@ +import { HistoryEntry, HistoryRenderer, ProjectBase } from './types'; + +import { renderBlocks } from './renders/blocks'; +import { renderAssignees, renderWatchers } from './renders/assignees'; +import { + renderCompletedAt, + renderStartDate, + renderTargetDate, +} from './renders/dates'; +import { + renderLinked, + renderLink, + renderLinkUrl, + renderLinkTitle, +} from './renders/links'; +import { renderAttachment } from './renders/attachments'; +import { renderParent, renderBlocking, renderSubIssue } from './renders/issues'; +import { renderComment } from './renders/comments'; +import { renderLabels, renderLabel } from './renders/labels'; +import { renderSprint } from './renders/sprints'; +import { renderDoc, renderSeqId, renderDescription } from './renders/docs'; +import { renderIssueTransfer, renderProject } from './renders/projects'; +import { renderPriority, renderDefault } from './renders/priority'; + +const fieldRenderers: Record = { + blocks: renderBlocks, + assignees: renderAssignees, + watchers: renderWatchers, + completed_at: renderCompletedAt, + start_date: renderStartDate, + target_date: renderTargetDate, + linked: renderLinked, + link: renderLink, + link_url: renderLinkUrl, + link_title: renderLinkTitle, + attachment: renderAttachment, + parent: renderParent, + blocking: renderBlocking, + sub_issue: renderSubIssue, + comment: renderComment, + labels: renderLabels, + label: renderLabel, + sprint: renderSprint, + doc: renderDoc, + seq_id: renderSeqId, + description: renderDescription, + issue_transfer: renderIssueTransfer, + project: renderProject, + priority: renderPriority, +}; + +export function entityActivityRenderer( + m: HistoryEntry, + wsProjects: ProjectBase[], +): string { + const renderer = fieldRenderers[m.field]; + + if (renderer) { + const result = renderer(m, wsProjects); + if (result) return result; + } + + return renderDefault(m); +} + +export { getIcon } from './utils/getIcon'; + +export type { HistoryEntry, HistoryRenderer } from './types'; + diff --git a/src/components/activity/entityActivity/renders/actionTranslations.ts b/src/components/activity/entityActivity/renders/actionTranslations.ts new file mode 100644 index 00000000..1a3aedd5 --- /dev/null +++ b/src/components/activity/entityActivity/renders/actionTranslations.ts @@ -0,0 +1,65 @@ +interface ActionTranslation { + add: string; + remove: string; +} + +export const HISTORY_ACTIONS: Record = { + blocks: { + add: 'добавил(-а)', + remove: 'удалил(-а)', + }, + assignees: { + add: 'добавил(-а)', + remove: 'убрал(-а)', + }, + watchers: { + add: 'добавил(-а)', + remove: 'убрал(-а)', + }, + labels: { + add: 'добавил(-а) тег', + remove: 'удалил(-а) тег', + }, + label: { + add: 'добавил(-а) тег', + remove: 'удалил(-а) тег', + }, + linked: { + add: 'добавил(-а) связь с задачей', + remove: 'убрал(-а) связь с задачей', + }, + sub_issue: { + add: 'добавил(-а) подзадачу', + remove: 'удалил(-а) подзадачу', + }, + parent: { + add: 'заблокировал(-а) задачу', + remove: 'снял(-а)', + }, + blocking: { + add: 'заблокировал(-а) задачу', + remove: 'снял(-а)', + }, + link: { + add: 'добавил(-а) ссылку', + remove: 'удалил(-а) ссылку', + }, + attachment: { + add: 'добавил(-а)', + remove: 'удалил(-а)', + }, + doc: { + add: 'добавил(-а) вложенный документ', + remove: 'убрал(-а) вложенный документ', + }, + sprint: { + add: 'добавил(-а) спринт', + remove: 'убрал(-а) спринт', + }, +}; + +export function translateHistoryAction(field: string, isAdd: boolean): string { + const actions = HISTORY_ACTIONS[field] || HISTORY_ACTIONS.blocks; + return isAdd ? actions.add : actions.remove; +} + diff --git a/src/components/activity/entityActivity/renders/assignees.ts b/src/components/activity/entityActivity/renders/assignees.ts new file mode 100644 index 00000000..a6bfdcf1 --- /dev/null +++ b/src/components/activity/entityActivity/renders/assignees.ts @@ -0,0 +1,24 @@ +import { HistoryEntry } from '../types'; +import aiplan from 'src/utils/aiplan'; +import { translateHistoryAction } from './actionTranslations'; + +export function renderAssignees(m: HistoryEntry): string { + if (m.old_value == null || m.old_value === '') { + return `${translateHistoryAction('assignees', true)} ${aiplan.ru_field(m)} ${aiplan.UserName(m.new_entity_detail).join(' ')}`; + } + if (m.new_value === '') { + return `${translateHistoryAction('assignees', false)} ${aiplan.ru_field(m)} ${aiplan.UserName(m.old_entity_detail).join(' ')}`; + } + return `${aiplan.ru_verb(m)} ${aiplan.ru_field(m)} ${aiplan.ru_value(m)}`; +} + +export function renderWatchers(m: HistoryEntry): string { + if (m.old_value == null || m.old_value === '') { + return `${translateHistoryAction('watchers', true)} ${aiplan.ru_field(m)} ${aiplan.UserName(m.new_entity_detail).join(' ')}`; + } + if (m.new_value === '') { + return `${translateHistoryAction('watchers', false)} ${aiplan.ru_field(m)} ${aiplan.UserName(m.old_entity_detail).join(' ')}`; + } + return `${aiplan.ru_verb(m)} ${aiplan.ru_field(m)} ${aiplan.ru_value(m)}`; +} + diff --git a/src/components/activity/entityActivity/renders/attachments.ts b/src/components/activity/entityActivity/renders/attachments.ts new file mode 100644 index 00000000..10416f82 --- /dev/null +++ b/src/components/activity/entityActivity/renders/attachments.ts @@ -0,0 +1,11 @@ +import { HistoryEntry } from '../types'; +import aiplan from 'src/utils/aiplan'; +import { translateHistoryAction } from './actionTranslations'; + +export function renderAttachment(m: HistoryEntry): string { + if (m.verb === 'created') { + return `${translateHistoryAction('attachment', true)} ${aiplan.ru_field(m)} ${aiplan.ru_value(m)} "${m.new_entity_detail?.asset.name || m.new_value}"`; + } + return `${aiplan.ru_verb(m)} ${aiplan.ru_field(m)} ${aiplan.ru_value(m)} "${m.old_value}"`; +} + diff --git a/src/components/activity/entityActivity/renders/blocks.ts b/src/components/activity/entityActivity/renders/blocks.ts new file mode 100644 index 00000000..ebb5e83d --- /dev/null +++ b/src/components/activity/entityActivity/renders/blocks.ts @@ -0,0 +1,14 @@ +import { HistoryEntry } from '../types'; +import aiplan from 'src/utils/aiplan'; +import { translateHistoryAction } from './actionTranslations'; + +export function renderBlocks(m: HistoryEntry): string { + if (m.old_value == null || m.old_value === '') { + return `${translateHistoryAction('blocks', true)} ${aiplan.ru_field(m)} ${m.new_value}`; + } + if (m.new_value === '') { + return `${translateHistoryAction('blocks', false)} ${aiplan.ru_field(m)} ${m.old_value}`; + } + return `${aiplan.ru_verb(m)} ${aiplan.ru_field(m)} ${aiplan.ru_value(m)}`; +} + diff --git a/src/components/activity/entityActivity/renders/comments.ts b/src/components/activity/entityActivity/renders/comments.ts new file mode 100644 index 00000000..887f3208 --- /dev/null +++ b/src/components/activity/entityActivity/renders/comments.ts @@ -0,0 +1,10 @@ +import { HistoryEntry } from '../types'; +import aiplan from 'src/utils/aiplan'; + +export function renderComment(m: HistoryEntry): string { + if (m.verb === 'created') { + return `добавил(-а) ${aiplan.ru_field(m)} ${aiplan.ru_value(m)}`; + } + return `${aiplan.ru_verb(m)} ${aiplan.ru_field(m)} ${aiplan.ru_value(m)}`; +} + diff --git a/src/components/activity/entityActivity/renders/dates.ts b/src/components/activity/entityActivity/renders/dates.ts new file mode 100644 index 00000000..a18826af --- /dev/null +++ b/src/components/activity/entityActivity/renders/dates.ts @@ -0,0 +1,37 @@ +import { HistoryEntry } from '../types'; +import { formatDate } from 'src/utils/time'; +import { canBeConvertedToDate } from 'src/utils/canBeConvertedToDate'; + +export function renderCompletedAt(): string { + return 'завершил(-а) задачу'; +} + +export function renderStartDate(): string { + return 'начал(-а) выполнение задачи'; +} + +export function renderTargetDate(m: HistoryEntry): string | undefined { + if (m.verb !== 'updated') { + return undefined; + } + + const newDate = + m.new_value && canBeConvertedToDate(m.new_value) + ? formatDate(m.new_value) + : ''; + const oldDate = + m.old_value && canBeConvertedToDate(m.old_value) + ? formatDate(m.old_value.replace(/"/g, '')) + : ''; + + if (m.old_value === '' || !m.old_value) { + return `установил(-а) срок исполнения ${newDate}`; + } else if (m.new_value === '' && oldDate) { + return `убрал(-а) срок исполнения ${oldDate}`; + } else { + return oldDate && newDate + ? `изменил(-а) срок исполнения с ${oldDate} на ${newDate}` + : 'изменил(-а) срок исполнения'; + } +} + diff --git a/src/components/activity/entityActivity/renders/docs.ts b/src/components/activity/entityActivity/renders/docs.ts new file mode 100644 index 00000000..7463b3d9 --- /dev/null +++ b/src/components/activity/entityActivity/renders/docs.ts @@ -0,0 +1,40 @@ +import { HistoryEntry } from '../types'; +import { translateHistoryAction } from './actionTranslations'; + +export function renderDoc(m: HistoryEntry): string | undefined { + if (m.verb === 'remove') { + return `${translateHistoryAction('doc', false)} "${m.old_value}"`; + } + if (m.verb === 'added') { + return `${translateHistoryAction('doc', true)} "${m.new_value}"`; + } + if (m.verb === 'created') { + return `создал(-а) вложенный документ "${m.new_value}"`; + } + return undefined; +} + +export function renderSeqId(m: HistoryEntry): string | undefined { + if (m.verb === 'updated') { + return `изменил(-а) позицию документа в директории с ${+m.old_value! + 1} на ${+m.new_value! + 1}`; + } + if (m.verb === 'move') { + if (m.new_value === m.workspace_detail?.name) { + return 'перенес(-ла) документ в корневую директорию'; + } else { + return `перенес(-ла) документ в директорию "${m.new_value}"`; + } + } + return undefined; +} + +export function renderDescription(m: HistoryEntry): string | undefined { + if (m.verb === 'updated') { + return 'изменил(-а) описание'; + } + if (m.entity_type === 'doc') { + return 'изменил(-а) описание'; + } + return undefined; +} + diff --git a/src/components/activity/entityActivity/renders/issues.ts b/src/components/activity/entityActivity/renders/issues.ts new file mode 100644 index 00000000..fd57791d --- /dev/null +++ b/src/components/activity/entityActivity/renders/issues.ts @@ -0,0 +1,34 @@ +import { HistoryEntry } from '../types'; +import aiplan from 'src/utils/aiplan'; +import { translateHistoryAction } from './actionTranslations'; + +export function renderParent(m: HistoryEntry): string { + if (m.old_value == null || m.old_value === '') { + return `${aiplan.UserName(m.target_user).join(' ')} ${translateHistoryAction('parent', true)} ${m.new_value}`; + } + if (m.new_value === '') { + return `${aiplan.UserName(m.target_user).join(' ')} ${translateHistoryAction('parent', false)} ${aiplan.ru_field(m)} с задачи ${m.old_value}`; + } + return `${aiplan.ru_verb(m)} ${aiplan.ru_field(m)} ${aiplan.ru_value(m)}`; +} + +export function renderBlocking(m: HistoryEntry): string { + if (m.old_value == null || m.old_value === '') { + return `${aiplan.UserName(m.target_user).join(' ')} ${translateHistoryAction('blocking', true)} ${m.new_value}`; + } + if (m.new_value === '') { + return `${aiplan.UserName(m.target_user).join(' ')} ${translateHistoryAction('blocking', false)} ${aiplan.ru_field(m)} с задачи ${m.old_value}`; + } + return `${aiplan.ru_verb(m)} ${aiplan.ru_field(m)} ${aiplan.ru_value(m)}`; +} + +export function renderSubIssue(m: HistoryEntry): string | undefined { + if (m.verb === 'added') { + return `${translateHistoryAction('sub_issue', true)} ${m.new_value}`; + } + if (m.verb === 'removed') { + return `${translateHistoryAction('sub_issue', false)} ${m.old_value}`; + } + return undefined; +} + diff --git a/src/components/activity/entityActivity/renders/labels.ts b/src/components/activity/entityActivity/renders/labels.ts new file mode 100644 index 00000000..7980041f --- /dev/null +++ b/src/components/activity/entityActivity/renders/labels.ts @@ -0,0 +1,23 @@ +import { HistoryEntry } from '../types'; +import { translateHistoryAction } from './actionTranslations'; + +export function renderLabels(m: HistoryEntry): string | undefined { + if (m.verb === 'added') { + return `${translateHistoryAction('labels', true)} ${m.new_value}`; + } + if (m.verb === 'removed') { + return `${translateHistoryAction('labels', false)} ${m.old_value}`; + } + return undefined; +} + +export function renderLabel(m: HistoryEntry): string | undefined { + if (m.verb === 'added') { + return `${translateHistoryAction('label', true)} ${m.new_value}`; + } + if (m.verb === 'removed') { + return `${translateHistoryAction('label', false)} ${m.old_value}`; + } + return undefined; +} + diff --git a/src/components/activity/entityActivity/renders/links.ts b/src/components/activity/entityActivity/renders/links.ts new file mode 100644 index 00000000..1b2b5c0e --- /dev/null +++ b/src/components/activity/entityActivity/renders/links.ts @@ -0,0 +1,34 @@ +import { HistoryEntry } from '../types'; +import { translateHistoryAction } from './actionTranslations'; + +export function renderLinked(m: HistoryEntry): string | undefined { + if (m.verb === 'updated' && !m.new_value && m.old_value) { + return `${translateHistoryAction('linked', false)} ${m.old_value}`; + } + if (m.verb === 'updated' && !m.old_value && m.new_value) { + return `${translateHistoryAction('linked', true)} ${m.new_value}`; + } + return undefined; +} + +export function renderLink(m: HistoryEntry): string | undefined { + if (m.verb === 'created' && !m.old_value && m.new_value) { + return `${translateHistoryAction('link', true)} "${m.new_entity_detail?.title || m.new_value}"`; + } + if (m.verb === 'deleted' && m.old_value) { + return `${translateHistoryAction('link', false)} "${m.old_entity_detail?.title || m.old_value}"`; + } + return undefined; +} + +export function renderLinkUrl(m: HistoryEntry): string | undefined { + if (m.verb === 'updated' && m.new_value) { + return `изменил(-а) ссылку c"${m.old_value}" на "${m.new_value}"`; + } + return undefined; +} + +export function renderLinkTitle(m: HistoryEntry): string { + return `изменил(-а) название ссылки с "${m.old_value}" на "${m.new_value}"`; +} + diff --git a/src/components/activity/entityActivity/renders/priority.ts b/src/components/activity/entityActivity/renders/priority.ts new file mode 100644 index 00000000..f13094c3 --- /dev/null +++ b/src/components/activity/entityActivity/renders/priority.ts @@ -0,0 +1,14 @@ +import { HistoryEntry } from '../types'; +import aiplan from 'src/utils/aiplan'; + +export function renderPriority(m: HistoryEntry): string { + if (m.verb === 'updated' && m.old_value === '') { + return `установил(-а) ${aiplan.ru_field(m)} ${aiplan.ru_value(m)}`; + } + return `${aiplan.ru_verb(m)} ${aiplan.ru_field(m)} ${aiplan.ru_value(m)}`; +} + +export function renderDefault(m: HistoryEntry): string { + return `${aiplan.ru_verb(m)} ${aiplan.ru_field(m)} ${aiplan.ru_value(m)}`; +} + diff --git a/src/components/activity/entityActivity/renders/projects.ts b/src/components/activity/entityActivity/renders/projects.ts new file mode 100644 index 00000000..18eee6f1 --- /dev/null +++ b/src/components/activity/entityActivity/renders/projects.ts @@ -0,0 +1,40 @@ +import { HistoryEntry, HistoryRenderer } from '../types'; + +export const renderIssueTransfer: HistoryRenderer = (m, wsProjects) => { + const newProject = wsProjects.find( + (project) => project.id === (m.new_identifier || m.project_id), + ); + const newVal = newProject + ? `в проект "${newProject.name}"` + : 'в скрытый/удаленный проект'; + const oldProject = wsProjects.find( + (project) => project.id === m.old_identifier, + ); + const oldVal = oldProject + ? `из проекта "${oldProject.name}"` + : 'из скрытого/удаленного проекта'; + + if (m.verb === 'copied') { + return `скопировал(-а) задачу ${oldVal}`; + } + + if (m.verb === 'move') { + return `перенес(-ла) задачу ${oldVal}`; + } + + return `перенес(-ла) задачу ${oldVal} ${newVal}`; +}; + +export function renderProject(m: HistoryEntry): string | undefined { + if (m.verb === 'move') { + const newProjectName = m.project_detail?.name + ? m.project_detail.name + : 'в скрытый/удаленный проект'; + const oldProjectName = m.old_entity_detail?.name + ? m.old_entity_detail.name + : 'из скрытого/удаленного проекта'; + return `перенес(-ла) задачу из "${oldProjectName}" в "${newProjectName}"`; + } + return undefined; +} + diff --git a/src/components/activity/entityActivity/renders/sprints.ts b/src/components/activity/entityActivity/renders/sprints.ts new file mode 100644 index 00000000..bea1ec03 --- /dev/null +++ b/src/components/activity/entityActivity/renders/sprints.ts @@ -0,0 +1,15 @@ +import { HistoryEntry } from '../types'; +import { translateHistoryAction } from './actionTranslations'; + +export function renderSprint(m: HistoryEntry): string | undefined { + if (m.verb === 'updated') { + return `изменил(-а) спринт с "${m.old_value}" на "${m.new_value}"`; + } + if (m.verb === 'added') { + return `${translateHistoryAction('sprint', true)} "${m.new_value}"`; + } + if (m.verb === 'removed') { + return `${translateHistoryAction('sprint', false)} "${m.old_value}"`; + } + return undefined; +} diff --git a/src/components/activity/entityActivity/types.ts b/src/components/activity/entityActivity/types.ts new file mode 100644 index 00000000..19e992ad --- /dev/null +++ b/src/components/activity/entityActivity/types.ts @@ -0,0 +1,26 @@ +export interface ProjectBase { + id?: string; + name?: string; +} + +export interface HistoryEntry { + field: string; + verb: string; + old_value: string | null; + new_value: string | null; + old_identifier?: string; + new_identifier?: string; + old_entity_detail?: any; + new_entity_detail?: any; + target_user?: any; + project_id?: string; + workspace_detail?: { name: string }; + project_detail?: { name: string }; + entity_type?: string; +} + +export type HistoryRenderer = ( + m: HistoryEntry, + wsProjects: ProjectBase[], +) => string | undefined; + diff --git a/src/components/activity/entityActivity/utils/getIcon.ts b/src/components/activity/entityActivity/utils/getIcon.ts new file mode 100644 index 00000000..089a4b07 --- /dev/null +++ b/src/components/activity/entityActivity/utils/getIcon.ts @@ -0,0 +1,13 @@ +export const getIcon = (name: string): string => { + switch (name) { + case 'deleted': + return 'delete'; + case 'created': + return 'add_circle_outline'; + case 'updated': + return 'edit'; + default: + return 'visibility'; + } +}; + diff --git a/src/components/activity/renders/project-activity.ts b/src/components/activity/renders/project-activity.ts index 62be1745..7734b78b 100644 --- a/src/components/activity/renders/project-activity.ts +++ b/src/components/activity/renders/project-activity.ts @@ -1,6 +1,10 @@ import { computed } from 'vue'; -import { valToNet, valToRole } from 'src/utils/strings'; -import { stateRUS, translateVerb } from 'src/utils/translator'; +import { + valToNet, + valToRole, + stateRUS, + translateVerb, +} from 'src/utils/translator'; import { getFullName } from 'src/utils/helpers'; import aiplan from 'src/utils/aiplan'; import { translateAction } from './actionTranslations'; diff --git a/src/components/activity/renders/workspace-activity.ts b/src/components/activity/renders/workspace-activity.ts index 89600345..96d87697 100644 --- a/src/components/activity/renders/workspace-activity.ts +++ b/src/components/activity/renders/workspace-activity.ts @@ -1,4 +1,4 @@ -import { valToRole } from 'src/utils/strings'; +import { valToRole } from 'src/utils/translator'; import { translateVerb } from 'src/utils/translator'; import aiplan from 'src/utils/aiplan'; import { getURLDoc } from './doc-activity'; diff --git a/src/modules/project-settings/members/ui/MembersProjectSettings.vue b/src/modules/project-settings/members/ui/MembersProjectSettings.vue index 381e4c6f..f3fab2ac 100644 --- a/src/modules/project-settings/members/ui/MembersProjectSettings.vue +++ b/src/modules/project-settings/members/ui/MembersProjectSettings.vue @@ -177,7 +177,7 @@ import SearchIcon from 'src/components/icons/SearchIcon.vue'; import AvatarImage from 'src/components/AvatarImage.vue'; // utils -import { valToRole } from 'src/utils/strings'; +import { valToRole } from 'src/utils/translator'; // constants import { diff --git a/src/modules/project-settings/members/ui/dialogs/EditProjectMemberDialog.vue b/src/modules/project-settings/members/ui/dialogs/EditProjectMemberDialog.vue index 288fa066..7a3a7391 100644 --- a/src/modules/project-settings/members/ui/dialogs/EditProjectMemberDialog.vue +++ b/src/modules/project-settings/members/ui/dialogs/EditProjectMemberDialog.vue @@ -34,7 +34,7 @@ import { onUpdated, ref, watch } from 'vue'; import { useProjectStore } from 'src/stores/project-store'; import { useWorkspaceStore } from 'src/stores/workspace-store'; // utils -import { valToRole } from 'src/utils/strings'; +import { valToRole } from 'src/utils/translator'; // constants import { ROLES } from 'src/constants/constants'; diff --git a/src/modules/workspace-notifications/utils/project-notification.ts b/src/modules/workspace-notifications/utils/project-notification.ts index dbd7ca55..de56da5d 100644 --- a/src/modules/workspace-notifications/utils/project-notification.ts +++ b/src/modules/workspace-notifications/utils/project-notification.ts @@ -1,5 +1,10 @@ -import { capitalizeFirstLetter, valToNet, valToRole } from 'src/utils/strings'; -import { stateRUS, translatePrioritets, translateVerb } from 'src/utils/translator'; +import { capitalizeFirstLetter } from 'src/utils/strings'; +import { valToNet, valToRole } from 'src/utils/translator'; +import { + stateRUS, + translatePrioritets, + translateVerb, +} from 'src/utils/translator'; import { getFullName } from 'src/utils/helpers'; import { NotificationsNotificationDetailResponse } from '@aisa-it/aiplan-api-ts/src/data-contracts'; import { getProjectLink } from 'src/utils/links'; diff --git a/src/modules/workspace-notifications/utils/workspace-notification.ts b/src/modules/workspace-notifications/utils/workspace-notification.ts index 92b416d6..64142cca 100644 --- a/src/modules/workspace-notifications/utils/workspace-notification.ts +++ b/src/modules/workspace-notifications/utils/workspace-notification.ts @@ -1,4 +1,4 @@ -import { valToRole } from 'src/utils/strings'; +import { valToRole } from 'src/utils/translator'; import { translateVerb } from 'src/utils/translator'; import { getFullName } from 'src/utils/helpers'; import { getDocLink } from '../helpers/getDocLink'; diff --git a/src/modules/workspace-settings/ui/tabs/MembersWorkspaceSettings.vue b/src/modules/workspace-settings/ui/tabs/MembersWorkspaceSettings.vue index f40eb043..1e633548 100644 --- a/src/modules/workspace-settings/ui/tabs/MembersWorkspaceSettings.vue +++ b/src/modules/workspace-settings/ui/tabs/MembersWorkspaceSettings.vue @@ -200,7 +200,7 @@ import SearchIcon from 'src/components/icons/SearchIcon.vue'; import AvatarImage from 'src/components/AvatarImage.vue'; // utils -import { valToRole } from 'src/utils/strings'; +import { valToRole } from 'src/utils/translator'; // constants import { diff --git a/src/stores/project-store.ts b/src/stores/project-store.ts index b08fa5a7..49aea0f1 100644 --- a/src/stores/project-store.ts +++ b/src/stores/project-store.ts @@ -2,7 +2,7 @@ import { defineStore, storeToRefs } from 'pinia'; import { withInterceptors } from 'src/utils/interceptorsWithInstanceClass'; import { Projects } from '@aisa-it/aiplan-api-ts/src/Projects'; import { Users } from '@aisa-it/aiplan-api-ts/src/Users'; -import { valToNet } from 'src/utils/strings'; +import { valToNet } from 'src/utils/translator'; import { getProjectEmojiViaCode } from 'src/utils/helpers'; import { useWorkspaceStore } from './workspace-store'; import { useRolesStore } from './roles-store'; diff --git a/src/utils/colors.ts b/src/utils/colors.ts new file mode 100644 index 00000000..807d29b1 --- /dev/null +++ b/src/utils/colors.ts @@ -0,0 +1,21 @@ +export const generateRandomColor = (string: string): string => { + if (!string) return 'rgb(var(--color-accent))'; + + string = `${string}`; + + const uniqueId = string.length.toString() + string; // Unique identifier based on string length + const combinedString = uniqueId + string; + + const hash = Array.from(combinedString).reduce((acc, char) => { + const charCode = char.charCodeAt(0); + return (acc << 5) - acc + charCode; + }, 0); + + const hue = hash % 360; + const saturation = 70; // Higher saturation for pastel colors + const lightness = 60; // Mid-range lightness for pastel colors + + const randomColor = `hsl(${hue}, ${saturation}%, ${lightness}%)`; + + return randomColor; +}; diff --git a/src/utils/strings.ts b/src/utils/strings.ts index 039a5593..e00c1060 100644 --- a/src/utils/strings.ts +++ b/src/utils/strings.ts @@ -1,8 +1,3 @@ -import aiplan from 'src/utils/aiplan'; -import { IProject } from 'src/interfaces/projects'; -import { formatDate } from './time'; -import { canBeConvertedToDate } from './canBeConvertedToDate'; - export const addSpaceIfCamelCase = (str: string) => str.replace(/([a-z])([A-Z])/g, '$1 $2'); @@ -90,28 +85,6 @@ export const cosineSimilarity = (a: string, b: string) => { return dotProduct / Math.sqrt(magnitudeA * magnitudeB); }; -export const generateRandomColor = (string: string): string => { - if (!string) return 'rgb(var(--color-accent))'; - - string = `${string}`; - - const uniqueId = string.length.toString() + string; // Unique identifier based on string length - const combinedString = uniqueId + string; - - const hash = Array.from(combinedString).reduce((acc, char) => { - const charCode = char.charCodeAt(0); - return (acc << 5) - acc + charCode; - }, 0); - - const hue = hash % 360; - const saturation = 70; // Higher saturation for pastel colors - const lightness = 60; // Mid-range lightness for pastel colors - - const randomColor = `hsl(${hue}, ${saturation}%, ${lightness}%)`; - - return randomColor; -}; - export const getFirstCharacters = (str: string) => { const words = str.trim().split(' '); if (words.length === 1) { @@ -120,258 +93,3 @@ export const getFirstCharacters = (str: string) => { return words[0].charAt(0) + words[1].charAt(0); } }; - -export const valToRole = (val: number) => { - switch (val) { - case 5: - return { value: 5, label: 'Гость' }; - case 10: - return { value: 10, label: 'Участник' }; - case 15: - return { value: 15, label: 'Администратор' }; - default: - return { value: 5, label: 'Гость' }; - } -}; - -export const valToNet = (val: boolean) => { - switch (val) { - case false: - return { value: false, label: 'Скрытый' }; - case true: - return { value: true, label: 'Публичный' }; - default: - return { value: false, label: 'Скрытый' }; - } -}; - -export const getIcon = (name: string): string => { - switch (name) { - case 'deleted': - return 'delete'; - case 'created': - return 'add_circle_outline'; - case 'updated': - return 'edit'; - default: - return 'visibility'; - } -}; - -export const getHistoryText = (m: any, wsProjects: IProject[]) => { - switch (m.field) { - case 'blocks': - if (m.old_value == null || m.old_value == '') - return `добавил(-а) ${aiplan.ru_field(m)} ${m.new_value}`; - if (m.new_value == '') - return `удалил(-а) ${aiplan.ru_field(m)} ${m.old_value}`; - return `${aiplan.ru_verb(m)} - ${aiplan.ru_field(m)} ${aiplan.ru_value(m)}`; - case 'assignees': - if (m.old_value == null || m.old_value == '') - return `добавил(-а) ${aiplan.ru_field(m)} - ${aiplan.UserName(m.new_entity_detail).join(' ')}`; - if (m.new_value == '') - return `убрал(-а) ${aiplan.ru_field(m)} - ${aiplan.UserName(m.old_entity_detail).join(' ')}`; - case 'completed_at': - return 'завершил(-а) задачу'; - case 'start_date': - return 'начал(-а) выполнение задачи'; - case 'watchers': - if (m.old_value == null || m.old_value == '') - return `добавил(-а) ${aiplan.ru_field(m)} - ${aiplan.UserName(m.new_entity_detail).join(' ')}`; - if (m.new_value == '') - return `убрал(-а) ${aiplan.ru_field(m)} - ${aiplan.UserName(m.old_entity_detail).join(' ')}`; - case 'parent': - case 'blocking': - if (m.old_value == null || m.old_value == '') - return `${aiplan - .UserName(m.target_user) - .join(' ')} заблокировал(-а) задачу ${m.new_value}`; - if (m.new_value == '') - return `${aiplan - .UserName(m.target_user) - .join(' ')} снял(-а) ${aiplan.ru_field(m)} с задачи ${m.old_value}`; - - return `${aiplan.ru_verb(m)} - ${aiplan.ru_field(m)} ${aiplan.ru_value(m)}`; - case 'comment': - if (m.verb === 'created') { - return `добавил(-а) - ${aiplan.ru_field(m)} ${aiplan.ru_value(m)}`; - } - return `${aiplan.ru_verb(m)} - ${aiplan.ru_field(m)} ${aiplan.ru_value(m)}`; - case 'attachment': - if (m.verb === 'created') { - return `добавил(-а) ${aiplan.ru_field(m)} ${aiplan.ru_value(m)} "${ - m.new_entity_detail?.asset.name || m.new_value - }" `; - } - return `${aiplan.ru_verb(m)} ${aiplan.ru_field(m)} ${aiplan.ru_value( - m, - )} "${m.old_value}"`; - case 'issue_transfer': - const newProject = wsProjects.find( - (project) => project.id === (m.new_identifier || m.project_id), - ); - const newVal = newProject - ? `в проект "${newProject.name}"` - : 'в скрытый/удаленный проект'; - const oldProject = wsProjects.find( - (project) => project.id === m.old_identifier, - ); - const oldVal = oldProject - ? `из проекта "${oldProject.name}"` - : 'из скрытого/удаленного проекта'; - - if (m.verb === 'copied') { - return `скопировал(-а) задачу ${oldVal}`; - } - - if (m.verb === 'move') { - return `перенес(-ла) задачу ${oldVal}`; - } - - return `перенес(-ла) задачу ${oldVal} ${newVal}`; - - case 'linked': - if (m.verb === 'updated' && !m.new_value && m.old_value) { - return `убрал(-а) связь с задачей ${m.old_value}`; - } - if (m.verb === 'updated' && !m.old_value && m.new_value) { - return `добавил(-а) связь с задачей ${m.new_value}`; - } - - case 'sub_issue': - if (m.verb === 'added') { - return `добавил(-а) подзадачу ${m.new_value}`; - } - if (m.verb === 'removed') { - return `удалил(-а) подзадачу ${m.old_value}`; - } - case 'priority': - if (m.verb === 'updated' && m.old_value === '') { - return `установил(-а) - ${aiplan.ru_field(m)} ${aiplan.ru_value(m)}`; - } - return `${aiplan.ru_verb(m)} - ${aiplan.ru_field(m)} ${aiplan.ru_value(m)}`; - - case 'labels': - if (m.verb === 'added') { - return `добавил(-а) тег ${m.new_value}`; - } - if (m.verb === 'removed') { - return `удалил(-а) тег ${m.old_value}`; - } - case 'label': - if (m.verb === 'added') { - return `добавил(-а) тег ${m.new_value}`; - } - if (m.verb === 'removed') { - return `удалил(-а) тег ${m.old_value}`; - } - case 'link': - if (m.verb === 'created' && !m.old_value && m.new_value) { - return `добавил(-а) ссылку "${ - m.new_entity_detail?.title || m.new_value - }"`; - } - - if (m.verb === 'deleted' && m.old_value) { - return `удалил(-а) ссылку "${ - m.old_entity_detail?.title || m.old_value - }"`; - } - case 'link_url': - if (m.verb === 'updated' && m.new_value) { - return `изменил(-а) ссылку c"${m.old_value}" на "${m.new_value}" `; - } - - case 'link_title': - return `изменил(-а) название ссылки с "${m.old_value}" на "${m.new_value}"`; - case 'doc': - if (m.verb === 'remove') { - return `убрал(-а) вложенный документ "${m.old_value}"`; - } - if (m.verb === 'added') { - return `добавил(-а) вложенный документ "${m.new_value}"`; - } - if (m.verb === 'created') { - return `создал(-а) вложенный документ "${m.new_value}"`; - } - - case 'seq_id': - if (m.verb === 'updated') { - return `изменил(-а) позицию документа в директории с ${ - +m.old_value + 1 - } на ${+m.new_value + 1}`; - } - if (m.verb === 'move') { - if (m.new_value === m.workspace_detail.name) { - return 'перенес(-ла) документ в корневую директорию'; - } else { - return `перенес(-ла) документ в директорию "${m.new_value}"`; - } - } - case 'sprint': - if (m.verb === 'updated') { - return `изменил(-а) спринт с "${m.old_value}" на "${m.new_value}"`; - } - if (m.verb === 'added') { - return `добавил(-а) спринт "${m.new_value}"`; - } - if (m.verb === 'removed') { - return `убрал(-а) спринт "${m.old_value}"`; - } - case 'description': - if (m.verb === 'updated') { - return 'изменил(-а) описание'; - } - if (m.entity_type === 'doc') { - return 'изменил(-а) описание'; - } - - case 'project': - if (m.verb === 'move') { - const newProjectName = m.project_detail?.name - ? m.project_detail.name - : 'в скрытый/удаленный проект'; - const oldProjectName = m.old_entity_detail.name - ? m.old_entity_detail.name - : 'из скрытого/удаленного проекта'; - return `перенес(-ла) задачу из "${oldProjectName}" в "${newProjectName}"`; - } - - case 'target_date': - if (m.verb === 'updated') { - const newDate = - m.new_value instanceof Date || canBeConvertedToDate(m.new_value) - ? formatDate(m.new_value) - : ''; - const oldDate = - m.old_value && - (m.old_value instanceof Date || canBeConvertedToDate(m.old_value)) - ? formatDate(m.old_value.replace(/"/g, '')) - : ''; - - if (m.old_value === '' || !m.old_value) { - return `установил(-а) срок исполнения ${newDate}`; - } else if (m.new_value === '' && oldDate) { - return `убрал(-а) срок исполнения ${oldDate}`; - } else { - return oldDate && newDate - ? `изменил(-а) срок исполнения с ${oldDate} на ${newDate}` - : 'изменил(-а) срок исполнения'; - } - } - - default: - return `${aiplan.ru_verb(m)} - ${aiplan.ru_field(m)} ${aiplan.ru_value(m)}`; - } -}; diff --git a/src/utils/translator.ts b/src/utils/translator.ts index c8b02718..653337af 100644 --- a/src/utils/translator.ts +++ b/src/utils/translator.ts @@ -36,6 +36,30 @@ export const translatePrioritets = (priority: string) => { } }; +export const valToRole = (val: number) => { + switch (val) { + case 5: + return { value: 5, label: 'Гость' }; + case 10: + return { value: 10, label: 'Участник' }; + case 15: + return { value: 15, label: 'Администратор' }; + default: + return { value: 5, label: 'Гость' }; + } +}; + +export const valToNet = (val: boolean) => { + switch (val) { + case false: + return { value: false, label: 'Скрытый' }; + case true: + return { value: true, label: 'Публичный' }; + default: + return { value: false, label: 'Скрытый' }; + } +}; + export const translateFirstCharUpperCasePriority = (priority: string) => { switch (priority) { case 'urgent':