diff --git a/apps/web/app/[locale]/page-component.tsx b/apps/web/app/[locale]/page-component.tsx
index f0ee73e76..03bc54d81 100644
--- a/apps/web/app/[locale]/page-component.tsx
+++ b/apps/web/app/[locale]/page-component.tsx
@@ -52,9 +52,6 @@ function MainPage() {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [path, setView]);
-
-
-
React.useEffect(() => {
window && window?.localStorage.getItem('conf-fullWidth-mode');
setFullWidth(JSON.parse(window?.localStorage.getItem('conf-fullWidth-mode') || 'true'));
@@ -65,7 +62,6 @@ function MainPage() {
}
return (
<>
-
{/*
*/}
diff --git a/apps/web/app/constants.ts b/apps/web/app/constants.ts
index d58c076eb..ca1b52578 100644
--- a/apps/web/app/constants.ts
+++ b/apps/web/app/constants.ts
@@ -2,8 +2,8 @@ import { JitsuOptions } from '@jitsu/jitsu-react/dist/useJitsu';
import { I_SMTPRequest } from './interfaces/ISmtp';
import { getNextPublicEnv } from './env';
import enLanguage from '../locales/en.json';
-// import { } from 'country-flag-icons/react/3x2'
import { BG, CN, DE, ES, FR, IS, IT, NL, PL, PT, RU, SA, US } from 'country-flag-icons/react/1x1';
+import { ManualTimeReasons } from './interfaces/timer/IManualTimeReasons';
export const API_BASE_URL = '/api';
export const DEFAULT_APP_PATH = '/auth/passcode';
export const DEFAULT_MAIN_PATH = '/';
@@ -298,3 +298,14 @@ export const SLACK_CLIENT_SECRET = process.env.SLACK_CLIENT_SECRET;
export const TWITTER_CLIENT_ID = process.env.TWITTER_CLIENT_ID;
export const TWITTER_CLIENT_SECRET = process.env.TWITTER_CLIENT_SECRET;
+
+// Add manual timer reason
+
+export const manualTimeReasons: ManualTimeReasons[] = [
+ 'LOST_ELECTRICITY',
+ 'LOST_INTERNET',
+ 'FORGOT_TO_START_TIMER',
+ 'ERROR',
+ 'UNPLANNED_WORK',
+ 'TESTED_TIMER'
+];
diff --git a/apps/web/app/interfaces/timer/IManualTimeReasons.ts b/apps/web/app/interfaces/timer/IManualTimeReasons.ts
new file mode 100644
index 000000000..0567ccb8c
--- /dev/null
+++ b/apps/web/app/interfaces/timer/IManualTimeReasons.ts
@@ -0,0 +1,7 @@
+export type ManualTimeReasons =
+ | 'LOST_ELECTRICITY'
+ | 'LOST_INTERNET'
+ | 'FORGOT_TO_START_TIMER'
+ | 'ERROR'
+ | 'UNPLANNED_WORK'
+ | 'TESTED_TIMER';
diff --git a/apps/web/lib/features/manual-time/add-manual-time-modal.tsx b/apps/web/lib/features/manual-time/add-manual-time-modal.tsx
new file mode 100644
index 000000000..61b3271d8
--- /dev/null
+++ b/apps/web/lib/features/manual-time/add-manual-time-modal.tsx
@@ -0,0 +1,290 @@
+import '../../../styles/style.css';
+import { useOrganizationTeams, useTeamTasks } from '@app/hooks';
+import api from '@app/services/client/axios';
+import { clsxm } from '@app/utils';
+import { DatePicker } from '@components/ui/DatePicker';
+import { PencilSquareIcon } from '@heroicons/react/20/solid';
+import { format } from 'date-fns';
+import { useState, useEffect, FormEvent, useCallback } from 'react';
+import { Button, SelectItems, Modal } from 'lib/components';
+import { FaRegCalendarAlt } from 'react-icons/fa';
+import { HiMiniClock } from 'react-icons/hi2';
+import { manualTimeReasons } from '@app/constants';
+import { useTranslations } from 'next-intl';
+
+interface IAddManualTimeModalProps {
+ isOpen: boolean;
+ closeModal: () => void;
+}
+
+export function AddManualTimeModal(props: IAddManualTimeModalProps) {
+ const { closeModal, isOpen } = props;
+ const t = useTranslations();
+ const [isBillable, setIsBillable] = useState(false);
+ const [description, setDescription] = useState('');
+ const [reason, setReason] = useState('');
+ const [errorMsg, setError] = useState('');
+ const [loading, setLoading] = useState(false);
+ const [endTime, setEndTime] = useState('');
+ const [date, setDate] = useState(new Date());
+ const [startTime, setStartTime] = useState('');
+ const [teamId, setTeamId] = useState('');
+ const [taskId, setTaskId] = useState('');
+ const [timeDifference, setTimeDifference] = useState('');
+ const { activeTeamTask, tasks, activeTeamId } = useTeamTasks();
+ const { teams } = useOrganizationTeams();
+
+ useEffect(() => {
+ const now = new Date();
+ const currentTime = now.toTimeString().slice(0, 5);
+
+ setDate(now);
+ setStartTime(currentTime);
+ setEndTime(currentTime);
+ }, []);
+
+ const handleSubmit = useCallback(
+ (e: FormEvent) => {
+ e.preventDefault();
+
+ const timeObject = {
+ date,
+ isBillable,
+ startTime,
+ endTime,
+ teamId,
+ taskId,
+ description,
+ reason,
+ timeDifference
+ };
+
+ if (date && startTime && endTime && teamId && taskId) {
+ setLoading(true);
+ setError('');
+ const postData = async () => {
+ try {
+ const response = await api.post('/add_time', timeObject);
+ if (response.data.message) {
+ setLoading(false);
+ closeModal();
+ }
+ } catch (err) {
+ setError('Failed to post data');
+ setLoading(false);
+ }
+ };
+
+ postData();
+ } else {
+ setError(`Please complete all required fields with a ${'*'}`);
+ }
+ },
+ [closeModal, date, description, endTime, isBillable, reason, startTime, taskId, teamId, timeDifference]
+ );
+
+ const calculateTimeDifference = useCallback(() => {
+ const [startHours, startMinutes] = startTime.split(':').map(Number);
+ const [endHours, endMinutes] = endTime.split(':').map(Number);
+
+ const startTotalMinutes = startHours * 60 + startMinutes;
+ const endTotalMinutes = endHours * 60 + endMinutes;
+
+ const diffMinutes = endTotalMinutes - startTotalMinutes;
+ if (diffMinutes < 0) {
+ return;
+ }
+
+ const hours = Math.floor(diffMinutes / 60);
+ const minutes = diffMinutes % 60;
+ setTimeDifference(`${String(hours).padStart(2, '0')}h ${String(minutes).padStart(2, '0')}m`);
+ }, [endTime, startTime]);
+
+ useEffect(() => {
+ calculateTimeDifference();
+ }, [calculateTimeDifference, endTime, startTime]);
+
+ useEffect(() => {
+ if (activeTeamTask) {
+ setTaskId(activeTeamTask.id);
+ }
+ if (activeTeamId) {
+ setTeamId(activeTeamId);
+ }
+ }, [activeTeamTask, activeTeamId]);
+
+ return (
+
+
+
+
+
+
setIsBillable(!isBillable)}
+ style={
+ isBillable
+ ? { background: 'linear-gradient(to right, #9d91efb7, #8a7bedb7)' }
+ : { background: '#6c57f4b7' }
+ }
+ >
+
+
+
+
+
+
+
+
+
+
+
+ {timeDifference}
+
+
+
+
+
+ setTeamId(value)}
+ itemId={(team) => team.id}
+ itemToString={(team) => team.name}
+ triggerClassName="border-slate-100 dark:border-slate-600"
+ />
+
+
+
+
+ setTaskId(value)}
+ itemId={(task) => task.id}
+ itemToString={(task) => task.title}
+ triggerClassName="border-slate-100 dark:border-slate-600"
+ />
+
+
+
+
+
+
+
+
+ t(`manualTime.reasons.${reason}`))}
+ onValueChange={(reason) => setReason(reason)}
+ itemId={(reason) => reason}
+ itemToString={(reason) => reason}
+ triggerClassName="border-slate-100 dark:border-slate-600"
+ />
+
+
+
+
+
+
+
{errorMsg}
+
+
+ );
+}
diff --git a/apps/web/lib/features/task/task-filters.tsx b/apps/web/lib/features/task/task-filters.tsx
index 05751ed5d..ff6c8f95d 100644
--- a/apps/web/lib/features/task/task-filters.tsx
+++ b/apps/web/lib/features/task/task-filters.tsx
@@ -7,33 +7,26 @@ import {
useDailyPlan,
useOrganizationTeams,
useOutsideClick,
- useModal,
- useTeamTasks,
+ useModal
} from '@app/hooks';
import { IClassName, ITeamTask } from '@app/interfaces';
import { clsxm } from '@app/utils';
import { Transition } from '@headlessui/react';
-import { Button, InputField, SelectItems, Tooltip, VerticalSeparator } from 'lib/components';
+import { Button, InputField, Tooltip, VerticalSeparator } from 'lib/components';
import { SearchNormalIcon } from 'assets/svg';
import intersection from 'lodash/intersection';
-import { useCallback, useEffect, useMemo, useState, FormEvent } from 'react';
+import { useCallback, useEffect, useMemo, useState } from 'react';
import { TaskUnOrAssignPopover } from './task-assign-popover';
import { TaskLabelsDropdown, TaskPropertiesDropdown, TaskSizesDropdown, TaskStatusDropdown } from './task-status';
import { useTranslations } from 'next-intl';
import { SettingFilterIcon } from 'assets/svg';
import { DailyPlanFilter } from './daily-plan/daily-plan-filter';
-import { Modal, Divider } from 'lib/components';
-import api from '@app/services/client/axios';
-import { DatePicker } from '@components/ui/DatePicker';
-import { PencilSquareIcon } from '@heroicons/react/20/solid';
-import { FaRegCalendarAlt } from "react-icons/fa";
+import { Divider } from 'lib/components';
+
import { useDateRange } from '@app/hooks/useDateRange';
import { TaskDatePickerWithRange } from './task-date-range';
-import { format } from 'date-fns';
-import { HiMiniClock } from "react-icons/hi2";
-import '../../../styles/style.css'
-import { TaskIssueStatus } from './task-issue';
-
+import '../../../styles/style.css';
+import { AddManualTimeModal } from '../manual-time/add-manual-time-modal';
export type ITab = 'worked' | 'assigned' | 'unassigned' | 'dailyplan';
@@ -189,9 +182,9 @@ export function useTaskFilter(profile: I_UserProfilePage) {
.every((k) => {
return k === 'label'
? intersection(
- statusFilters[k],
- task['tags'].map((item) => item.name)
- ).length === statusFilters[k].length
+ statusFilters[k],
+ task['tags'].map((item) => item.name)
+ ).length === statusFilters[k].length
: statusFilters[k].includes(task[k]);
});
});
@@ -276,105 +269,12 @@ export function TaskFilter({ className, hook, profile }: IClassName & Props) {
function InputFilters({ hook, profile }: Props) {
const t = useTranslations();
const [loading, setLoading] = useState(false);
- const { tasks } = useTeamTasks();
- const { activeTeam } = useOrganizationTeams();
- const members = activeTeam?.members;
-
-
- const [date, setDate] = useState
(new Date());
- const [isBillable, setIsBillable] = useState(false);
- const [startTime, setStartTime] = useState('');
- const [endTime, setEndTime] = useState('');
- const [team, setTeam] = useState('');
- const [task, setTask] = useState('');
- const [description, setDescription] = useState('');
- const [reason, setReason] = useState('');
- const [timeDifference, setTimeDifference] = useState('');
- const [errorMsg, setError] = useState('');
- const [loading1, setLoading1] = useState(false);
- const { isOpen, openModal, closeModal } = useModal();
-
- useEffect(() => {
- const now = new Date();
- const currentTime = now.toTimeString().slice(0, 5);
-
- setDate(now);
- setStartTime(currentTime);
- setEndTime(currentTime);
- }, []);
-
-
- const handleSubmit = (e: FormEvent) => {
- e.preventDefault();
-
- const timeObject = {
- date,
- isBillable,
- startTime,
- endTime,
- team,
- task,
- description,
- reason,
- timeDifference
- };
-
- if (date && startTime && endTime && team && task) {
- setLoading1(true);
- setError('');
- const postData = async () => {
- try {
- const response = await api.post('/add_time', timeObject);
- if (response.data.message) {
- setLoading1(false);
- closeModal();
- }
-
- } catch (err) {
- setError('Failed to post data');
- setLoading1(false);
- }
- };
-
- postData();
- } else {
- setError(`Please complete all required fields with a ${"*"}`)
- }
- };
-
- const calculateTimeDifference = () => {
-
- const [startHours, startMinutes] = startTime.split(':').map(Number);
- const [endHours, endMinutes] = endTime.split(':').map(Number);
-
- const startTotalMinutes = startHours * 60 + startMinutes;
- const endTotalMinutes = endHours * 60 + endMinutes;
-
- const diffMinutes = endTotalMinutes - startTotalMinutes;
- if (diffMinutes < 0) {
- return;
- }
-
- const hours = Math.floor(diffMinutes / 60);
- const minutes = diffMinutes % 60;
- setTimeDifference(`${String(hours).padStart(2, '0')}h ${String(minutes).padStart(2, '0')}m`);
- };
-
-
- useEffect(() => {
- calculateTimeDifference();
- }, [endTime, startTime]);
-
- useEffect(() => {
- if (task == '') {
- setTask(tasks[0]?.id);
- }
- if (team == '') {
- members && setTeam(members[0].id);
- }
-
- }, [tasks, members]);
+ const {
+ isOpen: isManualTimeModalOpen,
+ openModal: openManualTimeModal,
+ closeModal: closeManualTimeModal
+ } = useModal();
const osSpecificAssignTaskTooltipLabel = 'A';
@@ -406,13 +306,13 @@ function InputFilters({ hook, profile }: Props) {
{t('common.FILTER')}
@@ -441,163 +341,8 @@ function InputFilters({ hook, profile }: Props) {
-
-
- {/* */}
-
-
-
-
-
setIsBillable(!isBillable)}
- style={isBillable ? { background: 'linear-gradient(to right, #9d91efb7, #8a7bedb7)' } : { background: '#6c57f4b7' }}
- >
-
-
-
-
-
-
-
-
-
-
-
- {timeDifference}
-
-
-
-
-
- setTeam(value)}
- itemId={(member) => (member.id)}
- itemToString={(member: any) => member?.employee?.user?.firstName}
- triggerClassName='border-slate-100 dark:border-slate-600'
- />
-
-
-
-
- task.title}
- itemId={(task) => task.id}
- onValueChange={(value) => setTask(value)}
- triggerClassName='border-slate-100 dark:border-slate-600'
- renderItem={(item, onClick) => {
- return
- }}
- />
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {errorMsg}
-
-
-
-
+
+
);
}
@@ -687,7 +432,12 @@ export function TaskStatusFilter({ hook, employeeId }: { hook: I_TaskFilter; emp
{hook.tab === 'dailyplan' && }
{['Future Tasks', 'Past Tasks', 'All Tasks'].includes(dailyPlanTab) && (
- setDate(range)} label="Planned date" />
+ setDate(range)}
+ label="Planned date"
+ />
)}
diff --git a/apps/web/locales/ar.json b/apps/web/locales/ar.json
index 41f8bd956..907c33e68 100644
--- a/apps/web/locales/ar.json
+++ b/apps/web/locales/ar.json
@@ -643,5 +643,16 @@
"TEAM": "الفريق",
"SETTINGS": "الإعدادات"
}
+ },
+ "manualTime": {
+ "reasons": {
+ "DEFAULT": "فقط الوقت اليدوي",
+ "LOST_ELECTRICITY": "انقطاع الكهرباء",
+ "LOST_INTERNET": "فقدان الإنترنت",
+ "FORGOT_TO_START_TIMER": "نسيت تشغيل المؤقت",
+ "ERROR": "خطأ",
+ "UNPLANNED_WORK": "عمل غير مخطط له",
+ "TESTED_TIMER": "اختبار المؤقت"
+ }
}
}
diff --git a/apps/web/locales/bg.json b/apps/web/locales/bg.json
index a3010903b..d0c28566c 100644
--- a/apps/web/locales/bg.json
+++ b/apps/web/locales/bg.json
@@ -643,5 +643,16 @@
"TEAM": "Отбор",
"SETTINGS": "Настройки"
}
+ },
+ "manualTime": {
+ "reasons": {
+ "DEFAULT": "Само ръчно време",
+ "LOST_ELECTRICITY": "Изгубено електрическо захранване",
+ "LOST_INTERNET": "Изгубен интернет",
+ "FORGOT_TO_START_TIMER": "Забравих да стартирам таймера",
+ "ERROR": "Грешка",
+ "UNPLANNED_WORK": "Неочаквана работа",
+ "TESTED_TIMER": "Тест на таймера"
+ }
}
}
diff --git a/apps/web/locales/de.json b/apps/web/locales/de.json
index 47d05249e..4ba01dee8 100644
--- a/apps/web/locales/de.json
+++ b/apps/web/locales/de.json
@@ -643,5 +643,16 @@
"TEAM": "Team",
"SETTINGS": "Einstellungen"
}
+ },
+ "manualTime": {
+ "reasons": {
+ "DEFAULT": "Nur manuelle Zeit",
+ "LOST_ELECTRICITY": "Stromausfall",
+ "LOST_INTERNET": "Internetausfall",
+ "FORGOT_TO_START_TIMER": "Timer vergessen zu starten",
+ "ERROR": "Fehler",
+ "UNPLANNED_WORK": "Ungeplante Arbeit",
+ "TESTED_TIMER": "Timer getestet"
+ }
}
}
diff --git a/apps/web/locales/en.json b/apps/web/locales/en.json
index a5d42a25a..5e3b4424a 100644
--- a/apps/web/locales/en.json
+++ b/apps/web/locales/en.json
@@ -643,5 +643,16 @@
"TEAM": "Team",
"SETTINGS": "Settings"
}
+ },
+ "manualTime": {
+ "reasons": {
+ "DEFAULT": "Only manual time",
+ "LOST_ELECTRICITY": "Lost elelctricity",
+ "LOST_INTERNET": "Lost internet",
+ "FORGOT_TO_START_TIMER": "Forgot to start timer",
+ "ERROR": "Error",
+ "UNPLANNED_WORK": "Unplanned work",
+ "TESTED_TIMER": "Tested timer"
+ }
}
}
diff --git a/apps/web/locales/es.json b/apps/web/locales/es.json
index c9b5074d6..1f0a9b30c 100644
--- a/apps/web/locales/es.json
+++ b/apps/web/locales/es.json
@@ -643,5 +643,16 @@
"TEAM": "Equipo",
"SETTINGS": "Ajustes"
}
+ },
+ "manualTime": {
+ "reasons": {
+ "DEFAULT": "Solo tiempo manual",
+ "LOST_ELECTRICITY": "Corte de electricidad",
+ "LOST_INTERNET": "Pérdida de internet",
+ "FORGOT_TO_START_TIMER": "Olvidé iniciar el temporizador",
+ "ERROR": "Error",
+ "UNPLANNED_WORK": "Trabajo no planificado",
+ "TESTED_TIMER": "Temporizador probado"
+ }
}
}
diff --git a/apps/web/locales/fr.json b/apps/web/locales/fr.json
index 5ffa6eb9f..c34816f44 100644
--- a/apps/web/locales/fr.json
+++ b/apps/web/locales/fr.json
@@ -643,5 +643,16 @@
"TEAM": "Équipe",
"SETTINGS": "Paramètres"
}
+ },
+ "manualTime": {
+ "reasons": {
+ "DEFAULT": "Seulement le temps manuel",
+ "LOST_ELECTRICITY": "Panne d'électricité",
+ "LOST_INTERNET": "Perte d'internet",
+ "FORGOT_TO_START_TIMER": "Oubli de démarrer le chronomètre",
+ "ERROR": "Erreur",
+ "UNPLANNED_WORK": "Travail non planifié",
+ "TESTED_TIMER": "Test du chronomètre"
+ }
}
}
diff --git a/apps/web/locales/he.json b/apps/web/locales/he.json
index c797130bf..33af41f78 100644
--- a/apps/web/locales/he.json
+++ b/apps/web/locales/he.json
@@ -643,5 +643,16 @@
"TEAM": "צוות",
"SETTINGS": "הגדרות"
}
+ },
+ "manualTime": {
+ "reasons": {
+ "DEFAULT": "רק זמן ידני",
+ "LOST_ELECTRICITY": "הפסקת חשמל",
+ "LOST_INTERNET": "אובדן אינטרנט",
+ "FORGOT_TO_START_TIMER": "שכחתי להפעיל את הטיימר",
+ "ERROR": "שגיאה",
+ "UNPLANNED_WORK": "עבודה לא מתוכננת",
+ "TESTED_TIMER": "טיימר נבדק"
+ }
}
}
diff --git a/apps/web/locales/it.json b/apps/web/locales/it.json
index 910473648..81a7eac5c 100644
--- a/apps/web/locales/it.json
+++ b/apps/web/locales/it.json
@@ -643,5 +643,16 @@
"TEAM": "Team",
"SETTINGS": "Impostazioni"
}
+ },
+ "manualTime": {
+ "reasons": {
+ "DEFAULT": "Solo tempo manuale",
+ "LOST_ELECTRICITY": "Interruzione di corrente",
+ "LOST_INTERNET": "Perdita di internet",
+ "FORGOT_TO_START_TIMER": "Dimenticato di avviare il timer",
+ "ERROR": "Errore",
+ "UNPLANNED_WORK": "Lavoro non pianificato",
+ "TESTED_TIMER": "Timer testato"
+ }
}
}
diff --git a/apps/web/locales/nl.json b/apps/web/locales/nl.json
index 0451bcc77..bf44821df 100644
--- a/apps/web/locales/nl.json
+++ b/apps/web/locales/nl.json
@@ -643,5 +643,16 @@
"TEAM": "Team",
"SETTINGS": "Instellingen"
}
+ },
+ "manualTime": {
+ "reasons": {
+ "DEFAULT": "Alleen handmatige tijd",
+ "LOST_ELECTRICITY": "Stroomuitval",
+ "LOST_INTERNET": "Internetverlies",
+ "FORGOT_TO_START_TIMER": "Vergeten de timer te starten",
+ "ERROR": "Fout",
+ "UNPLANNED_WORK": "Ongeplande werkzaamheden",
+ "TESTED_TIMER": "Timer getest"
+ }
}
}
diff --git a/apps/web/locales/pl.json b/apps/web/locales/pl.json
index 9e33e82e3..ad9bc06e9 100644
--- a/apps/web/locales/pl.json
+++ b/apps/web/locales/pl.json
@@ -643,5 +643,16 @@
"TEAM": "Zespół",
"SETTINGS": "Ustawienia"
}
+ },
+ "manualTime": {
+ "reasons": {
+ "DEFAULT": "Tylko czas ręczny",
+ "LOST_ELECTRICITY": "Utrata zasilania",
+ "LOST_INTERNET": "Utrata internetu",
+ "FORGOT_TO_START_TIMER": "Zapomniałem uruchomić stoper",
+ "ERROR": "Błąd",
+ "UNPLANNED_WORK": "Nieplanowana praca",
+ "TESTED_TIMER": "Testowany stoper"
+ }
}
}
diff --git a/apps/web/locales/pt.json b/apps/web/locales/pt.json
index 88d280439..8f8cb7003 100644
--- a/apps/web/locales/pt.json
+++ b/apps/web/locales/pt.json
@@ -643,5 +643,16 @@
"TEAM": "Equipe",
"SETTINGS": "Configurações"
}
+ },
+ "manualTime": {
+ "reasons": {
+ "DEFAULT": "Apenas tempo manual",
+ "LOST_ELECTRICITY": "Perda de eletricidade",
+ "LOST_INTERNET": "Perda de internet",
+ "FORGOT_TO_START_TIMER": "Esqueci de iniciar o temporizador",
+ "ERROR": "Erro",
+ "UNPLANNED_WORK": "Trabalho não planejado",
+ "TESTED_TIMER": "Temporizador testado"
+ }
}
}
diff --git a/apps/web/locales/ru.json b/apps/web/locales/ru.json
index ec5036f90..f10419b14 100644
--- a/apps/web/locales/ru.json
+++ b/apps/web/locales/ru.json
@@ -643,5 +643,16 @@
"TEAM": "Команда",
"SETTINGS": "Настройки"
}
+ },
+ "manualTime": {
+ "reasons": {
+ "DEFAULT": "Только ручное время",
+ "LOST_ELECTRICITY": "Потеря электричества",
+ "LOST_INTERNET": "Потеря интернета",
+ "FORGOT_TO_START_TIMER": "Забыл запустить таймер",
+ "ERROR": "Ошибка",
+ "UNPLANNED_WORK": "Незапланированная работа",
+ "TESTED_TIMER": "Таймер протестирован"
+ }
}
}
diff --git a/apps/web/locales/zh.json b/apps/web/locales/zh.json
index 86f4dc55c..0203c99fb 100644
--- a/apps/web/locales/zh.json
+++ b/apps/web/locales/zh.json
@@ -643,5 +643,16 @@
"TEAM": "团队",
"SETTINGS": "设置"
}
+ },
+ "manualTime": {
+ "reasons": {
+ "DEFAULT": "仅手动时间",
+ "LOST_ELECTRICITY": "失去電力",
+ "LOST_INTERNET": "失去網路",
+ "FORGOT_TO_START_TIMER": "忘記啟動計時器",
+ "ERROR": "錯誤",
+ "UNPLANNED_WORK": "未計劃的工作",
+ "TESTED_TIMER": "計時器測試"
+ }
}
}