From a27fbc2a15b4705dad794ef48f2edb12d7eacbd1 Mon Sep 17 00:00:00 2001 From: CREDO23 Date: Wed, 2 Oct 2024 23:35:03 +0200 Subject: [PATCH 1/7] set the tab to outstanding if there are outstanding tasks next day --- apps/web/lib/features/task/task-filters.tsx | 22 ++++++++++++-------- apps/web/lib/features/user-profile-plans.tsx | 9 ++++++-- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/apps/web/lib/features/task/task-filters.tsx b/apps/web/lib/features/task/task-filters.tsx index ec9475b71..11a53ea4c 100644 --- a/apps/web/lib/features/task/task-filters.tsx +++ b/apps/web/lib/features/task/task-filters.tsx @@ -31,6 +31,7 @@ import { AddManualTimeModal } from '../manual-time/add-manual-time-modal'; import { useTimeLogs } from '@app/hooks/features/useTimeLogs'; import { estimatedTotalTime, getTotalTasks } from './daily-plan'; import { DAILY_PLAN_SUGGESTION_MODAL_DATE } from '@app/constants'; +import { usePathname } from 'next/navigation'; export type ITab = 'worked' | 'assigned' | 'unassigned' | 'dailyplan' | 'stats'; @@ -68,6 +69,7 @@ export function useTaskFilter(profile: I_UserProfilePage) { () => profile.userProfile?.id === user?.id || isManagerConnectedUser != -1, [isManagerConnectedUser, profile.userProfile?.id, user?.id] ); + const path = usePathname(); // const [tab, setTab] = useState(defaultValue || 'worked'); const [tab, setTab] = useLocalStorageState('task-tab', 'worked'); @@ -172,20 +174,22 @@ export function useTaskFilter(profile: I_UserProfilePage) { // Set the tab to assigned if user has not planned tasks (if outstanding is empty) (on first load) useEffect(() => { - if (dailyPlanSuggestionModalDate) { - if (!getTotalTasks(todayPlan)) { - if (estimatedTotalTime(outstandingPlans).totalTasks) { - setTab('dailyplan'); - } else if (profile.tasksGrouped.assignedTasks.length) { - setTab('assigned'); - } else { - setTab('unassigned'); + if (dailyPlanSuggestionModalDate != new Date().toISOString().split('T')[0] && path.split('/')[1] == 'profile') { + if (estimatedTotalTime(outstandingPlans).totalTasks) { + setTab('dailyplan'); + } else { + if (!getTotalTasks(todayPlan)) { + if (profile.tasksGrouped.assignedTasks.length) { + setTab('assigned'); + } else { + setTab('unassigned'); + } } } } // eslint-disable-next-line react-hooks/exhaustive-deps - }, [dailyPlanSuggestionModalDate]); + }, []); // Reset status applied filter status when filter changed useEffect(() => { diff --git a/apps/web/lib/features/user-profile-plans.tsx b/apps/web/lib/features/user-profile-plans.tsx index e02acb305..cd4d13c4d 100644 --- a/apps/web/lib/features/user-profile-plans.tsx +++ b/apps/web/lib/features/user-profile-plans.tsx @@ -14,7 +14,7 @@ import { useAuthenticateUser, useCanSeeActivityScreen, useDailyPlan, useUserProf import { useDateRange } from '@app/hooks/useDateRange'; import { filterDailyPlan } from '@app/hooks/useFilterDateRange'; import { useLocalStorageState } from '@app/hooks/useLocalStorageState'; -import { HAS_VISITED_OUTSTANDING_TASKS } from '@app/constants'; +import { DAILY_PLAN_SUGGESTION_MODAL_DATE, HAS_VISITED_OUTSTANDING_TASKS } from '@app/constants'; import { IDailyPlan, ITeamTask } from '@app/interfaces'; import { dataDailyPlanState } from '@app/stores'; import { fullWidthState } from '@app/stores/fullWidth'; @@ -38,6 +38,7 @@ import ViewsHeaderTabs from './task/daily-plan/views-header-tabs'; import TaskBlockCard from './task/task-block-card'; import { TaskCard } from './task/task-card'; import moment from 'moment'; +import { usePathname } from 'next/navigation'; export type FilterTabs = 'Today Tasks' | 'Future Tasks' | 'Past Tasks' | 'All Tasks' | 'Outstanding'; type FilterOutstanding = 'ALL' | 'DATE'; @@ -69,14 +70,18 @@ export function UserProfilePlans() { const [filterFuturePlanData, setFilterFuturePlanData] = useState(futurePlans); const [filterPastPlanData, setFilteredPastPlanData] = useState(pastPlans); const [filterAllPlanData, setFilterAllPlanData] = useState(sortedPlans); + const dailyPlanSuggestionModalDate = window && window?.localStorage.getItem(DAILY_PLAN_SUGGESTION_MODAL_DATE); + const path = usePathname(); // Set the tab plan tab to outstanding if user has no daily plan and there are outstanding tasks (on first load) useEffect(() => { - if (!getTotalTasks(todayPlan)) { + if (dailyPlanSuggestionModalDate != new Date().toISOString().split('T')[0] && path.split('/')[1] == 'profile') { if (estimatedTotalTime(outstandingPlans).totalTasks) { setCurrentTab('Outstanding'); } + window.localStorage.setItem(DAILY_PLAN_SUGGESTION_MODAL_DATE, new Date().toISOString().split('T')[0]); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); From 2ce5a4292c508e76e4872e8c9a75fdfe28b9430c Mon Sep 17 00:00:00 2001 From: CREDO23 Date: Tue, 8 Oct 2024 00:33:51 +0200 Subject: [PATCH 2/7] show live for task on which the user is working on --- apps/web/lib/components/kanban-card.tsx | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/apps/web/lib/components/kanban-card.tsx b/apps/web/lib/components/kanban-card.tsx index 4277ff74a..7c98dc847 100644 --- a/apps/web/lib/components/kanban-card.tsx +++ b/apps/web/lib/components/kanban-card.tsx @@ -5,7 +5,9 @@ import { useAuthenticateUser, useOrganizationTeams, useTaskStatistics, - useTeamMemberCard + useTeamMemberCard, + useTeamTasks, + useTimerView } from '@app/hooks'; import ImageComponent, { ImageOverlapperProps } from './image-overlapper'; import { TaskAllStatusTypes, TaskInput, TaskIssueStatus } from 'lib/features'; @@ -13,7 +15,6 @@ import Link from 'next/link'; import CircularProgress from '@components/ui/svgs/circular-progress'; import { HorizontalSeparator } from './separator'; import { secondsToTime } from '@app/helpers'; -import { TaskStatus } from '@app/constants'; import MenuKanbanCard from '@components/pages/kanban/menu-kanban-card'; import { activeTeamTaskId } from '@app/stores'; import { useAtom } from 'jotai'; @@ -158,6 +159,8 @@ export default function Item(props: ItemProps) { const { user } = useAuthenticateUser(); const { getEstimation } = useTaskStatistics(0); const [activeTask, setActiveTask] = useAtom(activeTeamTaskId); + const {activeTeamTask} = useTeamTasks() + const {timerStatus} = useTimerView() const members = activeTeam?.members || []; const currentUser = members.find((m) => m.employee.userId === user?.id); @@ -285,7 +288,7 @@ export default function Item(props: ItemProps) {
- {item.status === TaskStatus.INPROGRESS ? ( + {item.id === activeTeamTask?.id && timerStatus?.running ? (
Live:

@@ -295,8 +298,8 @@ export default function Item(props: ItemProps) { ) : (

Worked: -

- {h}h : {m}m : {s}s +

+ {h}h : {m}m

)} From 18646893ba58c77866ef4e4fc6988881aa815c98 Mon Sep 17 00:00:00 2001 From: "Thierry CH." Date: Tue, 8 Oct 2024 18:30:06 +0200 Subject: [PATCH 3/7] format the time string (#3119) --- apps/web/app/helpers/date.ts | 36 +++++++++++++++++-- .../add-task-estimation-hours-modal.tsx | 21 ++++------- 2 files changed, 40 insertions(+), 17 deletions(-) diff --git a/apps/web/app/helpers/date.ts b/apps/web/app/helpers/date.ts index 6078cf3ac..5bc4349fa 100644 --- a/apps/web/app/helpers/date.ts +++ b/apps/web/app/helpers/date.ts @@ -151,7 +151,6 @@ export const formatIntegerToHour = (number: number) => { return formattedHour; }; - export const isTestDateRange = (itemDate: Date, from?: Date, to?: Date) => { if (from && to) { return itemDate >= from && itemDate <= to; @@ -162,9 +161,40 @@ export const isTestDateRange = (itemDate: Date, from?: Date, to?: Date) => { } else { return true; // or false, depending on your default logic } -} - +}; export function convertHourToSeconds(hours: number) { return hours * 60 * 60; } + +/** + * A helper function to parse a time string + * + * @param timeString - The time string to be formated. + * + * @returns {string} The formated time string + */ +export function formatTimeString(timeString: string): string { + // Extract hours and minutes using regex + const matches = timeString.match(/(\d+)h\s*(\d+)m|\b(\d+)m\b|\b(\d+)h\b/); + + let result = ''; + + if (matches) { + const hours = matches[1] || matches[4]; // Group 1 for hours when both exist, Group 4 for hours only + const minutes = matches[2] || matches[3]; // Group 2 for minutes when both exist, Group 3 for minutes only + + if (parseInt(hours) > 0) { + result += `${hours}h`; + } + + if (parseInt(minutes) > 0) { + if (result) { + result += ' '; // Add space if hours were included + } + result += `${minutes}m`; + } + } + + return result.length ? result : '0h 00m'; +} diff --git a/apps/web/lib/features/daily-plan/add-task-estimation-hours-modal.tsx b/apps/web/lib/features/daily-plan/add-task-estimation-hours-modal.tsx index 86ce1bf6e..3c8231f2d 100644 --- a/apps/web/lib/features/daily-plan/add-task-estimation-hours-modal.tsx +++ b/apps/web/lib/features/daily-plan/add-task-estimation-hours-modal.tsx @@ -13,7 +13,7 @@ import clsx from 'clsx'; import { AddIcon, ThreeCircleOutlineVerticalIcon } from 'assets/svg'; import { estimatedTotalTime } from '../task/daily-plan'; import { clsxm } from '@app/utils'; -import { formatIntegerToHour } from '@app/helpers'; +import { formatIntegerToHour, formatTimeString } from '@app/helpers'; import { DEFAULT_PLANNED_TASK_ID } from '@app/constants'; import { ActiveTaskHandlerModal } from './active-task-handler-modal'; import { TaskDetailsModal } from './task-details-modal'; @@ -331,11 +331,7 @@ export function AddTasksEstimationHoursModal(props: IAddTasksEstimationHoursModa
{checkPastDate(plan?.date ?? selectedDate) ? (
- {/**Create a space between hours and minutes for past plans view */} - {formatIntegerToHour(plan?.workTimePlanned ?? 0).replace( - /(\d+h)(\d+m)/, - '$1 $2' - )} + {formatTimeString(formatIntegerToHour(tasksEstimationTimes))}
) : ( {t('common.plan.TRACKED_TIME')}
- {formatIntegerToHour(totalWorkedTime ?? 0).replace(/(\d+h)(\d+m)/, '$1 $2')} + {formatTimeString(formatIntegerToHour(totalWorkedTime ?? 0))}
)} @@ -416,20 +412,17 @@ export function AddTasksEstimationHoursModal(props: IAddTasksEstimationHoursModa <> {t('dailyPlan.ESTIMATED')} : - {formatIntegerToHour(tasksEstimationTimes).replace( - /(\d+h)(\d+m)/, - '$1 $2' - )} + {formatTimeString(formatIntegerToHour(tasksEstimationTimes))} - ) : ( + ) : tasksEstimationTimes ? ( <> {t('dailyPlan.TOTAL_ESTIMATED')} : - {formatIntegerToHour(tasksEstimationTimes)} + {formatTimeString(formatIntegerToHour(tasksEstimationTimes))} - )} + ) : null}
From b5c6a5dcc775fe3449c5ccc95b6c6dbed26e51be Mon Sep 17 00:00:00 2001 From: CREDO23 Date: Tue, 8 Oct 2024 18:32:49 +0200 Subject: [PATCH 4/7] change 'require plan to track' to 'enforce plan' --- apps/web/app/[locale]/settings/team/page.tsx | 2 +- apps/web/lib/settings/team-setting-form.tsx | 4 ++-- apps/web/locales/ar.json | 6 ++++-- apps/web/locales/bg.json | 6 ++++-- apps/web/locales/de.json | 6 ++++-- apps/web/locales/en.json | 6 ++++-- apps/web/locales/es.json | 6 ++++-- apps/web/locales/fr.json | 6 ++++-- apps/web/locales/he.json | 6 ++++-- apps/web/locales/it.json | 6 ++++-- apps/web/locales/nl.json | 6 ++++-- apps/web/locales/pl.json | 6 ++++-- apps/web/locales/pt.json | 6 ++++-- apps/web/locales/ru.json | 6 ++++-- apps/web/locales/zh.json | 6 ++++-- 15 files changed, 55 insertions(+), 29 deletions(-) diff --git a/apps/web/app/[locale]/settings/team/page.tsx b/apps/web/app/[locale]/settings/team/page.tsx index a3fbe5d35..6a0072ab8 100644 --- a/apps/web/app/[locale]/settings/team/page.tsx +++ b/apps/web/app/[locale]/settings/team/page.tsx @@ -39,7 +39,7 @@ const Team = () => { <> {/* General Settings */} diff --git a/apps/web/lib/settings/team-setting-form.tsx b/apps/web/lib/settings/team-setting-form.tsx index ad6c10d07..62f26f6e4 100644 --- a/apps/web/lib/settings/team-setting-form.tsx +++ b/apps/web/lib/settings/team-setting-form.tsx @@ -394,7 +394,7 @@ export const TeamSettingForm = () => {
- Share members profile views + {t("pages.settingsTeam.SHARE_MEMBERS_PROFILE_VIEWS")}
@@ -402,7 +402,7 @@ export const TeamSettingForm = () => {
- {t('pages.settingsTeam.REQUIRE_PLAN_TO_TRACK')} + {t('pages.settingsTeam.ENFORCE_PLAN')}
diff --git a/apps/web/locales/ar.json b/apps/web/locales/ar.json index b00789f45..4798a949a 100644 --- a/apps/web/locales/ar.json +++ b/apps/web/locales/ar.json @@ -377,7 +377,7 @@ "TEAM_NAME": "اسم الفريق", "TEAM_TYPE": "نوع الفريق", "TIME_TRACKING": "تتبع الوقت", - "REQUIRE_PLAN_TO_TRACK": "طلب خطة للتتبع", + "ENFORCE_PLAN": "فرض الخطة", "ADD_NEW_MEMBER": "إضافة عضو جديد", "MANAGE_ASSIGNEES": "إدارة المعينين", "SEARCH_MEMBER": "البحث عن عضو", @@ -471,7 +471,9 @@ "ROLES_POSITIONS_CHANGE": "تغيير الأدوار/المناصب", "VIEW_DETAILS": "عرض التفاصيل", "NO_INVITATIONS": "لا توجد دعوات حاليًا!", - "NO_MEMBERS": "لا يوجد أعضاء حاليًا!" + "NO_MEMBERS": "لا يوجد أعضاء حاليًا!", + "GO_TO_PERSONAL_SETTINGS": "الانتقال إلى الإعدادات الشخصية", + "SHARE_MEMBERS_PROFILE_VIEWS": "مشاركة مشاهدات ملفات تعريف الأعضاء" }, "invite": { "HEADING_TITLE": "دعوة عضو لفريقك", diff --git a/apps/web/locales/bg.json b/apps/web/locales/bg.json index 923b4e5e0..789d127df 100644 --- a/apps/web/locales/bg.json +++ b/apps/web/locales/bg.json @@ -377,7 +377,7 @@ "TEAM_NAME": "Име на отбора", "TEAM_TYPE": "Тип отбор", "TIME_TRACKING": "Проследяване на времето", - "REQUIRE_PLAN_TO_TRACK": "Изискване за план за проследяване", + "ENFORCE_PLAN": "Принудете плана", "ADD_NEW_MEMBER": "Добави нов член", "MANAGE_ASSIGNEES": "Управление на отговорници", "SEARCH_MEMBER": "Търсене на член", @@ -471,7 +471,9 @@ "ROLES_POSITIONS_CHANGE": "Промяна на роли/позиции", "VIEW_DETAILS": "Преглед на детайли", "NO_INVITATIONS": "В момента няма покани!", - "NO_MEMBERS": "В момента няма членове!" + "NO_MEMBERS": "В момента няма членове!", + "GO_TO_PERSONAL_SETTINGS": "Отидете в лични настройки", + "SHARE_MEMBERS_PROFILE_VIEWS": "Споделете прегледите на профилите на членовете" }, "invite": { "HEADING_TITLE": "Поканете член в отбора ви", diff --git a/apps/web/locales/de.json b/apps/web/locales/de.json index d6786ffc5..219038a76 100644 --- a/apps/web/locales/de.json +++ b/apps/web/locales/de.json @@ -377,7 +377,7 @@ "TEAM_NAME": "Teamname", "TEAM_TYPE": "Teamtyp", "TIME_TRACKING": "Zeiterfassung", - "REQUIRE_PLAN_TO_TRACK": "Verlangt einen Plan zur Verfolgung", + "ENFORCE_PLAN": "Plan erzwingen", "ADD_NEW_MEMBER": "Neues Mitglied hinzufügen", "MANAGE_ASSIGNEES": "Zugewiesene verwalten", "SEARCH_MEMBER": "Mitglied suchen", @@ -471,7 +471,9 @@ "ROLES_POSITIONS_CHANGE": "Rollen-/Positionsänderung", "VIEW_DETAILS": "Details anzeigen", "NO_INVITATIONS": "Es gibt derzeit keine Einladungen!", - "NO_MEMBERS": "Es gibt derzeit keine Mitglieder!" + "NO_MEMBERS": "Es gibt derzeit keine Mitglieder!", + "GO_TO_PERSONAL_SETTINGS": "Zu den persönlichen Einstellungen gehen", + "SHARE_MEMBERS_PROFILE_VIEWS": "Profilansichten von Mitgliedern teilen" }, "invite": { "HEADING_TITLE": "Mitglied in Ihr Team einladen", diff --git a/apps/web/locales/en.json b/apps/web/locales/en.json index e326b98df..87fdeb8b0 100644 --- a/apps/web/locales/en.json +++ b/apps/web/locales/en.json @@ -377,7 +377,7 @@ "TEAM_NAME": "Team Name", "TEAM_TYPE": "Team Type", "TIME_TRACKING": "Time Tracking", - "REQUIRE_PLAN_TO_TRACK": "Require a Plan to track", + "ENFORCE_PLAN": "Enforce plan", "ADD_NEW_MEMBER": "Add new member", "MANAGE_ASSIGNEES": "Manage Assignees", "SEARCH_MEMBER": "Search Member", @@ -471,7 +471,9 @@ "ROLES_POSITIONS_CHANGE": "Roles/Positions change", "VIEW_DETAILS": "View Details", "NO_INVITATIONS": "There are no Invitations at the moment!", - "NO_MEMBERS": "There are no Members at the moment!" + "NO_MEMBERS": "There are no Members at the moment!", + "GO_TO_PERSONAL_SETTINGS": "Go to Personal settings", + "SHARE_MEMBERS_PROFILE_VIEWS": "Share members profile views" }, "invite": { "HEADING_TITLE": "Invite member to your team", diff --git a/apps/web/locales/es.json b/apps/web/locales/es.json index 5ff10e4d1..62418cacd 100644 --- a/apps/web/locales/es.json +++ b/apps/web/locales/es.json @@ -377,7 +377,7 @@ "TEAM_NAME": "Nombre del equipo", "TEAM_TYPE": "Tipo de equipo", "TIME_TRACKING": "Seguimiento de tiempo", - "REQUIRE_PLAN_TO_TRACK": "Exigir un Plan de seguimiento", + "ENFORCE_PLAN": "Hacer cumplir el plan", "ADD_NEW_MEMBER": "Agregar nuevo miembro", "MANAGE_ASSIGNEES": "Administrar asignados", "SEARCH_MEMBER": "Buscar miembro", @@ -471,7 +471,9 @@ "ROLES_POSITIONS_CHANGE": "Cambios de roles/posiciones", "VIEW_DETAILS": "Ver detalles", "NO_INVITATIONS": "¡No hay invitaciones en este momento!", - "NO_MEMBERS": "¡No hay miembros en este momento!" + "NO_MEMBERS": "¡No hay miembros en este momento!", + "GO_TO_PERSONAL_SETTINGS": "Ir a la configuración personal", + "SHARE_MEMBERS_PROFILE_VIEWS": "Compartir vistas de perfil de miembros" }, "invite": { "HEADING_TITLE": "Invitar miembro a tu equipo", diff --git a/apps/web/locales/fr.json b/apps/web/locales/fr.json index 1b1fce02c..ccb1c09ee 100644 --- a/apps/web/locales/fr.json +++ b/apps/web/locales/fr.json @@ -377,7 +377,7 @@ "TEAM_NAME": "Nom de l'équipe", "TEAM_TYPE": "Type d'équipe", "TIME_TRACKING": "Suivi du temps", - "REQUIRE_PLAN_TO_TRACK": "Exiger un plan pour tracker", + "ENFORCE_PLAN": "Faire respecter le plan", "ADD_NEW_MEMBER": "Ajouter un nouveau membre", "MANAGE_ASSIGNEES": "Gérer les personnes assignées", "SEARCH_MEMBER": "Rechercher un membre", @@ -471,7 +471,9 @@ "ROLES_POSITIONS_CHANGE": "Changer les rôles/positions", "VIEW_DETAILS": "Voir les détails", "NO_INVITATIONS": "Il n'y a pas d'invitation pour le moment !", - "NO_MEMBERS": "Il n'y a pas de membres pour le moment !" + "NO_MEMBERS": "Il n'y a pas de membres pour le moment !", + "GO_TO_PERSONAL_SETTINGS": "Aller aux paramètres personnels", + "SHARE_MEMBERS_PROFILE_VIEWS": "Partager les vues de profil des membres" }, "invite": { "HEADING_TITLE": "Inviter un membre dans votre équipe", diff --git a/apps/web/locales/he.json b/apps/web/locales/he.json index 296206f2c..917fb5005 100644 --- a/apps/web/locales/he.json +++ b/apps/web/locales/he.json @@ -377,7 +377,7 @@ "TEAM_NAME": "שם צוות", "TEAM_TYPE": "סוג צוות", "TIME_TRACKING": "מעקב זמן", - "REQUIRE_PLAN_TO_TRACK": "דרוש תוכנית למעקב", + "ENFORCE_PLAN": "אכוף את התוכנית", "ADD_NEW_MEMBER": "הוסף חבר חדש", "MANAGE_ASSIGNEES": "נהל מוקצים", "SEARCH_MEMBER": "חפש חבר", @@ -471,7 +471,9 @@ "ROLES_POSITIONS_CHANGE": "שינוי תפקידים/תחנות", "VIEW_DETAILS": "צפה בפרטים", "NO_INVITATIONS": "אין הזמנות כרגע!", - "NO_MEMBERS": "אין חברים כרגע!" + "NO_MEMBERS": "אין חברים כרגע!", + "GO_TO_PERSONAL_SETTINGS": "עבור להגדרות האישיות", + "SHARE_MEMBERS_PROFILE_VIEWS": "שתף תצוגות פרופיל של חברים" }, "invite": { "HEADING_TITLE": "הזמן חבר לצוות שלך", diff --git a/apps/web/locales/it.json b/apps/web/locales/it.json index 3fd71133b..b0b0a347c 100644 --- a/apps/web/locales/it.json +++ b/apps/web/locales/it.json @@ -377,7 +377,7 @@ "TEAM_NAME": "Nome del Team", "TEAM_TYPE": "Tipo di Team", "TIME_TRACKING": "Tracciamento del Tempo", - "REQUIRE_PLAN_TO_TRACK": "Richiedere un piano di monitoraggio", + "ENFORCE_PLAN": "Far rispettare il piano", "ADD_NEW_MEMBER": "Aggiungi nuovo membro", "MANAGE_ASSIGNEES": "Gestisci Assegnatari", "SEARCH_MEMBER": "Cerca Membro", @@ -471,7 +471,9 @@ "ROLES_POSITIONS_CHANGE": "Cambio Ruoli/Posizioni", "VIEW_DETAILS": "Visualizza Dettagli", "NO_INVITATIONS": "Al momento non ci sono inviti!", - "NO_MEMBERS": "Al momento non ci sono membri!" + "NO_MEMBERS": "Al momento non ci sono membri!", + "GO_TO_PERSONAL_SETTINGS": "Vai alle impostazioni personali", + "SHARE_MEMBERS_PROFILE_VIEWS": "Condividi visualizzazioni del profilo dei membri" }, "invite": { "HEADING_TITLE": "Invita un membro nel tuo team", diff --git a/apps/web/locales/nl.json b/apps/web/locales/nl.json index 2049eede7..114a35b62 100644 --- a/apps/web/locales/nl.json +++ b/apps/web/locales/nl.json @@ -377,7 +377,7 @@ "TEAM_NAME": "Teamnaam", "TEAM_TYPE": "Teamtype", "TIME_TRACKING": "Tijd bijhouden", - "REQUIRE_PLAN_TO_TRACK": "Vereist een plan om bij te houden", + "ENFORCE_PLAN": "Plan afdwingen", "ADD_NEW_MEMBER": "Nieuw lid toevoegen", "MANAGE_ASSIGNEES": "Toegewezenen beheren", "SEARCH_MEMBER": "Lid zoeken", @@ -471,7 +471,9 @@ "ROLES_POSITIONS_CHANGE": "Rollen/posities wijzigen", "VIEW_DETAILS": "Details bekijken", "NO_INVITATIONS": "Er zijn op dit moment geen uitnodigingen!", - "NO_MEMBERS": "Er zijn op dit moment geen leden!" + "NO_MEMBERS": "Er zijn op dit moment geen leden!", + "GO_TO_PERSONAL_SETTINGS": "Ga naar persoonlijke instellingen", + "SHARE_MEMBERS_PROFILE_VIEWS": "Deel profielweergaven van leden" }, "invite": { "HEADING_TITLE": "Invite member to your team", diff --git a/apps/web/locales/pl.json b/apps/web/locales/pl.json index 9d4c0a5c4..36277840c 100644 --- a/apps/web/locales/pl.json +++ b/apps/web/locales/pl.json @@ -377,7 +377,7 @@ "TEAM_NAME": "Nazwa Zespołu", "TEAM_TYPE": "Typ Zespołu", "TIME_TRACKING": "Śledzenie Czasu", - "REQUIRE_PLAN_TO_TRACK": "Wymagaj planu do śledzenia", + "ENFORCE_PLAN": "Wymuś plan", "ADD_NEW_MEMBER": "Dodaj nowego członka", "MANAGE_ASSIGNEES": "Zarządzaj Przydzielonymi", "SEARCH_MEMBER": "Szukaj Członka", @@ -471,7 +471,9 @@ "ROLES_POSITIONS_CHANGE": "Zmiana Ról/Pozycji", "VIEW_DETAILS": "Pokaż Szczegóły", "NO_INVITATIONS": "Obecnie nie ma żadnych zaproszeń!", - "NO_MEMBERS": "Obecnie nie ma żadnych członków!" + "NO_MEMBERS": "Obecnie nie ma żadnych członków!", + "GO_TO_PERSONAL_SETTINGS": "Przejdź do ustawień osobistych", + "SHARE_MEMBERS_PROFILE_VIEWS": "Udostępnij widoki profili członków" }, "invite": { "HEADING_TITLE": "Zaproś członka do swojego zespołu", diff --git a/apps/web/locales/pt.json b/apps/web/locales/pt.json index 763c20dc7..4cd255cf1 100644 --- a/apps/web/locales/pt.json +++ b/apps/web/locales/pt.json @@ -377,7 +377,7 @@ "TEAM_NAME": "Nome da Equipe", "TEAM_TYPE": "Tipo de Equipe", "TIME_TRACKING": "Rastreamento de Tempo", - "REQUIRE_PLAN_TO_TRACK": "Exigir um plano para rastrear", + "ENFORCE_PLAN": "Fazer cumprir o plano", "ADD_NEW_MEMBER": "Adicionar novo membro", "MANAGE_ASSIGNEES": "Gerenciar Designados", "SEARCH_MEMBER": "Procurar Membro", @@ -471,7 +471,9 @@ "ROLES_POSITIONS_CHANGE": "Alterar Funções/Posições", "VIEW_DETAILS": "Ver Detalhes", "NO_INVITATIONS": "Não há convites no momento!", - "NO_MEMBERS": "Não há membros no momento!" + "NO_MEMBERS": "Não há membros no momento!", + "GO_TO_PERSONAL_SETTINGS": "Ir para configurações pessoais", + "SHARE_MEMBERS_PROFILE_VIEWS": "Compartilhar visualizações de perfil de membros" }, "invite": { "HEADING_TITLE": "Convidar membro para sua equipe", diff --git a/apps/web/locales/ru.json b/apps/web/locales/ru.json index 36ae292c6..7549a8659 100644 --- a/apps/web/locales/ru.json +++ b/apps/web/locales/ru.json @@ -377,7 +377,7 @@ "TEAM_NAME": "Название команды", "TEAM_TYPE": "Тип команды", "TIME_TRACKING": "Отслеживание времени", - "REQUIRE_PLAN_TO_TRACK": "Требовать план для отслеживания", + "ENFORCE_PLAN": "Принудить план", "ADD_NEW_MEMBER": "Добавить нового участника", "MANAGE_ASSIGNEES": "Управление назначенными", "SEARCH_MEMBER": "Поиск участника", @@ -471,7 +471,9 @@ "ROLES_POSITIONS_CHANGE": "Изменение ролей/позиций", "VIEW_DETAILS": "Просмотр деталей", "NO_INVITATIONS": "На данный момент нет приглашений!", - "NO_MEMBERS": "На данный момент нет участников!" + "NO_MEMBERS": "На данный момент нет участников!", + "GO_TO_PERSONAL_SETTINGS": "Перейти в личные настройки", + "SHARE_MEMBERS_PROFILE_VIEWS": "Поделиться просмотрами профилей участников" }, "invite": { "HEADING_TITLE": "Пригласить участника в вашу команду", diff --git a/apps/web/locales/zh.json b/apps/web/locales/zh.json index 5feaa682c..06ea8b227 100644 --- a/apps/web/locales/zh.json +++ b/apps/web/locales/zh.json @@ -377,7 +377,7 @@ "TEAM_NAME": "团队名称", "TEAM_TYPE": "团队类型", "TIME_TRACKING": "时间跟踪", - "REQUIRE_PLAN_TO_TRACK": "需要一个计划来跟踪", + "ENFORCE_PLAN": "强制计划", "ADD_NEW_MEMBER": "添加新成员", "MANAGE_ASSIGNEES": "管理受分配者", "SEARCH_MEMBER": "搜索成员", @@ -471,7 +471,9 @@ "ROLES_POSITIONS_CHANGE": "角色/职位更改", "VIEW_DETAILS": "查看详情", "NO_INVITATIONS": "目前没有邀请!", - "NO_MEMBERS": "目前没有成员!" + "NO_MEMBERS": "目前没有成员!", + "GO_TO_PERSONAL_SETTINGS": "转到个人设置", + "SHARE_MEMBERS_PROFILE_VIEWS": "共享成员资料视图" }, "unauthorized": { "TITLE": "未經授權", From 00db51dc7e0da196800dd50ce50f02f758ea8ad0 Mon Sep 17 00:00:00 2001 From: "Thierry CH." Date: Tue, 8 Oct 2024 19:31:30 +0200 Subject: [PATCH 5/7] 3086 improvement see plan add arrows back forward (#3123) * add arrows back/forward * add arrow navigation handler --- .../features/daily-plan/all-plans-modal.tsx | 47 ++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/apps/web/lib/features/daily-plan/all-plans-modal.tsx b/apps/web/lib/features/daily-plan/all-plans-modal.tsx index ff6f52bed..7ba98059b 100644 --- a/apps/web/lib/features/daily-plan/all-plans-modal.tsx +++ b/apps/web/lib/features/daily-plan/all-plans-modal.tsx @@ -135,6 +135,36 @@ export const AllPlansModal = memo(function AllPlansModal(props: IAllPlansModal) } }, [createDailyPlan, customDate, user?.employee.id, user?.employee.organizationId, user?.tenantId]); + // Handle narrow navigation + const arrowNavigationHandler = useCallback( + async (date: Date) => { + const existPlan = myDailyPlans.items.find((plan) => { + return isSameDate(plan.date.toString().split('T')[0], date.setHours(0, 0, 0, 0)); + }); + + setCustomDate(date); + + if (selectedPlan) { + if (isSameDate(date, moment().startOf('day').toDate())) { + setSelectedTab('Today'); + } else if (isSameDate(date, moment().add(1, 'days').startOf('day').toDate())) { + setSelectedTab('Tomorrow'); + } else { + setSelectedTab('Calendar'); + if (existPlan) { + setShowCalendar(false); + setShowCustomPlan(true); + } else { + setCustomDate(date); + setShowCalendar(true); + setShowCustomPlan(false); + } + } + } + }, + [isSameDate, myDailyPlans.items, selectedPlan] + ); + return (
-
+
    {tabs.map((tab, index) => (
  • ))}
+
+ arrowNavigationHandler(moment(customDate).subtract(1, 'days').toDate())} + className="rotate-180 cursor-pointer px-2 h-full flex items-center justify-center" + > + + + + arrowNavigationHandler(moment(customDate).add(1, 'days').toDate())} + className=" h-full cursor-pointer flex px-2 items-center justify-center" + > + + +
From bbb1aecccdb1bb21f556e3755d1c34d68f179380 Mon Sep 17 00:00:00 2001 From: CREDO23 Date: Tue, 8 Oct 2024 23:30:31 +0200 Subject: [PATCH 6/7] add enforce plan flow --- .../features/useStartStopTimerHandler.ts | 45 +++++++++++++---- .../components/shared/timer/timer-card.tsx | 1 + .../add-task-estimation-hours-modal.tsx | 10 +++- .../daily-plan/enforce-planed-task-modal.tsx | 50 ++++++++++++++++--- apps/web/lib/features/task/task-card.tsx | 1 + apps/web/lib/features/timer/timer.tsx | 2 + 6 files changed, 90 insertions(+), 19 deletions(-) diff --git a/apps/web/app/hooks/features/useStartStopTimerHandler.ts b/apps/web/app/hooks/features/useStartStopTimerHandler.ts index 449f888fb..6bebbfca6 100644 --- a/apps/web/app/hooks/features/useStartStopTimerHandler.ts +++ b/apps/web/app/hooks/features/useStartStopTimerHandler.ts @@ -7,6 +7,7 @@ import { TASKS_ESTIMATE_HOURS_MODAL_DATE, DAILY_PLAN_ESTIMATE_HOURS_MODAL_DATE } from '@app/constants'; +import { estimatedTotalTime } from 'lib/features/task/daily-plan'; export function useStartStopTimerHandler() { const { @@ -60,6 +61,11 @@ export function useStartStopTimerHandler() { [activeTeamTask?.id, hasPlan?.tasks] ); + const tasksEstimationTimes = useMemo( + () => (hasPlan && hasPlan.tasks ? estimatedTotalTime(hasPlan.tasks).timesEstimated / 3600 : 0), + [hasPlan] + ); + const enforceTaskSoftCloseModal = () => { _enforceTaskSoftCloseModal(); openAddTasksEstimationHoursModal(); @@ -82,13 +88,13 @@ export function useStartStopTimerHandler() { if (tasksEstimateHoursModalDate != currentDate) { openAddTasksEstimationHoursModal(); } else { - startTimer(); + handleWarnings(); } } else { openEnforcePlannedTaskSoftModal(); } } else { - startTimer(); + handleWarnings(); } }; @@ -100,10 +106,10 @@ export function useStartStopTimerHandler() { if (!hasWorkedHours) { openAddDailyPlanWorkHoursModal(); } else { - startTimer(); + handleWarnings(); } } else { - startTimer(); + handleWarnings(); } }; @@ -118,15 +124,32 @@ export function useStartStopTimerHandler() { if (dailyPlanEstimateHoursModalDate != currentDate) { handleMissingDailyPlanWorkHour(); } else { - startTimer(); + handleWarnings(); } } else { if (tasksEstimateHoursModalDate != currentDate) { openAddTasksEstimationHoursModal(); } else { - startTimer(); + handleWarnings(); } } + } else { + handleWarnings(); + } + }; + + /** + * Check if there is warning for 'enforce' mode. If not, + * start tracking + */ + const handleWarnings = () => { + if ( + requirePlan && + (!areAllTasksEstimated || + !hasWorkedHours || + Math.abs(Number(hasPlan?.workTimePlanned) - tasksEstimationTimes) > 1) + ) { + openAddTasksEstimationHoursModal(); } else { startTimer(); } @@ -147,7 +170,7 @@ export function useStartStopTimerHandler() { tasksEstimateHoursModalDate == currentDate && dailyPlanEstimateHoursModalDate == currentDate ) { - startTimer(); + handleWarnings(); } else { if (dailyPlanSuggestionModalDate != currentDate) { if (!hasPlan) { @@ -162,14 +185,13 @@ export function useStartStopTimerHandler() { if (areAllTasksEstimated) { handleMissingDailyPlanWorkHour(); } else { - startTimer(); + handleWarnings(); } } else { - startTimer(); + handleWarnings(); } } else { - // Default action to start the timer - startTimer(); + handleWarnings(); } } } @@ -187,6 +209,7 @@ export function useStartStopTimerHandler() { requirePlan, startTimer, stopTimer, + tasksEstimationTimes, timerStatus?.running, timerStatusFetching ]); diff --git a/apps/web/components/shared/timer/timer-card.tsx b/apps/web/components/shared/timer/timer-card.tsx index 797408b31..b2c3a840a 100644 --- a/apps/web/components/shared/timer/timer-card.tsx +++ b/apps/web/components/shared/timer/timer-card.tsx @@ -99,6 +99,7 @@ const Timer = () => { plan={hasPlan} open={modals.isEnforceTaskModalOpen} task={activeTeamTask} + openDailyPlanModal={modals.openAddTasksEstimationHoursModal} /> )} diff --git a/apps/web/lib/features/daily-plan/add-task-estimation-hours-modal.tsx b/apps/web/lib/features/daily-plan/add-task-estimation-hours-modal.tsx index 3c8231f2d..9df79c1c2 100644 --- a/apps/web/lib/features/daily-plan/add-task-estimation-hours-modal.tsx +++ b/apps/web/lib/features/daily-plan/add-task-estimation-hours-modal.tsx @@ -454,7 +454,15 @@ export function AddTasksEstimationHoursModal(props: IAddTasksEstimationHoursModa
diff --git a/apps/web/lib/features/task/task-card.tsx b/apps/web/lib/features/task/task-card.tsx index 03abe112c..bf9a212c8 100644 --- a/apps/web/lib/features/task/task-card.tsx +++ b/apps/web/lib/features/task/task-card.tsx @@ -534,6 +534,7 @@ function TimerButtonCall({ plan={hasPlan} open={modals.isEnforceTaskModalOpen} task={activeTeamTask} + openDailyPlanModal={modals.openAddTasksEstimationHoursModal} /> )} diff --git a/apps/web/lib/features/timer/timer.tsx b/apps/web/lib/features/timer/timer.tsx index 7ec6e8c5b..e4aced347 100644 --- a/apps/web/lib/features/timer/timer.tsx +++ b/apps/web/lib/features/timer/timer.tsx @@ -188,6 +188,7 @@ export function Timer({ className }: IClassName) { plan={hasPlan} open={modals.isEnforceTaskModalOpen} task={activeTeamTask} + openDailyPlanModal={modals.openAddTasksEstimationHoursModal} /> )}
@@ -287,6 +288,7 @@ export function MinTimerFrame({ className }: IClassName) { plan={hasPlan} open={modals.isEnforceTaskModalOpen} task={activeTeamTask} + openDailyPlanModal={modals.openAddTasksEstimationHoursModal} /> )}
From e6aa44db0cc952e0cbb12d0bc6a05420cb2125e1 Mon Sep 17 00:00:00 2001 From: "Thierry CH." Date: Wed, 9 Oct 2024 20:41:15 +0200 Subject: [PATCH 7/7] feat: add actions on kanban | task action menu (#3136) * feat: add actions on kanban | task action menu * feat: add missing action menus --- .../pages/kanban/menu-kanban-card.tsx | 410 +++++++++++++----- apps/web/locales/ar.json | 8 +- apps/web/locales/bg.json | 8 +- apps/web/locales/de.json | 8 +- apps/web/locales/en.json | 8 +- apps/web/locales/es.json | 8 +- apps/web/locales/fr.json | 8 +- apps/web/locales/he.json | 8 +- apps/web/locales/it.json | 8 +- apps/web/locales/nl.json | 8 +- apps/web/locales/pl.json | 8 +- apps/web/locales/pt.json | 8 +- apps/web/locales/ru.json | 8 +- apps/web/locales/zh.json | 8 +- 14 files changed, 382 insertions(+), 132 deletions(-) diff --git a/apps/web/components/pages/kanban/menu-kanban-card.tsx b/apps/web/components/pages/kanban/menu-kanban-card.tsx index 0af7105bb..457433d6b 100644 --- a/apps/web/components/pages/kanban/menu-kanban-card.tsx +++ b/apps/web/components/pages/kanban/menu-kanban-card.tsx @@ -1,128 +1,300 @@ -import { useTeamMemberCard } from '@app/hooks'; +import { useOrganizationTeams, useTaskStatus, useTeamMemberCard, useTeamTasks } from '@app/hooks'; import { activeTeamTaskId } from '@app/stores'; -import { - Popover, - PopoverContent, - PopoverTrigger -} from '@components/ui/popover'; +import { Popover, PopoverContent, PopoverTrigger } from '@components/ui/popover'; import { ThreeCircleOutlineVerticalIcon } from 'assets/svg'; import { HorizontalSeparator, SpinnerLoader } from 'lib/components'; import { PlanTask } from 'lib/features/task/task-card'; import { useTranslations } from 'next-intl'; -import { useState } from 'react'; import { useSetAtom } from 'jotai'; +import { ITeamTask, OT_Member } from '@app/interfaces'; +import { Combobox, Transition } from '@headlessui/react'; +import { Fragment, useCallback } from 'react'; +import { CheckIcon, ChevronUpDownIcon } from '@heroicons/react/20/solid'; -export default function MenuKanbanCard({ - member, - item -}: { - item: any; - member: any; -}) { - const t = useTranslations(); - const { - assignTask, - unassignTask, - assignTaskLoading, - unAssignTaskLoading - } = useTeamMemberCard(member); - const setActiveTask = useSetAtom(activeTeamTaskId); - const [load, setLoad] = useState<'' | 'assign' | 'unassign'>(''); - const menu = [ - { - name: t('common.EDIT_TASK'), - closable: true, - action: 'edit', - active: true, - onClick: () => { - setActiveTask({ - id: item.id - }); - } - }, - { - name: t('common.ESTIMATE'), - closable: true, - action: 'estimate', - onClick: () => { - // TODO: Implement estimate task after fixing the time estimate issue - }, - active: true - }, - { - name: t('common.ASSIGN_TASK'), - action: 'assign', - active: true, - onClick: () => { - setLoad('assign'); - assignTask(item); - } - }, - { - name: t('common.UNASSIGN_TASK'), - action: 'unassign', - closable: true, - active: true, - onClick: () => { - setLoad('unassign'); - unassignTask(item); - } - } - ].filter((item) => item.active || item.active === undefined); +export default function MenuKanbanCard({ item: task, member }: { item: ITeamTask; member: any }) { + const t = useTranslations(); + const setActiveTask = useSetAtom(activeTeamTaskId); + const { createTask, createLoading } = useTeamTasks(); + const { assignTask, unassignTask, assignTaskLoading, unAssignTaskLoading } = useTeamMemberCard(member); + const { taskStatus } = useTaskStatus(); + const { activeTeam } = useOrganizationTeams(); + const menu = [ + { + name: t('common.EDIT_TASK'), + closable: true, + action: 'edit', + active: true, + onClick: () => { + setActiveTask({ + id: task.id + }); + } + }, + { + name: t('common.ESTIMATE'), + closable: true, + action: 'estimate', + onClick: () => { + // TODO: Implement estimate task after fixing the time estimate issue + }, + active: true + }, + { + name: t('common.ASSIGN_TASK'), + action: 'assign', + active: true, + onClick: () => { + assignTask(task); + }, + loading: assignTaskLoading + }, + { + name: t('common.UNASSIGN_TASK'), + action: 'unassign', + closable: true, + active: true, + onClick: () => { + unassignTask(task); + }, + loading: unAssignTaskLoading + }, + { + name: t('common.COPY_ISSUE_LINK'), + closable: true, + action: 'copy_issue_link', + active: true, + onClick: async () => { + try { + const baseUrl = window?.location.origin; + await navigator.clipboard.writeText(`${baseUrl}/task/${task.id}`); + } catch (error) { + console.log(error); + } + } + }, + { + name: t('common.MAKE_A_COPY'), + closable: true, + action: 'male_a_copy', + active: true, + onClick: async () => { + try { + await createTask({ + ...task, + taskStatusId: task.taskStatusId ?? taskStatus[0].id, + taskName: `Copy ${task.title}`, + issueType: task.issueType ?? 'Bug' + }); + } catch (error) { + console.log(error); + } + }, + loading: createLoading + }, + { + name: t('common.ASSIGNEE'), + closable: true, + action: 'assignee', + active: true + }, + { + name: t('common.CHANGE_PARENT'), + closable: true, + action: 'change_parent', + onClick: () => { + // TODO: Implement the logic here + }, + active: true + }, + { + name: t('common.CHANGE_RELATIONS'), + closable: true, + action: 'change_relations', + onClick: () => { + // TODO: Implement the logic here + }, + active: true + }, + { + name: t('common.SET_AS_NEXT'), + closable: true, + action: 'set_as_next', + onClick: () => { + // TODO: Implement the logic here + }, + active: true + }, + { + name: t('common.MOVE_TO'), + closable: true, + action: 'move_to', + onClick: () => { + // TODO: Implement the logic here + }, + active: true + } + ].filter((item) => item.active || item.active === undefined); - return ( - - - - - -
    - {menu.map((item) => { - return ( -
  • item?.onClick()}> - -
  • - ); - })} -
- -
-
    -
  • - -
  • -
  • - -
  • -
  • - -
  • -
-
-
-
- ); + return ( + + + + + +
    + {menu.map((item) => { + return ( +
  • await item?.onClick?.()}> + {item.action == 'assignee' ? ( +
    + +
    + ) : ( + + )} +
  • + ); + })} +
+ +
    +
  • + +
  • +
  • + +
  • +
  • + +
  • +
+
+
+ ); +} + +/** + * -------------------------------------------------- + * --------- TEAM MEMBER SELECT ----------- + * -------------------------------------------------- + */ + +interface ITeamMemberSelectProps { + teamMembers: OT_Member[]; + task: ITeamTask; +} + +/** + * A multi select component that allows to assign members to a task + * + * @param {object} props - The props object + * @param {OT_Member[]} props.teamMembers - Members of the current team + * @param {ITeamTask} props.task - The task + * + * @return {JSX.Element} The multi select component + */ +function TeamMembersSelect(props: ITeamMemberSelectProps): JSX.Element { + const { teamMembers, task } = props; + const t = useTranslations(); + + return ( +
+ +
+
+ + + {t('common.ASSIGNEE')} + +
+ + + {teamMembers.map((member) => ( + + `relative cursor-default select-none py-2 pl-10 pr-4 ${ + active ? 'bg-primary/5' : 'text-gray-900' + }` + } + value={member} + > + el.user?.id == member.employee.user?.id)} + /> + + ))} + + +
+
+
+ ); +} + +/** + * -------------------------------------------------- + * ------- TEAM MEMBER LIST OPTION -------- + * -------------------------------------------------- + */ + +interface ITeamMemberOptionProps { + isAssignee: boolean; + member: OT_Member; + task: ITeamTask; +} +/** + * The team member list option (allow to handle member specific actions) + * + * @param {object} props - The props object + * @param {boolean} props.isAssignee - true if the task/issue is assigned to the member + * @param {OT_Member} props.member - The team member + * @param {ITeamTask} props.task - The task + * + * @returns {JSX.Element} - The list option + */ +function TeamMemberOption({ isAssignee, member, task }: ITeamMemberOptionProps): JSX.Element { + const { assignTask, unassignTask, assignTaskLoading, unAssignTaskLoading } = useTeamMemberCard(member); + + // Assign or unassign the task + const handleAssignTask = useCallback(() => { + if (isAssignee) { + unassignTask(task); + } else { + assignTask(task); + } + }, [assignTask, isAssignee, task, unassignTask]); + + return ( +
+ {member.employee.fullName} + {!(assignTaskLoading || unAssignTaskLoading) && isAssignee ? ( + + + ) : null} + + {(assignTaskLoading || unAssignTaskLoading) && ( + + + + )} +
+ ); } diff --git a/apps/web/locales/ar.json b/apps/web/locales/ar.json index 4798a949a..8d125389b 100644 --- a/apps/web/locales/ar.json +++ b/apps/web/locales/ar.json @@ -222,7 +222,13 @@ "timesheets": { "SINGULAR": "ورقة الحضور", "PLURAL": "ورقات الحضور" - } + }, + "COPY_ISSUE_LINK": "نسخ رابط المشكلة", + "MAKE_A_COPY": "إنشاء نسخة", + "ASSIGNEE": "المسند إليه", + "CHANGE_RELATIONS": "تغيير العلاقات", + "SET_AS_NEXT": "تعيين كالتالي", + "MOVE_TO": "نقل إلى" }, "hotkeys": { "HELP": "مساعدة", diff --git a/apps/web/locales/bg.json b/apps/web/locales/bg.json index 789d127df..3f8b6fe8c 100644 --- a/apps/web/locales/bg.json +++ b/apps/web/locales/bg.json @@ -222,7 +222,13 @@ "timesheets": { "SINGULAR": "Работен лист", "PLURAL": "Работни листове" - } + }, + "COPY_ISSUE_LINK": "Копирай връзката на проблема", + "MAKE_A_COPY": "Направете копие", + "ASSIGNEE": "Назначен", + "CHANGE_RELATIONS": "Променете отношенията", + "SET_AS_NEXT": "Задайте като следващо", + "MOVE_TO": "Преместете в" }, "hotkeys": { "HELP": "Помощ", diff --git a/apps/web/locales/de.json b/apps/web/locales/de.json index 219038a76..133934101 100644 --- a/apps/web/locales/de.json +++ b/apps/web/locales/de.json @@ -222,7 +222,13 @@ "timesheets": { "SINGULAR": "Stundenzettel", "PLURAL": "Stundenzettel" - } + }, + "COPY_ISSUE_LINK": "Problemlink kopieren", + "MAKE_A_COPY": "Eine Kopie erstellen", + "ASSIGNEE": "Zessionar", + "CHANGE_RELATIONS": "Beziehungen ändern", + "SET_AS_NEXT": "Als nächstes festlegen", + "MOVE_TO": "Verschieben nach" }, "hotkeys": { "HELP": "Hilfe", diff --git a/apps/web/locales/en.json b/apps/web/locales/en.json index 87fdeb8b0..4692276dc 100644 --- a/apps/web/locales/en.json +++ b/apps/web/locales/en.json @@ -222,7 +222,13 @@ "timesheets": { "SINGULAR": "Timesheet", "PLURAL": "Timesheets" - } + }, + "COPY_ISSUE_LINK": "Copy issue link", + "MAKE_A_COPY": "Make a copy", + "ASSIGNEE": "Assignee", + "CHANGE_RELATIONS": "Change relations", + "SET_AS_NEXT": "Set as next", + "MOVE_TO": "Move to" }, "hotkeys": { "HELP": "Help", diff --git a/apps/web/locales/es.json b/apps/web/locales/es.json index 62418cacd..2bfadb58b 100644 --- a/apps/web/locales/es.json +++ b/apps/web/locales/es.json @@ -222,7 +222,13 @@ "timesheets": { "SINGULAR": "Hoja de horas", "PLURAL": "Hojas de horas" - } + }, + "COPY_ISSUE_LINK": "Copiar enlace del problema", + "MAKE_A_COPY": "Hacer una copia", + "ASSIGNEE": "Cesionario", + "CHANGE_RELATIONS": "Cambiar relaciones", + "SET_AS_NEXT": "Establecer como siguiente", + "MOVE_TO": "Mover a" }, "hotkeys": { "HELP": "Ayuda", diff --git a/apps/web/locales/fr.json b/apps/web/locales/fr.json index ccb1c09ee..32440addd 100644 --- a/apps/web/locales/fr.json +++ b/apps/web/locales/fr.json @@ -222,7 +222,13 @@ "timesheets": { "SINGULAR": "Feuille de temps", "PLURAL": "Feuilles de temps" - } + }, + "COPY_ISSUE_LINK": "Copier le lien du problème", + "MAKE_A_COPY": "Faire une copie", + "ASSIGNEE": "Cessionnaire", + "CHANGE_RELATIONS": "Changer les relations", + "SET_AS_NEXT": "Définir comme suivant", + "MOVE_TO": "Déplacer vers" }, "hotkeys": { "HELP": "Aide", diff --git a/apps/web/locales/he.json b/apps/web/locales/he.json index 917fb5005..5d9df1e15 100644 --- a/apps/web/locales/he.json +++ b/apps/web/locales/he.json @@ -222,7 +222,13 @@ "timesheets": { "SINGULAR": "דוח שעות", "PLURAL": "דוחות שעות" - } + }, + "COPY_ISSUE_LINK": "העתק קישור לבעיה", + "MAKE_A_COPY": "בצע עותק", + "ASSIGNEE": "נמען", + "CHANGE_RELATIONS": "שנה יחסים", + "SET_AS_NEXT": "הגדר כהבא", + "MOVE_TO": "העבר אל" }, "hotkeys": { "HELP": "עזרה", diff --git a/apps/web/locales/it.json b/apps/web/locales/it.json index b0b0a347c..bd11fbd71 100644 --- a/apps/web/locales/it.json +++ b/apps/web/locales/it.json @@ -222,7 +222,13 @@ "TRACKED_TIME": "Tempo tracciato", "SEE_PLANS": "Vedi piani", "ADD_PLAN": "Aggiungi Piano" - } + }, + "COPY_ISSUE_LINK": "Copia collegamento problema", + "MAKE_A_COPY": "Fai una copia", + "ASSIGNEE": "Assegnatario", + "CHANGE_RELATIONS": "Cambia relazioni", + "SET_AS_NEXT": "Imposta come successivo", + "MOVE_TO": "Sposta a" }, "hotkeys": { "HELP": "Aiuto", diff --git a/apps/web/locales/nl.json b/apps/web/locales/nl.json index 114a35b62..c59d6f29d 100644 --- a/apps/web/locales/nl.json +++ b/apps/web/locales/nl.json @@ -222,7 +222,13 @@ "TRACKED_TIME": "Gegeneraliseerde tijd", "SEE_PLANS": "Plannen bekijken", "ADD_PLAN": "Plan toevoegen" - } + }, + "COPY_ISSUE_LINK": "Probleemlink kopiëren", + "MAKE_A_COPY": "Maak een kopie", + "ASSIGNEE": "Cessionaris", + "CHANGE_RELATIONS": "Verander relaties", + "SET_AS_NEXT": "Instellen als volgende", + "MOVE_TO": "Verplaatsen naar" }, "hotkeys": { "HELP": "Help", diff --git a/apps/web/locales/pl.json b/apps/web/locales/pl.json index 36277840c..013652426 100644 --- a/apps/web/locales/pl.json +++ b/apps/web/locales/pl.json @@ -222,7 +222,13 @@ "TRACKED_TIME": "Śledzony czas", "SEE_PLANS": "Zobacz plany", "ADD_PLAN": "Dodaj plan" - } + }, + "COPY_ISSUE_LINK": "Skopiuj link do zgłoszenia", + "MAKE_A_COPY": "Zrób kopię", + "ASSIGNEE": "Cesjonariusz", + "CHANGE_RELATIONS": "Zmień relacje", + "SET_AS_NEXT": "Ustaw jako następny", + "MOVE_TO": "Przenieś do" }, "hotkeys": { "HELP": "Pomoc", diff --git a/apps/web/locales/pt.json b/apps/web/locales/pt.json index 4cd255cf1..a21389cf1 100644 --- a/apps/web/locales/pt.json +++ b/apps/web/locales/pt.json @@ -222,7 +222,13 @@ "TRACKED_TIME": "Tempo rastreado", "SEE_PLANS": "Ver planos", "ADD_PLAN": "Adicionar Plano" - } + }, + "COPY_ISSUE_LINK": "Copiar link do problema", + "MAKE_A_COPY": "Fazer uma cópia", + "ASSIGNEE": "Cessionário", + "CHANGE_RELATIONS": "Alterar relações", + "SET_AS_NEXT": "Definir como o próximo", + "MOVE_TO": "Mover para" }, "hotkeys": { "HELP": "Ajuda", diff --git a/apps/web/locales/ru.json b/apps/web/locales/ru.json index 7549a8659..7c58dd7a6 100644 --- a/apps/web/locales/ru.json +++ b/apps/web/locales/ru.json @@ -222,7 +222,13 @@ "TRACKED_TIME": "Отслеживаемое время", "SEE_PLANS": "Посмотреть планы", "ADD_PLAN": "Agregar Plan" - } + }, + "COPY_ISSUE_LINK": "Скопировать ссылку на проблему", + "MAKE_A_COPY": "Сделать копию", + "ASSIGNEE": "Исполнитель", + "CHANGE_RELATIONS": "Изменить отношения", + "SET_AS_NEXT": "Установить следующим", + "MOVE_TO": "Переместить в" }, "hotkeys": { "HELP": "Помощь", diff --git a/apps/web/locales/zh.json b/apps/web/locales/zh.json index 06ea8b227..5e8860b36 100644 --- a/apps/web/locales/zh.json +++ b/apps/web/locales/zh.json @@ -222,7 +222,13 @@ "TRACKED_TIME": "跟踪时间", "SEE_PLANS": "查看计划", "ADD_PLAN": "添加计划" - } + }, + "COPY_ISSUE_LINK": "复制问题链接", + "MAKE_A_COPY": "制作副本", + "ASSIGNEE": "受让人", + "CHANGE_RELATIONS": "更改关系", + "SET_AS_NEXT": "设置为下一个", + "MOVE_TO": "移动到" }, "hotkeys": { "HELP": "帮助",