- {t('backup.createNewBackup', 'Create New Backup')}
+ {t(
+ 'backup.createNewBackup',
+ 'Create New Backup'
+ )}
{t(
@@ -353,12 +367,18 @@ const BackupRestore: React.FC = ({ onImportSuccess }) => {
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
>
- {t('backup.creating', 'Creating backup...')}
+ {t(
+ 'backup.creating',
+ 'Creating backup...'
+ )}
>
) : (
<>
- {t('backup.createBackupNow', 'Create Backup Now')}
+ {t(
+ 'backup.createBackupNow',
+ 'Create Backup Now'
+ )}
>
)}
@@ -367,14 +387,19 @@ const BackupRestore: React.FC = ({ onImportSuccess }) => {
- {t('backup.savedBackups', 'Saved Backups')}
+ {t(
+ 'backup.savedBackups',
+ 'Saved Backups'
+ )}
@@ -385,7 +410,10 @@ const BackupRestore: React.FC
= ({ onImportSuccess }) => {
) : savedBackups.length === 0 ? (
- {t('backup.noBackups', 'No backups found. Create your first backup above.')}
+ {t(
+ 'backup.noBackups',
+ 'No backups found. Create your first backup above.'
+ )}
) : (
@@ -393,74 +421,136 @@ const BackupRestore: React.FC
= ({ onImportSuccess }) => {
|
- {t('backup.createdAt', 'Created')}
+ {t(
+ 'backup.createdAt',
+ 'Created'
+ )}
|
- {t('backup.version', 'Version')}
+ {t(
+ 'backup.version',
+ 'Version'
+ )}
|
- {t('backup.size', 'Size')}
+ {t(
+ 'backup.size',
+ 'Size'
+ )}
|
- {t('backup.contents', 'Contents')}
+ {t(
+ 'backup.contents',
+ 'Contents'
+ )}
|
- {t('backup.actions', 'Actions')}
+ {t(
+ 'backup.actions',
+ 'Actions'
+ )}
|
- {savedBackups.map((backup) => (
-
- |
- {formatDate(backup.created_at)}
- |
-
- {backup.version}
- |
-
- {formatFileSize(backup.file_size)}
- |
-
-
-
- {backup.item_counts.tasks} tasks
-
-
- {backup.item_counts.projects} projects
-
-
- {backup.item_counts.notes} notes
-
-
- |
-
-
-
-
-
-
- |
-
- ))}
+ {savedBackups.map(
+ (backup) => (
+
+ |
+ {formatDate(
+ backup.created_at
+ )}
+ |
+
+ {
+ backup.version
+ }
+ |
+
+ {formatFileSize(
+ backup.file_size
+ )}
+ |
+
+
+
+ {
+ backup
+ .item_counts
+ .tasks
+ }{' '}
+ tasks
+
+
+ {
+ backup
+ .item_counts
+ .projects
+ }{' '}
+ projects
+
+
+ {
+ backup
+ .item_counts
+ .notes
+ }{' '}
+ notes
+
+
+ |
+
+
+
+
+
+
+ |
+
+ )
+ )}
@@ -471,7 +561,10 @@ const BackupRestore: React.FC = ({ onImportSuccess }) => {
- {t('backup.importTitle', 'Import from File')}
+ {t(
+ 'backup.importTitle',
+ 'Import from File'
+ )}
{t(
@@ -502,16 +595,24 @@ const BackupRestore: React.FC = ({ onImportSuccess }) => {
/>
@@ -524,7 +625,10 @@ const BackupRestore: React.FC = ({ onImportSuccess }) => {
{selectedFile.name}
- {(selectedFile.size / 1024).toFixed(2)} KB
+ {(
+ selectedFile.size / 1024
+ ).toFixed(2)}{' '}
+ KB
{isValidating && (
@@ -554,71 +658,139 @@ const BackupRestore: React.FC
= ({ onImportSuccess }) => {
)}
- {validationResult?.valid && validationResult.summary && (
-
-
- {t('backup.backupContents', 'Backup contents:')}
-
-
-
-
- {validationResult.summary.tasks} tasks
-
-
-
- {validationResult.summary.projects} projects
-
-
-
- {validationResult.summary.notes} notes
-
-
-
- {validationResult.summary.tags} tags
-
-
-
- {validationResult.summary.areas} areas
-
-
-
- {validationResult.summary.views} views
+ {validationResult?.valid &&
+ validationResult.summary && (
+
+
+ {t(
+ 'backup.backupContents',
+ 'Backup contents:'
+ )}
+
+
+
+
+ {
+ validationResult
+ .summary
+ .tasks
+ }{' '}
+ tasks
+
+
+
+ {
+ validationResult
+ .summary
+ .projects
+ }{' '}
+ projects
+
+
+
+ {
+ validationResult
+ .summary
+ .notes
+ }{' '}
+ notes
+
+
+
+ {
+ validationResult
+ .summary
+ .tags
+ }{' '}
+ tags
+
+
+
+ {
+ validationResult
+ .summary
+ .areas
+ }{' '}
+ areas
+
+
+
+ {
+ validationResult
+ .summary
+ .views
+ }{' '}
+ views
+
-
- )}
+ )}
- {validationResult && !validationResult.valid && (
-
- {validationResult.versionIncompatible ? (
- <>
-
- {t('backup.versionIncompatible', 'Version Incompatible')}
-
-
- {validationResult.message}
-
-
- {t('backup.backupVersion', 'Backup version')}: {validationResult.backupVersion}
-
-
- {t('backup.currentVersion', 'Current version')}: {appVersion}
-
- >
- ) : (
- <>
-
- {t('backup.validationErrors', 'Validation errors:')}
-
-
- {validationResult.errors?.map((error, index) => (
- - • {error}
- ))}
-
- >
- )}
-
- )}
+ {validationResult &&
+ !validationResult.valid && (
+
+ {validationResult.versionIncompatible ? (
+ <>
+
+ {t(
+ 'backup.versionIncompatible',
+ 'Version Incompatible'
+ )}
+
+
+ {
+ validationResult.message
+ }
+
+
+ {t(
+ 'backup.backupVersion',
+ 'Backup version'
+ )}
+ :{' '}
+ {
+ validationResult.backupVersion
+ }
+
+
+ {t(
+ 'backup.currentVersion',
+ 'Current version'
+ )}
+ : {appVersion}
+
+ >
+ ) : (
+ <>
+
+ {t(
+ 'backup.validationErrors',
+ 'Validation errors:'
+ )}
+
+
+ {validationResult.errors?.map(
+ (
+ error,
+ index
+ ) => (
+ -
+ •{' '}
+ {
+ error
+ }
+
+ )
+ )}
+
+ >
+ )}
+
+ )}
)}
@@ -650,12 +822,18 @@ const BackupRestore: React.FC
= ({ onImportSuccess }) => {
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
>
- {t('backup.importing', 'Importing...')}
+ {t(
+ 'backup.importing',
+ 'Importing...'
+ )}
>
) : (
<>
- {t('backup.restoreBackup', 'Restore Backup')}
+ {t(
+ 'backup.restoreBackup',
+ 'Restore Backup'
+ )}
>
)}
diff --git a/frontend/components/Navbar.tsx b/frontend/components/Navbar.tsx
index ff4be93..effde41 100644
--- a/frontend/components/Navbar.tsx
+++ b/frontend/components/Navbar.tsx
@@ -38,7 +38,10 @@ const Navbar: React.FC = ({
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
const [isMobileSearchOpen, setIsMobileSearchOpen] = useState(false);
const [pomodoroEnabled, setPomodoroEnabled] = useState(true); // Default to true
- const [featureFlags, setFeatureFlags] = useState({ backups: false, calendar: false });
+ const [featureFlags, setFeatureFlags] = useState({
+ backups: false,
+ calendar: false,
+ });
const dropdownRef = useRef(null);
const navigate = useNavigate();
@@ -264,7 +267,10 @@ const Navbar: React.FC = ({
className="block px-4 py-2 text-sm text-gray-700 dark:text-gray-200 hover:bg-gray-100 dark:hover:bg-gray-700"
onClick={() => setIsDropdownOpen(false)}
>
- {t('navigation.backupRestore', 'Backup & Restore')}
+ {t(
+ 'navigation.backupRestore',
+ 'Backup & Restore'
+ )}
)}
{currentUser?.is_admin === true && (
diff --git a/frontend/components/Productivity/ProductivityAssistant.tsx b/frontend/components/Productivity/ProductivityAssistant.tsx
index 71ee790..d2b0671 100644
--- a/frontend/components/Productivity/ProductivityAssistant.tsx
+++ b/frontend/components/Productivity/ProductivityAssistant.tsx
@@ -299,7 +299,6 @@ const ProductivityAssistant: React.FC = ({
}
};
-
if (totalIssues === 0) {
return null;
}
@@ -313,10 +312,7 @@ const ProductivityAssistant: React.FC = ({
- {t(
- 'productivity.issuesFound',
- { count: totalIssues }
- )}
+ {t('productivity.issuesFound', { count: totalIssues })}
{t(
diff --git a/frontend/components/Project/ProjectItem.tsx b/frontend/components/Project/ProjectItem.tsx
index 36f1b3b..b53223f 100644
--- a/frontend/components/Project/ProjectItem.tsx
+++ b/frontend/components/Project/ProjectItem.tsx
@@ -98,7 +98,6 @@ const getShareInitials = (value?: string | null) => {
return cleaned.substring(0, 2) || '?';
};
-
const ProjectItem: React.FC = ({
project,
viewMode,
@@ -221,14 +220,10 @@ const ProjectItem: React.FC = ({
}
return {
- text: t(
- 'projectItem.overdue',
- 'Overdue {{count}} {{unit}} ago',
- {
- count: Math.abs(diff),
- unit,
- }
- ),
+ text: t('projectItem.overdue', 'Overdue {{count}} {{unit}} ago', {
+ count: Math.abs(diff),
+ unit,
+ }),
isOverdue: true,
};
}, [project.due_date_at, t]);
@@ -244,9 +239,7 @@ const ProjectItem: React.FC = ({
const knownShares = sharedUsers ?? [];
const avatars = knownShares.slice(0, MAX_SHARE_AVATARS);
const totalCount =
- (sharedUsers?.length ??
- project.share_count ??
- avatars.length) || 0;
+ (sharedUsers?.length ?? project.share_count ?? avatars.length) || 0;
const remaining = Math.max(0, totalCount - avatars.length);
return { avatars, remaining };
@@ -313,7 +306,10 @@ const ProjectItem: React.FC = ({
return (
);
})()}
@@ -353,10 +349,14 @@ const ProjectItem: React.FC = ({
'Permission denied'
)
);
- setActiveDropdown(null);
+ setActiveDropdown(
+ null
+ );
return;
}
- handleEditProject(project);
+ handleEditProject(
+ project
+ );
setActiveDropdown(null);
}}
className="block px-4 py-2 text-sm text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-600 w-full text-left"
@@ -369,8 +369,12 @@ const ProjectItem: React.FC = ({
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
- onOpenShare(project);
- setActiveDropdown(null);
+ onOpenShare(
+ project
+ );
+ setActiveDropdown(
+ null
+ );
}}
className="block px-4 py-2 text-sm text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-600 w-full text-left"
>
@@ -395,8 +399,12 @@ const ProjectItem: React.FC = ({
);
return;
}
- setProjectToDelete(project);
- setIsConfirmDialogOpen(true);
+ setProjectToDelete(
+ project
+ );
+ setIsConfirmDialogOpen(
+ true
+ );
setActiveDropdown(null);
}}
className="block px-4 py-2 text-sm text-red-500 dark:text-red-300 hover:bg-gray-100 dark:hover:bg-gray-600 w-full text-left"
@@ -453,9 +461,13 @@ const ProjectItem: React.FC = ({
title={
(project as any).task_status
? `${(project as any).task_status.done} of ${(project as any).task_status.total} tasks completed (${getCompletionPercentage()}%)`
- : t('projectItem.completionPercentage', {
- percentage: getCompletionPercentage(),
- })
+ : t(
+ 'projectItem.completionPercentage',
+ {
+ percentage:
+ getCompletionPercentage(),
+ }
+ )
}
>
= ({
: '0/0'}
-
-
+
+
{dueInfo.isOverdue ? (
-
+
{dueInfo.text}
) : (
- {dueInfo.text}
+
+ {dueInfo.text}
+
)}
{project.is_shared && (
<>
- {shareAvatars.avatars.map((share) => (
-
- {share.avatar_image ? (
-
- ) : (
-
- {getShareInitials(share.email)}
-
- )}
-
- ))}
+ {shareAvatars.avatars.map(
+ (share) => (
+
+ {share.avatar_image ? (
+
+ ) : (
+
+ {getShareInitials(
+ share.email
+ )}
+
+ )}
+
+ )
+ )}
{shareAvatars.remaining > 0 && (
= ({
)}
>
- +{shareAvatars.remaining}
+ +
+ {shareAvatars.remaining}
)}
diff --git a/frontend/components/Sidebar/SidebarNav.tsx b/frontend/components/Sidebar/SidebarNav.tsx
index 64727f5..e731197 100644
--- a/frontend/components/Sidebar/SidebarNav.tsx
+++ b/frontend/components/Sidebar/SidebarNav.tsx
@@ -27,7 +27,10 @@ const SidebarNav: React.FC
= ({
}) => {
const { t } = useTranslation();
const store = useStore();
- const [featureFlags, setFeatureFlags] = useState({ backups: false, calendar: false });
+ const [featureFlags, setFeatureFlags] = useState({
+ backups: false,
+ calendar: false,
+ });
const inboxItemsCount = store.inboxStore.pagination.total;
@@ -72,7 +75,7 @@ const SidebarNav: React.FC = ({
},
];
- const navLinks = allNavLinks.filter(link => {
+ const navLinks = allNavLinks.filter((link) => {
if (link.featureFlag) {
return featureFlags[link.featureFlag as keyof FeatureFlags];
}
diff --git a/frontend/components/Task/TaskDetails.tsx b/frontend/components/Task/TaskDetails.tsx
index 34ee217..c792969 100644
--- a/frontend/components/Task/TaskDetails.tsx
+++ b/frontend/components/Task/TaskDetails.tsx
@@ -1248,6 +1248,7 @@ const TaskDetails: React.FC = () => {
onToggleTodayPlan={handleToggleTodayPlan}
onQuickStatusToggle={handleQuickStatusToggle}
attachmentCount={attachmentCount}
+ subtasksCount={subtasks.length}
/>
{/* Content - Full width layout */}
@@ -1285,49 +1286,47 @@ const TaskDetails: React.FC = () => {
onLoadTags={() => tagsStore.loadTags()}
getTagLink={getTagLink}
/>
+
+
+
+
)}
- {/* Schedule Pill */}
- {activePill === 'schedule' && (
-
)}
@@ -1362,16 +1361,11 @@ const TaskDetails: React.FC = () => {
{/* Activity Pill */}
{activePill === 'activity' && (
-
-
- {t('task.recentActivity', 'Recent Activity')}
-
-
-
-
+
+
)}
diff --git a/frontend/components/Task/TaskDetails/TaskDetailsHeader.tsx b/frontend/components/Task/TaskDetails/TaskDetailsHeader.tsx
index 7d45e18..ef2fc64 100644
--- a/frontend/components/Task/TaskDetails/TaskDetailsHeader.tsx
+++ b/frontend/components/Task/TaskDetails/TaskDetailsHeader.tsx
@@ -40,6 +40,7 @@ interface TaskDetailsHeaderProps {
onToggleTodayPlan?: () => void;
onQuickStatusToggle?: () => void;
attachmentCount?: number;
+ subtasksCount?: number;
}
const TaskDetailsHeader: React.FC
= ({
@@ -61,6 +62,7 @@ const TaskDetailsHeader: React.FC = ({
onToggleTodayPlan,
onQuickStatusToggle,
attachmentCount = 0,
+ subtasksCount = 0,
}) => {
const { t } = useTranslation();
const [isEditingTitle, setIsEditingTitle] = useState(false);
@@ -657,7 +659,7 @@ const TaskDetailsHeader: React.FC = ({
{t(
'task.lastUpdatedAt',
- 'Last updated at'
+ 'Updated at'
)}
:{' '}
@@ -761,23 +763,30 @@ const TaskDetailsHeader: React.FC = ({
)}
- {view.recurring && (
-
-
- {t('views.recurring')}
-
-
- {view.recurring.replace(
- /_/g,
- ' '
- )}
-
-
- )}
+ {view.extras &&
+ view.extras.length > 0 && (
+
+
+ {t('search.extras')}
+
+
+ {view.extras.map(
+ (
+ extra,
+ index
+ ) => (
+
+ {extra.replace(
+ /_/g,
+ ' '
+ )}
+
+ )
+ )}
+
+
+ )}
{!view.filters.length &&
!view.search_query &&
!view.priority &&
!view.due &&
(!view.tags ||
view.tags.length === 0) &&
- !view.recurring && (
+ (!view.extras ||
+ view.extras.length ===
+ 0) && (
{t(
'views.noCriteriaSet'
diff --git a/frontend/components/Views.tsx b/frontend/components/Views.tsx
index eab1b4f..2d4205a 100644
--- a/frontend/components/Views.tsx
+++ b/frontend/components/Views.tsx
@@ -18,6 +18,9 @@ interface View {
filters: string[];
priority: string | null;
due: string | null;
+ defer: string | null;
+ tags: string[];
+ extras: string[] | null;
is_pinned: boolean;
}
@@ -43,7 +46,13 @@ const Views: React.FC = () => {
});
if (response.ok) {
const data = await response.json();
- setViews(data);
+ const normalized: View[] = data.map((view: View) => ({
+ ...view,
+ tags: view.tags || [],
+ extras: view.extras || [],
+ defer: view.defer || null,
+ }));
+ setViews(normalized);
}
} catch (error) {
console.error('Error fetching views:', error);
@@ -236,6 +245,24 @@ const Views: React.FC = () => {
{view.due}
)}
+ {view.defer && (
+
+ •{' '}
+ {t('search.deferUntil')}{' '}
+ {view.defer}
+
+ )}
+ {view.extras &&
+ view.extras.length > 0 && (
+
+ •{' '}
+ {t('search.extras')}
+ :{' '}
+ {view.extras.join(
+ ', '
+ )}
+
+ )}
diff --git a/frontend/utils/backupService.ts b/frontend/utils/backupService.ts
index 1813fd9..14ef9fe 100644
--- a/frontend/utils/backupService.ts
+++ b/frontend/utils/backupService.ts
@@ -222,9 +222,7 @@ export const importBackup = async (
/**
* Validate backup file without importing
*/
-export const validateBackup = async (
- file: File
-): Promise => {
+export const validateBackup = async (file: File): Promise => {
const formData = new FormData();
formData.append('backup', file);
diff --git a/frontend/utils/searchService.ts b/frontend/utils/searchService.ts
index 72fa1b6..75d13a1 100644
--- a/frontend/utils/searchService.ts
+++ b/frontend/utils/searchService.ts
@@ -7,7 +7,7 @@ interface SearchParams {
due?: string;
defer?: string;
tags?: string[];
- recurring?: string;
+ extras?: string[];
limit?: number;
offset?: number;
excludeSubtasks?: boolean;
@@ -66,8 +66,8 @@ export const searchUniversal = async (
queryParams.append('tags', params.tags.join(','));
}
- if (params.recurring) {
- queryParams.append('recurring', params.recurring);
+ if (params.extras && params.extras.length > 0) {
+ queryParams.append('extras', params.extras.join(','));
}
if (params.limit !== undefined) {
diff --git a/public/locales/ar/translation.json b/public/locales/ar/translation.json
index 0ef4eab..2faa647 100644
--- a/public/locales/ar/translation.json
+++ b/public/locales/ar/translation.json
@@ -745,7 +745,7 @@
"dueDate": "تاريخ الاستحقاق",
"deferUntil": "تأجيل حتى",
"recurringSetup": "إعداد متكرر",
- "notRecurring": "هذه المهمة ليست متكررة بعد.",
+ "notRecurring": "أضف تفاصيل التكرار",
"clickToEditTitle": "انقر لتحرير العنوان",
"clickToEditContent": "انقر لتحرير المحتوى",
"clickToAddContent": "انقر لإضافة محتوى",
@@ -1206,7 +1206,15 @@
"instances": "حالات متكررة"
},
"deferUntilFilter": "تأجيل حتى",
- "deferUntil": "، تأجيل حتى"
+ "deferUntil": "، تأجيل حتى",
+ "extrasFilter": {
+ "isRecurring": "يتكرر",
+ "isOverdue": "متأخر",
+ "hasContent": "يمتلك محتوى",
+ "isDeferred": "مؤجل",
+ "hasTags": "يمتلك علامات",
+ "isAssignedToProject": "مخصص لمشروع"
+ }
},
"subtasks": {
"placeholder": "أضف مهمة فرعية..."
diff --git a/public/locales/bg/translation.json b/public/locales/bg/translation.json
index a2f14d6..8b701c4 100644
--- a/public/locales/bg/translation.json
+++ b/public/locales/bg/translation.json
@@ -1206,7 +1206,15 @@
"instances": "повтарящи се инстанции"
},
"deferUntilFilter": "Отложи до",
- "deferUntil": ", отложи до"
+ "deferUntil": ", отложи до",
+ "extrasFilter": {
+ "isRecurring": "е Повтарящо се",
+ "isOverdue": "е Просрочено",
+ "hasContent": "има Съдържание",
+ "isDeferred": "е Отложено",
+ "hasTags": "има Тагове",
+ "isAssignedToProject": "е Назначено на Проект"
+ }
},
"subtasks": {
"placeholder": "Добавете подзадача..."
diff --git a/public/locales/da/translation.json b/public/locales/da/translation.json
index f18ed29..52e3bfb 100644
--- a/public/locales/da/translation.json
+++ b/public/locales/da/translation.json
@@ -1206,7 +1206,15 @@
"instances": "gentagende instanser"
},
"deferUntilFilter": "Udskyd indtil",
- "deferUntil": ", udskyd indtil"
+ "deferUntil": ", udskyd indtil",
+ "extrasFilter": {
+ "isRecurring": "er tilbagevendende",
+ "isOverdue": "er forfalden",
+ "hasContent": "har indhold",
+ "isDeferred": "er udsat",
+ "hasTags": "har tags",
+ "isAssignedToProject": "er tildelt projekt"
+ }
},
"subtasks": {
"placeholder": "Tilføj en underopgave..."
diff --git a/public/locales/de/translation.json b/public/locales/de/translation.json
index 7699b1f..6c93d93 100644
--- a/public/locales/de/translation.json
+++ b/public/locales/de/translation.json
@@ -1215,7 +1215,15 @@
"instances": "wiederkehrende Instanzen"
},
"deferUntilFilter": "Bis zu",
- "deferUntil": ", bis zu"
+ "deferUntil": ", bis zu",
+ "extrasFilter": {
+ "isRecurring": "ist wiederkehrend",
+ "isOverdue": "ist überfällig",
+ "hasContent": "hat Inhalt",
+ "isDeferred": "ist verschoben",
+ "hasTags": "hat Tags",
+ "isAssignedToProject": "ist einem Projekt zugewiesen"
+ }
},
"subtasks": {
"placeholder": "Fügen Sie eine Unteraufgabe hinzu..."
diff --git a/public/locales/el/translation.json b/public/locales/el/translation.json
index d88c911..00ad0d8 100644
--- a/public/locales/el/translation.json
+++ b/public/locales/el/translation.json
@@ -1210,7 +1210,15 @@
"instances": "επαναλαμβανόμενες περιπτώσεις"
},
"deferUntilFilter": "Αναβολή μέχρι",
- "deferUntil": ", αναβολή μέχρι"
+ "deferUntil": ", αναβολή μέχρι",
+ "extrasFilter": {
+ "isRecurring": "είναι Επαναλαμβανόμενο",
+ "isOverdue": "είναι Υπερβολικό",
+ "hasContent": "έχει Περιεχόμενο",
+ "isDeferred": "είναι Αναβληθέν",
+ "hasTags": "έχει Ετικέτες",
+ "isAssignedToProject": "είναι Ανατεθειμένο σε Έργο"
+ }
},
"subtasks": {
"placeholder": "Προσθέστε μια υποεργασία..."
diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json
index d478bbf..aef8a66 100644
--- a/public/locales/en/translation.json
+++ b/public/locales/en/translation.json
@@ -723,7 +723,7 @@
"dueDate": "Due Date",
"noDueDate": "No due date",
"recurringSetup": "Recurring Setup",
- "notRecurring": "This task is not recurring yet.",
+ "notRecurring": "Add recurrence details",
"instanceOf": "This is an instance of a recurring task",
"parentTask": "Parent Task",
"clickToEditTitle": "Click to edit title",
@@ -1179,11 +1179,13 @@
"deferUntilFilter": "Defer Until",
"deferUntil": ", defer until",
"tagsFilter": "Tags",
- "recurringFilter": {
- "label": "Recurring",
- "recurring": "recurring templates",
- "nonRecurring": "non-recurring",
- "instances": "recurring instances"
+ "extrasFilter": {
+ "isRecurring": "is Recurring",
+ "isOverdue": "is Overdue",
+ "hasContent": "has Content",
+ "isDeferred": "is Deferred",
+ "hasTags": "has Tags",
+ "isAssignedToProject": "is Assigned to Project"
},
"saveAsSmartView": "Save as Smart View",
"viewName": "View Name",
diff --git a/public/locales/es/translation.json b/public/locales/es/translation.json
index aa0dd77..701b68f 100644
--- a/public/locales/es/translation.json
+++ b/public/locales/es/translation.json
@@ -1207,7 +1207,15 @@
"instances": "instancias recurrentes"
},
"deferUntilFilter": "Aplazar hasta",
- "deferUntil": ", aplazar hasta"
+ "deferUntil": ", aplazar hasta",
+ "extrasFilter": {
+ "isRecurring": "es Recurrente",
+ "isOverdue": "está Vencido",
+ "hasContent": "tiene Contenido",
+ "isDeferred": "está Diferido",
+ "hasTags": "tiene Etiquetas",
+ "isAssignedToProject": "está Asignado al Proyecto"
+ }
},
"subtasks": {
"placeholder": "Agregar una subtarea..."
diff --git a/public/locales/fi/translation.json b/public/locales/fi/translation.json
index e18f611..e2bc84c 100644
--- a/public/locales/fi/translation.json
+++ b/public/locales/fi/translation.json
@@ -1206,7 +1206,15 @@
"instances": "toistuvat instanssit"
},
"deferUntilFilter": "Viivästytä kunnes",
- "deferUntil": ", viivästytä kunnes"
+ "deferUntil": ", viivästytä kunnes",
+ "extrasFilter": {
+ "isRecurring": "on Toistuva",
+ "isOverdue": "on Erääntynyt",
+ "hasContent": "on Sisältöä",
+ "isDeferred": "on Siirretty",
+ "hasTags": "on Tunnisteita",
+ "isAssignedToProject": "on Määrätty Projektiin"
+ }
},
"subtasks": {
"placeholder": "Lisää alitehtävä..."
diff --git a/public/locales/fr/translation.json b/public/locales/fr/translation.json
index ebaa609..68bc369 100644
--- a/public/locales/fr/translation.json
+++ b/public/locales/fr/translation.json
@@ -1206,7 +1206,15 @@
"instances": "instances récurrentes"
},
"deferUntilFilter": "Différer jusqu'à",
- "deferUntil": ", différer jusqu'à"
+ "deferUntil": ", différer jusqu'à",
+ "extrasFilter": {
+ "isRecurring": "est Récurrent",
+ "isOverdue": "est En Retard",
+ "hasContent": "a du Contenu",
+ "isDeferred": "est Différé",
+ "hasTags": "a des Étiquettes",
+ "isAssignedToProject": "est Assigné au Projet"
+ }
},
"subtasks": {
"placeholder": "Ajouter une sous-tâche..."
diff --git a/public/locales/id/translation.json b/public/locales/id/translation.json
index 7d9f722..e1633e0 100644
--- a/public/locales/id/translation.json
+++ b/public/locales/id/translation.json
@@ -1206,7 +1206,15 @@
"instances": "instansi berulang"
},
"deferUntilFilter": "Tunda Hingga",
- "deferUntil": ", tunda hingga"
+ "deferUntil": ", tunda hingga",
+ "extrasFilter": {
+ "isRecurring": "adalah Berulang",
+ "isOverdue": "adalah Terlambat",
+ "hasContent": "memiliki Konten",
+ "isDeferred": "adalah Ditunda",
+ "hasTags": "memiliki Tag",
+ "isAssignedToProject": "adalah Ditugaskan ke Proyek"
+ }
},
"subtasks": {
"placeholder": "Tambahkan subtugas..."
diff --git a/public/locales/it/translation.json b/public/locales/it/translation.json
index fefb30c..5213144 100644
--- a/public/locales/it/translation.json
+++ b/public/locales/it/translation.json
@@ -1206,7 +1206,15 @@
"instances": "istanze ricorrenti"
},
"deferUntilFilter": "Rimanda fino a",
- "deferUntil": ", rimanda fino a"
+ "deferUntil": ", rimanda fino a",
+ "extrasFilter": {
+ "isRecurring": "è Ricorrente",
+ "isOverdue": "è Scaduto",
+ "hasContent": "ha Contenuto",
+ "isDeferred": "è Rinviato",
+ "hasTags": "ha Tag",
+ "isAssignedToProject": "è Assegnato al Progetto"
+ }
},
"subtasks": {
"placeholder": "Aggiungi un sottocompito..."
diff --git a/public/locales/jp/translation.json b/public/locales/jp/translation.json
index c2d02d7..056eee0 100644
--- a/public/locales/jp/translation.json
+++ b/public/locales/jp/translation.json
@@ -1206,7 +1206,15 @@
"instances": "定期的なインスタンス"
},
"deferUntilFilter": "フィルターまで遅延",
- "deferUntil": "、遅延するまで"
+ "deferUntil": "、遅延するまで",
+ "extrasFilter": {
+ "isRecurring": "繰り返し",
+ "isOverdue": "期限切れ",
+ "hasContent": "コンテンツあり",
+ "isDeferred": "保留中",
+ "hasTags": "タグあり",
+ "isAssignedToProject": "プロジェクトに割り当てられている"
+ }
},
"subtasks": {
"placeholder": "サブタスクを追加..."
diff --git a/public/locales/ko/translation.json b/public/locales/ko/translation.json
index 0b96eb4..0607e31 100644
--- a/public/locales/ko/translation.json
+++ b/public/locales/ko/translation.json
@@ -1206,7 +1206,15 @@
"instances": "반복 인스턴스"
},
"deferUntilFilter": "지연할 때까지",
- "deferUntil": ", 지연할 때까지"
+ "deferUntil": ", 지연할 때까지",
+ "extrasFilter": {
+ "isRecurring": "반복됨",
+ "isOverdue": "연체됨",
+ "hasContent": "내용이 있음",
+ "isDeferred": "연기됨",
+ "hasTags": "태그가 있음",
+ "isAssignedToProject": "프로젝트에 할당됨"
+ }
},
"subtasks": {
"placeholder": "하위 작업 추가..."
diff --git a/public/locales/nl/translation.json b/public/locales/nl/translation.json
index a92803a..e94ce6a 100644
--- a/public/locales/nl/translation.json
+++ b/public/locales/nl/translation.json
@@ -1206,7 +1206,15 @@
"instances": "herhalende instanties"
},
"deferUntilFilter": "Uitstellen tot",
- "deferUntil": ", uitstellen tot"
+ "deferUntil": ", uitstellen tot",
+ "extrasFilter": {
+ "isRecurring": "is Herhalend",
+ "isOverdue": "is Achterstallig",
+ "hasContent": "heeft Inhoud",
+ "isDeferred": "is Uitgesteld",
+ "hasTags": "heeft Tags",
+ "isAssignedToProject": "is Toegewezen aan Project"
+ }
},
"subtasks": {
"placeholder": "Voeg een subtaak toe..."
diff --git a/public/locales/no/translation.json b/public/locales/no/translation.json
index 341d5e3..623c740 100644
--- a/public/locales/no/translation.json
+++ b/public/locales/no/translation.json
@@ -1206,7 +1206,15 @@
"instances": "gjentakende instanser"
},
"deferUntilFilter": "Utsett til",
- "deferUntil": ", utsett til"
+ "deferUntil": ", utsett til",
+ "extrasFilter": {
+ "isRecurring": "er Gjentakende",
+ "isOverdue": "er Forfalt",
+ "hasContent": "har Innhold",
+ "isDeferred": "er Utsatt",
+ "hasTags": "har Tagger",
+ "isAssignedToProject": "er Tildelt Prosjekt"
+ }
},
"subtasks": {
"placeholder": "Legg til en underoppgave..."
diff --git a/public/locales/pl/translation.json b/public/locales/pl/translation.json
index 0c1bdde..09c9b40 100644
--- a/public/locales/pl/translation.json
+++ b/public/locales/pl/translation.json
@@ -1206,7 +1206,15 @@
"instances": "powtarzające się instancje"
},
"deferUntilFilter": "Odłóż do",
- "deferUntil": ", odłóż do"
+ "deferUntil": ", odłóż do",
+ "extrasFilter": {
+ "isRecurring": "jest powtarzający się",
+ "isOverdue": "jest przeterminowany",
+ "hasContent": "ma zawartość",
+ "isDeferred": "jest odroczony",
+ "hasTags": "ma tagi",
+ "isAssignedToProject": "jest przypisany do projektu"
+ }
},
"subtasks": {
"placeholder": "Dodaj podzadanie..."
diff --git a/public/locales/pt/translation.json b/public/locales/pt/translation.json
index 461cfd0..6017622 100644
--- a/public/locales/pt/translation.json
+++ b/public/locales/pt/translation.json
@@ -1206,7 +1206,15 @@
"instances": "instâncias recorrentes"
},
"deferUntilFilter": "Aguardar Até",
- "deferUntil": ", aguardar até"
+ "deferUntil": ", aguardar até",
+ "extrasFilter": {
+ "isRecurring": "é Recorrente",
+ "isOverdue": "está Atrasado",
+ "hasContent": "tem Conteúdo",
+ "isDeferred": "está Adiado",
+ "hasTags": "tem Tags",
+ "isAssignedToProject": "está Atribuído ao Projeto"
+ }
},
"subtasks": {
"placeholder": "Adicionar uma subtarefa..."
diff --git a/public/locales/ro/translation.json b/public/locales/ro/translation.json
index fb248f8..ea5b017 100644
--- a/public/locales/ro/translation.json
+++ b/public/locales/ro/translation.json
@@ -1206,7 +1206,15 @@
"instances": "instanțe recurente"
},
"deferUntilFilter": "Amână până la",
- "deferUntil": ", amână până la"
+ "deferUntil": ", amână până la",
+ "extrasFilter": {
+ "isRecurring": "este Recurent",
+ "isOverdue": "este Întârziat",
+ "hasContent": "are Conținut",
+ "isDeferred": "este Amânat",
+ "hasTags": "are Etichete",
+ "isAssignedToProject": "este Atribuit Proiectului"
+ }
},
"subtasks": {
"placeholder": "Adaugă o subtask..."
diff --git a/public/locales/ru/translation.json b/public/locales/ru/translation.json
index fcead9a..3d9f272 100644
--- a/public/locales/ru/translation.json
+++ b/public/locales/ru/translation.json
@@ -1206,7 +1206,15 @@
"instances": "повторяющиеся экземпляры"
},
"deferUntilFilter": "Отложить до",
- "deferUntil": ", отложить до"
+ "deferUntil": ", отложить до",
+ "extrasFilter": {
+ "isRecurring": "является повторяющимся",
+ "isOverdue": "просрочено",
+ "hasContent": "имеет содержимое",
+ "isDeferred": "отложено",
+ "hasTags": "имеет теги",
+ "isAssignedToProject": "назначено на проект"
+ }
},
"subtasks": {
"placeholder": "Добавить подзадачу..."
diff --git a/public/locales/sl/translation.json b/public/locales/sl/translation.json
index 38c1f7d..c7e1c59 100644
--- a/public/locales/sl/translation.json
+++ b/public/locales/sl/translation.json
@@ -1206,7 +1206,15 @@
"instances": "ponavljajoče se instance"
},
"deferUntilFilter": "Odloži do",
- "deferUntil": ", odloži do"
+ "deferUntil": ", odloži do",
+ "extrasFilter": {
+ "isRecurring": "je ponavljajoče",
+ "isOverdue": "je zapadlo",
+ "hasContent": "ima vsebino",
+ "isDeferred": "je odloženo",
+ "hasTags": "ima oznake",
+ "isAssignedToProject": "je dodeljeno projektu"
+ }
},
"subtasks": {
"placeholder": "Dodaj podnalogo..."
diff --git a/public/locales/sv/translation.json b/public/locales/sv/translation.json
index 874fd1d..077fd36 100644
--- a/public/locales/sv/translation.json
+++ b/public/locales/sv/translation.json
@@ -1206,7 +1206,15 @@
"instances": "återkommande instanser"
},
"deferUntilFilter": "Skjut upp tills",
- "deferUntil": ", skjuta upp tills"
+ "deferUntil": ", skjuta upp tills",
+ "extrasFilter": {
+ "isRecurring": "är Återkommande",
+ "isOverdue": "är Försenad",
+ "hasContent": "har Innehåll",
+ "isDeferred": "är Utsatt",
+ "hasTags": "har Taggar",
+ "isAssignedToProject": "är Tilldelad Projekt"
+ }
},
"subtasks": {
"placeholder": "Lägg till en deluppgift..."
diff --git a/public/locales/tr/translation.json b/public/locales/tr/translation.json
index a8aae6e..4a9757d 100644
--- a/public/locales/tr/translation.json
+++ b/public/locales/tr/translation.json
@@ -1206,7 +1206,15 @@
"instances": "tekrarlayan örnekler"
},
"deferUntilFilter": "Ertele",
- "deferUntil": ", ertele"
+ "deferUntil": ", ertele",
+ "extrasFilter": {
+ "isRecurring": "Tekrarlayan",
+ "isOverdue": "Vadesi Geçmiş",
+ "hasContent": "İçerik Var",
+ "isDeferred": "Ertelenmiş",
+ "hasTags": "Etiket Var",
+ "isAssignedToProject": "Projeye Atanmış"
+ }
},
"subtasks": {
"placeholder": "Bir alt görev ekle..."
diff --git a/public/locales/ua/translation.json b/public/locales/ua/translation.json
index 6c65d88..d8f087f 100644
--- a/public/locales/ua/translation.json
+++ b/public/locales/ua/translation.json
@@ -1206,7 +1206,15 @@
"instances": "повторювані екземпляри"
},
"deferUntilFilter": "Відкласти до",
- "deferUntil": ", відкласти до"
+ "deferUntil": ", відкласти до",
+ "extrasFilter": {
+ "isRecurring": "є повторюваним",
+ "isOverdue": "прострочено",
+ "hasContent": "має вміст",
+ "isDeferred": "відкладено",
+ "hasTags": "має теги",
+ "isAssignedToProject": "призначено проекту"
+ }
},
"subtasks": {
"placeholder": "Додати підзадачу..."
diff --git a/public/locales/vi/translation.json b/public/locales/vi/translation.json
index 463cc56..e389a28 100644
--- a/public/locales/vi/translation.json
+++ b/public/locales/vi/translation.json
@@ -1206,7 +1206,15 @@
"instances": "các phiên bản lặp lại"
},
"deferUntilFilter": "Hoãn lại cho đến",
- "deferUntil": ", hoãn lại cho đến"
+ "deferUntil": ", hoãn lại cho đến",
+ "extrasFilter": {
+ "isRecurring": "là Lặp lại",
+ "isOverdue": "đã Quá hạn",
+ "hasContent": "có Nội dung",
+ "isDeferred": "đã Hoãn lại",
+ "hasTags": "có Thẻ",
+ "isAssignedToProject": "được Giao cho Dự án"
+ }
},
"subtasks": {
"placeholder": "Thêm một công việc phụ..."
diff --git a/public/locales/zh/translation.json b/public/locales/zh/translation.json
index eb1615e..8ec1cef 100644
--- a/public/locales/zh/translation.json
+++ b/public/locales/zh/translation.json
@@ -1206,7 +1206,15 @@
"instances": "循环实例"
},
"deferUntilFilter": "延迟到",
- "deferUntil": ",延迟到"
+ "deferUntil": ",延迟到",
+ "extrasFilter": {
+ "isRecurring": "是重复的",
+ "isOverdue": "已逾期",
+ "hasContent": "有内容",
+ "isDeferred": "已延迟",
+ "hasTags": "有标签",
+ "isAssignedToProject": "已分配给项目"
+ }
},
"subtasks": {
"placeholder": "添加子任务..."
diff --git a/webpack.config.js b/webpack.config.js
index eeb54b4..29de4e9 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -5,6 +5,14 @@ const CopyWebpackPlugin = require('copy-webpack-plugin');
const webpack = require('webpack');
const isDevelopment = process.env.NODE_ENV !== 'production';
+const frontendPort = parseInt(process.env.FRONTEND_PORT || '8080', 10);
+const frontendHost = process.env.FRONTEND_HOST || '0.0.0.0';
+const backendUrl = process.env.BACKEND_URL || 'http://localhost:3002';
+const frontendUrl = new URL(
+ process.env.FRONTEND_ORIGIN || `http://localhost:${frontendPort}`
+);
+const frontendOrigin = frontendUrl.origin;
+const frontendCookieDomain = frontendUrl.hostname;
module.exports = {
entry: './frontend/index.tsx',
@@ -29,22 +37,22 @@ module.exports = {
},
hot: isDevelopment,
watchFiles: isDevelopment ? ['frontend/**/*'] : [],
- port: 8080,
- host: '0.0.0.0',
+ port: frontendPort,
+ host: frontendHost,
historyApiFallback: true,
proxy: [
{
context: ['/api', '/locales'],
- target: 'http://localhost:3002',
+ target: backendUrl,
changeOrigin: true,
secure: false,
- cookieDomainRewrite: 'localhost',
+ cookieDomainRewrite: frontendCookieDomain,
headers: {
'Access-Control-Allow-Origin': '*',
},
onProxyRes: function (proxyRes, req, res) {
proxyRes.headers['Access-Control-Allow-Origin'] =
- 'http://localhost:8080';
+ frontendOrigin;
proxyRes.headers['Access-Control-Allow-Credentials'] =
'true';
},