From 41495cdabe1de79b9e471cc154987bcfe9b83a48 Mon Sep 17 00:00:00 2001 From: Marcello Fitton <106866560+angelplusultra@users.noreply.github.com> Date: Wed, 29 Apr 2026 12:05:46 -0700 Subject: [PATCH] feat: Scheduled Jobs (#5322) * initialize * expand tool result text limit | add syntax highlighting and json formatting to tool result rendering * fix onError jsdoc * lint * fix unread icon * route protection * improve form handling for NewJobModal * safeJsonParse * remove unneeded comments * remove trycatch * add truncateText helper * add explicit fallback value tos safeJsonParse * add shared cron constant and helpers * reduce frontend indirection * use isLight to compute syntax highlighting theme * remove dead code * remove forJob and make job limit to 50 * create recomputeNextRunAt helper method * add comment about nextRunAt recomputation * add job queue and concurrency control to scheduled jobs * use p-queue * change default max concurrent value to 1 * add comment explaining internal scheduling system * add recomputeNextRunAt on boot * add generated documents to run details * Modify toolsOverride functionality where no tools selected means no tools are given to the agent add a select all/deselect all toggle button for easily selecting all tools in the cerate job form * create usePolling hook * add polling to scheduled jobs and scheduled job runs pages * add cron generation feature in job form * remove cron generation feature | add cron builder feature | add max active scheduled jobs limit * set MAX_ACTIVE to null * replace hour and minute input fields with input with type time * simplify * organize components * move components to bottom of page component * change Generated Documents to Generated Files * add i18n to cronstrue * add i18n * add type="button" to button elements * refactor fileSource retrieval logic * one scheduled job run can have status "running" * add protection of file retrieveal from scheduled job in multiuser mode * fix comments * make job status default to queued * add queued status * fix bug with result trace rendering * store timeout ref and clearTimeout once race settles * remove unneeded handlerPromise tracking * move imports to top level * refactor hardcoded paths to path resolve functions * implement new job form design * simplify * fix button styles * fix runJob bug * implement styles for scheduled jobs page * apply dark mode figma styles * delete unused translation key * implement light mode for new new job modal, run history, and run details * lint * fix light mode scroll bar in tool call card * adjust table header contrast * fix type in subtitle * kill workers when job is in-flight before deleting job * add border-none to buttons * change locale time to iso string * import BackgroundService module level | instatiate backgroundService singltone once and reuse across handlers * add p-queue, @breejs/later and cron-validate as core deps * parse cron expression to a builder state once * add theme to day buttons in cron builder * fix stale tools selection caption * flip popover when popover clips screen height * make ScheduleJob.trigger() await the run insertion | disable run now button if job is in flight * regen table * refactor generated file card * refactor frontend * remove logs * major refactor for tool picking, fix bree/later bug * combine action endpoints, move contine to method * fix unoptimized query with include + take + order * fix dangerous use, refactor job to utils * add copy content to text response * improve notification system subscription for browser * remove unused translations * prevent gen-file cleanup job from deleting active job file generated references * rich text copy * Scheduled Jobs: Translations (#5482) * add locales for scheduled jobs * i18n --------- Co-authored-by: Timothy Carambat * add config flag with UI notice * update README * telemetry datapoints * Always use UTC on backend, convert to local in frontend * fix tz render * Add job killing * cleanup thinking text in job notifications and break out reasoning in response text. Also hide zero metrics since that is useless * Port generatedFile schema to the normalized workspace chat `outputs` file format so porting to thread is simple and implem between chats <> jobs is 1:1 * what the fuck * compiled bug * fixed thinking oddity in complied frontend * supress multi-toast * fix duration call * Revert "fix duration call" This reverts commit 0491bc71f4223e65ea4046561b15b268fefb8da2. * revert and reapply fix --------- Co-authored-by: Timothy Carambat --- README.md | 1 + frontend/package.json | 1 + .../src/components/PrivateRoute/index.jsx | 19 + .../src/components/SettingsSidebar/index.jsx | 6 + .../Actions/RenderMetrics/index.jsx | 16 +- frontend/src/hooks/useCopyText.js | 9 +- frontend/src/hooks/usePolling.js | 52 ++ frontend/src/hooks/useWebPushNotifications.js | 26 +- frontend/src/index.css | 30 ++ frontend/src/locales/ar/common.js | 158 ++++++ frontend/src/locales/ca/common.js | 160 ++++++ frontend/src/locales/cs/common.js | 161 ++++++ frontend/src/locales/da/common.js | 161 ++++++ frontend/src/locales/de/common.js | 163 ++++++ frontend/src/locales/en/common.js | 159 ++++++ frontend/src/locales/es/common.js | 160 ++++++ frontend/src/locales/et/common.js | 161 ++++++ frontend/src/locales/fa/common.js | 161 ++++++ frontend/src/locales/fr/common.js | 162 ++++++ frontend/src/locales/he/common.js | 158 ++++++ frontend/src/locales/it/common.js | 163 ++++++ frontend/src/locales/ja/common.js | 159 ++++++ frontend/src/locales/ko/common.js | 159 ++++++ frontend/src/locales/lt/common.js | 161 ++++++ frontend/src/locales/lv/common.js | 161 ++++++ frontend/src/locales/nl/common.js | 162 ++++++ frontend/src/locales/pl/common.js | 161 ++++++ frontend/src/locales/pt_BR/common.js | 160 ++++++ frontend/src/locales/ro/common.js | 162 ++++++ frontend/src/locales/ru/common.js | 161 ++++++ frontend/src/locales/tr/common.js | 160 ++++++ frontend/src/locales/vn/common.js | 160 ++++++ frontend/src/locales/zh/common.js | 158 ++++++ frontend/src/locales/zh_TW/common.js | 158 ++++++ frontend/src/main.jsx | 30 ++ frontend/src/models/scheduledJobs.js | 127 +++++ .../JobFormModal/CronBuilder.jsx | 196 +++++++ .../JobFormModal/FormActions.jsx | 28 + .../JobFormModal/JobDescription.jsx | 57 ++ .../JobFormModal/JobSchedule.jsx | 89 ++++ .../JobFormModal/ToolsSelector.jsx | 352 ++++++++++++ .../ScheduledJobs/JobFormModal/index.jsx | 163 ++++++ .../ScheduledJobs/RunDetailPage.jsx | 434 +++++++++++++++ .../ScheduledJobs/RunHistoryPage.jsx | 165 ++++++ .../components/CollapsibleSection.jsx | 71 +++ .../components/GeneratedFileCard.jsx | 134 +++++ .../ScheduledJobs/components/JobRow.jsx | 107 ++++ .../ScheduledJobs/components/RunRow.jsx | 101 ++++ .../ScheduledJobs/components/StatusBadge.jsx | 51 ++ .../ScheduledJobs/components/ToolCallCard.jsx | 144 +++++ .../GeneralSettings/ScheduledJobs/index.jsx | 242 +++++++++ .../ScheduledJobs/utils/cron.js | 277 ++++++++++ frontend/src/utils/clipboard.js | 25 + frontend/src/utils/numbers.js | 29 + frontend/src/utils/paths.js | 9 + frontend/yarn.lock | 5 + server/.env.example | 10 +- server/endpoints/agentFileServer.js | 142 +++-- server/endpoints/scheduledJobs.js | 376 +++++++++++++ server/index.js | 2 + server/jobs/cleanup-generated-files.js | 60 ++- server/jobs/helpers/index.js | 20 + server/jobs/helpers/scheduled-job-helper.js | 111 ++++ server/jobs/run-scheduled-job.js | 157 ++++++ server/models/scheduledJob.js | 499 ++++++++++++++++++ server/models/scheduledJobRun.js | 362 +++++++++++++ server/models/telemetry.js | 1 + server/models/workspace.js | 23 + server/package.json | 3 + .../20260423191158_init/migration.sql | 29 + server/prisma/schema.prisma | 28 + server/utils/BackgroundWorkers/index.js | 227 ++++++++ server/utils/PushNotifications/index.js | 16 +- server/utils/agents/aibitat/index.js | 22 + server/utils/agents/ephemeral.js | 33 +- server/yarn.lock | 6 +- 76 files changed, 9031 insertions(+), 101 deletions(-) create mode 100644 frontend/src/hooks/usePolling.js create mode 100644 frontend/src/models/scheduledJobs.js create mode 100644 frontend/src/pages/GeneralSettings/ScheduledJobs/JobFormModal/CronBuilder.jsx create mode 100644 frontend/src/pages/GeneralSettings/ScheduledJobs/JobFormModal/FormActions.jsx create mode 100644 frontend/src/pages/GeneralSettings/ScheduledJobs/JobFormModal/JobDescription.jsx create mode 100644 frontend/src/pages/GeneralSettings/ScheduledJobs/JobFormModal/JobSchedule.jsx create mode 100644 frontend/src/pages/GeneralSettings/ScheduledJobs/JobFormModal/ToolsSelector.jsx create mode 100644 frontend/src/pages/GeneralSettings/ScheduledJobs/JobFormModal/index.jsx create mode 100644 frontend/src/pages/GeneralSettings/ScheduledJobs/RunDetailPage.jsx create mode 100644 frontend/src/pages/GeneralSettings/ScheduledJobs/RunHistoryPage.jsx create mode 100644 frontend/src/pages/GeneralSettings/ScheduledJobs/components/CollapsibleSection.jsx create mode 100644 frontend/src/pages/GeneralSettings/ScheduledJobs/components/GeneratedFileCard.jsx create mode 100644 frontend/src/pages/GeneralSettings/ScheduledJobs/components/JobRow.jsx create mode 100644 frontend/src/pages/GeneralSettings/ScheduledJobs/components/RunRow.jsx create mode 100644 frontend/src/pages/GeneralSettings/ScheduledJobs/components/StatusBadge.jsx create mode 100644 frontend/src/pages/GeneralSettings/ScheduledJobs/components/ToolCallCard.jsx create mode 100644 frontend/src/pages/GeneralSettings/ScheduledJobs/index.jsx create mode 100644 frontend/src/pages/GeneralSettings/ScheduledJobs/utils/cron.js create mode 100644 frontend/src/utils/clipboard.js create mode 100644 server/endpoints/scheduledJobs.js create mode 100644 server/jobs/helpers/scheduled-job-helper.js create mode 100644 server/jobs/run-scheduled-job.js create mode 100644 server/models/scheduledJob.js create mode 100644 server/models/scheduledJobRun.js create mode 100644 server/prisma/migrations/20260423191158_init/migration.sql diff --git a/README.md b/README.md index b44a5cf1..1116008f 100644 --- a/README.md +++ b/README.md @@ -55,6 +55,7 @@ AnythingLLM supports multiple users as well where you can control the access and ## Cool features of AnythingLLM +- [Scheduled Tasks](https://docs.anythingllm.com/scheduled-jobs/overview) - [Intelligent Skill Selection](https://docs.anythingllm.com/agent/intelligent-tool-selection) Enable **unlimited** tools for your models while reducing token usage by up to 80% per query - [**No-code AI Agent builder**](https://docs.anythingllm.com/agent-flows/overview) - [**Full MCP-compatibility**](https://docs.anythingllm.com/mcp-compatibility/overview) diff --git a/frontend/package.json b/frontend/package.json index 071bc266..b0634148 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -17,6 +17,7 @@ "@mintplex-labs/piper-tts-web": "^1.0.4", "@phosphor-icons/react": "^2.1.7", "@tremor/react": "^3.15.1", + "cronstrue": "^2.50.0", "dompurify": "^3.0.8", "file-saver": "^2.0.5", "he": "^1.2.0", diff --git a/frontend/src/components/PrivateRoute/index.jsx b/frontend/src/components/PrivateRoute/index.jsx index 6220ecff..9cd91105 100644 --- a/frontend/src/components/PrivateRoute/index.jsx +++ b/frontend/src/components/PrivateRoute/index.jsx @@ -126,6 +126,25 @@ export function ManagerRoute({ Component }) { ); } +// Allows access only in single user mode — redirects to home in multi-user mode +export function SingleUserRoute({ Component }) { + const { isAuthd, shouldRedirectToOnboarding, multiUserMode } = + useIsAuthenticated(); + if (isAuthd === null) return ; + + if (shouldRedirectToOnboarding) { + return ; + } + + return isAuthd && !multiUserMode ? ( + + + + ) : ( + + ); +} + export default function PrivateRoute({ Component }) { const { isAuthd, shouldRedirectToOnboarding } = useIsAuthenticated(); if (isAuthd === null) return ; diff --git a/frontend/src/components/SettingsSidebar/index.jsx b/frontend/src/components/SettingsSidebar/index.jsx index e61de158..bcfa0136 100644 --- a/frontend/src/components/SettingsSidebar/index.jsx +++ b/frontend/src/components/SettingsSidebar/index.jsx @@ -395,6 +395,12 @@ const SidebarOptions = ({ user = null, t }) => ( flex: true, roles: ["admin"], }, + { + btnText: t("settings.scheduled-jobs"), + href: paths.settings.scheduledJobs(), + flex: true, + hidden: !!user, + }, { btnText: t("settings.api-keys"), href: paths.settings.apiKeys(), diff --git a/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/HistoricalMessage/Actions/RenderMetrics/index.jsx b/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/HistoricalMessage/Actions/RenderMetrics/index.jsx index 0865ef22..ea6dac53 100644 --- a/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/HistoricalMessage/Actions/RenderMetrics/index.jsx +++ b/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/HistoricalMessage/Actions/RenderMetrics/index.jsx @@ -1,25 +1,11 @@ import { formatDateTimeAsMoment } from "@/utils/directories"; -import { numberWithCommas } from "@/utils/numbers"; +import { formatDuration, numberWithCommas } from "@/utils/numbers"; import React, { useEffect, useState, useContext } from "react"; import { isMobile } from "react-device-detect"; const MetricsContext = React.createContext(); const SHOW_METRICS_KEY = "anythingllm_show_chat_metrics"; const SHOW_METRICS_EVENT = "anythingllm_show_metrics_change"; -/** - * @param {number} duration - duration in milliseconds - * @returns {string} - */ -function formatDuration(duration) { - try { - return duration < 1 - ? `${(duration * 1000).toFixed(0)}ms` - : `${duration.toFixed(3)}s`; - } catch { - return ""; - } -} - /** * Format the output TPS to a string * @param {number} outputTps - output TPS diff --git a/frontend/src/hooks/useCopyText.js b/frontend/src/hooks/useCopyText.js index 2f17d6c1..526fcdbc 100644 --- a/frontend/src/hooks/useCopyText.js +++ b/frontend/src/hooks/useCopyText.js @@ -1,4 +1,5 @@ import { THOUGHT_REGEX_COMPLETE } from "@/components/WorkspaceChat/ChatContainer/ChatHistory/ThoughtContainer"; +import { copyMarkdownAsRichText } from "@/utils/clipboard"; import { useState } from "react"; export default function useCopyText(delay = 2500) { @@ -8,11 +9,9 @@ export default function useCopyText(delay = 2500) { // Filter thinking blocks from the content if they exist const nonThinkingContent = content.replace(THOUGHT_REGEX_COMPLETE, ""); - navigator?.clipboard?.writeText(nonThinkingContent); - setCopied(nonThinkingContent); - setTimeout(() => { - setCopied(false); - }, delay); + await copyMarkdownAsRichText(nonThinkingContent); + setCopied(true); + setTimeout(() => setCopied(false), delay); }; return { copyText, copied }; diff --git a/frontend/src/hooks/usePolling.js b/frontend/src/hooks/usePolling.js new file mode 100644 index 00000000..76f59281 --- /dev/null +++ b/frontend/src/hooks/usePolling.js @@ -0,0 +1,52 @@ +import { useEffect, useRef } from "react"; + +/** + * Polls a callback on an interval, but only while the tab is visible. + * Automatically pauses when the user switches away and resumes on return. + * + * @param {() => void | Promise} callback - The function to invoke on each tick + * @param {number} intervalMs - Polling interval in milliseconds + * @param {boolean} [enabled=true] - When false, polling is suspended + */ +export default function usePolling(callback, intervalMs, enabled = true) { + const savedCallback = useRef(callback); + + useEffect(() => { + savedCallback.current = callback; + }, [callback]); + + useEffect(() => { + if (!enabled || !intervalMs) return; + + let timerId = null; + + const start = () => { + if (timerId) return; + timerId = setInterval(() => savedCallback.current(), intervalMs); + }; + + const stop = () => { + if (!timerId) return; + clearInterval(timerId); + timerId = null; + }; + + const handleVisibilityChange = () => { + if (document.visibilityState === "visible") { + // Fire immediately on return so the UI feels fresh, then resume interval + savedCallback.current(); + start(); + } else { + stop(); + } + }; + + if (document.visibilityState === "visible") start(); + document.addEventListener("visibilitychange", handleVisibilityChange); + + return () => { + stop(); + document.removeEventListener("visibilitychange", handleVisibilityChange); + }; + }, [intervalMs, enabled]); +} diff --git a/frontend/src/hooks/useWebPushNotifications.js b/frontend/src/hooks/useWebPushNotifications.js index e712e9e8..72b10925 100644 --- a/frontend/src/hooks/useWebPushNotifications.js +++ b/frontend/src/hooks/useWebPushNotifications.js @@ -19,20 +19,28 @@ function log(message, ...args) { /** * Subscribes to push notifications for the current client - can be called multiple times without re-subscribing * or generating infinite tokens. - * @returns {void} + * @returns {Promise} */ -export async function subscribeToPushNotifications() { +export async function subscribeToPushNotifications(askToEnable = true) { try { if (!("serviceWorker" in navigator) || !("PushManager" in window)) { log("Push notifications not supported"); return; } - // Check current permission status - const permission = await Notification.requestPermission(); - if (permission !== "granted") { - log("Notification permission not granted"); - return; + if (askToEnable) { + // Check current permission status + const permission = await Notification.requestPermission(); + if (permission !== "granted") { + log("Notification permission not granted"); + return; + } + } else { + const permission = Notification.permission; + if (permission !== "granted") { + log("Notification permission not granted"); + return; + } } const publicKey = await fetch(PUSH_PUBKEY_URL, { headers: baseHeaders() }) @@ -107,9 +115,9 @@ export async function subscribeToPushNotifications() { * Hook that registers a service worker for push notifications. * @returns {void} */ -export default function useWebPushNotifications() { +export default function useWebPushNotifications(askToEnable = true) { useEffect(() => { - subscribeToPushNotifications(); + subscribeToPushNotifications(askToEnable); }, []); } diff --git a/frontend/src/index.css b/frontend/src/index.css index ccd0d575..1e51d02c 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -945,6 +945,36 @@ does not extend the close button beyond the viewport. */ background-color: #cccccc; } +/* Scoped to the scheduled jobs tool call card — the default white-scrollbar + is tuned for dark surfaces and looks harsh over light backgrounds. */ +.tool-call-scrollbar { + scrollbar-width: thin; +} + +[data-theme="light"] .tool-call-scrollbar { + scrollbar-color: #cbd5e1 transparent; +} + +[data-theme="light"] .tool-call-scrollbar::-webkit-scrollbar { + width: 3px; + height: 3px; + background-color: transparent; +} + +[data-theme="light"] .tool-call-scrollbar::-webkit-scrollbar-track { + background-color: transparent; +} + +[data-theme="light"] .tool-call-scrollbar::-webkit-scrollbar-thumb { + background-color: #cbd5e1; + border-radius: 4px; + border: 2px solid transparent; +} + +[data-theme="light"] .tool-call-scrollbar::-webkit-scrollbar-thumb:hover { + background-color: #94a3b8; +} + /* Recharts rendering styles */ .recharts-text > * { fill: #fff; diff --git a/frontend/src/locales/ar/common.js b/frontend/src/locales/ar/common.js index b6343dec..6bd5c456 100644 --- a/frontend/src/locales/ar/common.js +++ b/frontend/src/locales/ar/common.js @@ -107,6 +107,7 @@ const TRANSLATIONS = { "available-channels": { telegram: "تليجرام", }, + "scheduled-jobs": "المهام المجدولة", }, login: { "multi-user": { @@ -1485,6 +1486,163 @@ const TRANSLATIONS = { unknown: "غير معروف", }, }, + scheduledJobs: { + title: "المهام المجدولة", + enableNotifications: "قم بتمكين إشعارات المتصفح لنتائج البحث عن وظائف.", + description: + "إنشاء مهام ذكاء اصطناعي متكررة تعمل وفق جدول زمني. تقوم كل مهمة بتشغيل استعلام مع أدوات اختيارية، وتخزين النتيجة للمراجعة.", + newJob: "وظيفة جديدة", + loading: "جاري التحميل...", + emptyTitle: "لا توجد مهام محددة حتى الآن.", + emptySubtitle: "ابدأ بإنشاء واحد.", + table: { + name: "الاسم", + schedule: "الجدول الزمني", + status: "الحالة", + lastRun: "آخر مرة", + nextRun: "الرحلة التالية", + actions: "الإجراءات", + }, + confirmDelete: "هل أنت متأكد من أنك تريد حذف هذه المهمة المجدولة؟", + toast: { + deleted: "تم حذف الوظيفة", + triggered: "تم تنفيذ المهمة بنجاح.", + triggerFailed: "لم يتم تشغيل المهمة.", + triggerSkipped: "تم بالفعل البدء في تنفيذ هذا المشروع.", + killed: "تم إيقاف الوظيفة بنجاح.", + killFailed: "فشل في إيقاف العمل", + }, + row: { + neverRun: "لا تفرط", + viewRuns: "تسجيلات", + runNow: "ابدأ الآن", + enable: "تمكين", + disable: "تعطيل", + edit: "تحرير", + delete: "حذف", + }, + modal: { + titleEdit: "تعديل المهمة المجدولة", + titleNew: "وظيفة جديدة مُجدولة", + nameLabel: "الاسم", + namePlaceholder: "على سبيل المثال: ملخص الأخبار اليومية", + promptLabel: "طلب", + promptPlaceholder: "التوجيه بتشغيل البرنامج في كل مرة يتم تنفيذها...", + scheduleLabel: "الجدول الزمني", + modeBuilder: "مقاول", + modeCustom: "مخصص", + cronPlaceholder: "تعبير الوقت (مثل 0 9 * * *)", + currentSchedule: "الجدول الزمني الحالي:", + toolsLabel: "الأدوات (اختياري)", + toolsDescription: + "حدد الأدوات التي يمكن لهذه المهمة استخدامها. إذا لم يتم تحديد أي أدوات، فستتم إكمال المهمة بدون استخدام أي أدوات.", + toolsSearch: "البحث", + toolsNoResults: "لا توجد أدوات مطابقة", + required: "مطلوب", + requiredFieldsBanner: "يرجى ملء جميع الحقول المطلوبة لإنشاء الوظيفة.", + cancel: "إلغاء", + saving: "حفظ...", + updateJob: "تحديث الوظيفة", + createJob: "إنشاء وظيفة", + jobUpdated: "تم تحديث الوظيفة", + jobCreated: "تم إنشاء وظيفة", + }, + builder: { + fallbackWarning: + 'لا يمكن تعديل هذا النص بصريًا. إذا كنت ترغب في الاحتفاظ به، فاختر "مخصص". وإلا، يمكنك تغيير أي شيء أسفله لاستبداله.', + run: "شغل", + frequency: { + minute: "كل دقيقة", + hour: "ساعي", + day: "يوميًا", + week: "أسبوعي", + month: "شهريًا", + }, + every: "كل", + minuteOne: "دقيقة واحدة", + minuteOther: "{{count}} دقيقة", + atMinute: "في الدقيقة", + pastEveryHour: "كل ساعة", + at: "في", + on: "على", + onDay: "في يوم", + ofEveryMonth: "في كل شهر", + weekdays: { + sun: "الشمس", + mon: "الاثنين", + tue: "الثلاثاء", + wed: "الخميس", + thu: "الخميس", + fri: "يوم الجمعة", + sat: "السبت", + }, + }, + runHistory: { + back: "العودة إلى الوظائف", + title: "سجل التشغيل: {{name}}", + schedule: "الجدول الزمني:", + emptyTitle: "لم يتم تحقيق أي تقدم حتى الآن في هذا المشروع.", + emptySubtitle: "ابدأ تنفيذ المهمة الآن، وشاهد نتائجها.", + runNow: "ابدأ الآن", + table: { + status: "الحالة", + started: "بدأ", + duration: "المدة", + error: "خطأ", + }, + stopJob: "إيقاف العمل", + }, + runDetail: { + loading: "تحميل تفاصيل الجولة...", + notFound: "لم يتم العثور على الأمر.", + back: "الرجوع", + unknownJob: "وظيفة غير محددة", + runHeading: "{{name}} — تشغيل المهمة رقم {{id}}", + duration: "المدة: {{value}}", + continueInThread: "تابع في هذا الموضوع/النقاش", + creating: "إنشاء...", + threadFailed: "فشل في إنشاء سلسلة (thread).", + sections: { + prompt: "طلب", + error: "خطأ", + thinking: "الأفكار ({{count}})", + toolCalls: "استدعاء الأدوات ({{count}})", + files: "الملفات ({{count}})", + response: "الرد", + metrics: "المقاييس", + }, + metrics: { + promptTokens: "رموز التذكير:", + completionTokens: "رموز الإكمال:", + }, + stopJob: "إيقاف التوظيف", + killing: "التوقف...", + }, + toolCall: { + arguments: "الحجج:", + showResult: "اعرض النتيجة", + hideResult: "إخفاء النتيجة", + }, + file: { + unknown: "ملف غير معروف", + download: "تنزيل", + downloadFailed: "فشل تنزيل الملف.", + types: { + powerpoint: "برنامج باوربوينت", + pdf: "ملف PDF", + word: "ملف مستند (Word)", + spreadsheet: "جدول البيانات", + generic: "الملف", + }, + }, + status: { + completed: "تمت", + failed: "فشل", + timed_out: "انتهت المدة المحددة", + running: "الجري", + queued: "في قائمة الانتظار", + }, + }, }; export default TRANSLATIONS; diff --git a/frontend/src/locales/ca/common.js b/frontend/src/locales/ca/common.js index 79d61cc9..d001bee3 100644 --- a/frontend/src/locales/ca/common.js +++ b/frontend/src/locales/ca/common.js @@ -116,6 +116,7 @@ const TRANSLATIONS = { "available-channels": { telegram: "Telegram", }, + "scheduled-jobs": "Feines programades", }, login: { "multi-user": { @@ -1534,6 +1535,165 @@ const TRANSLATIONS = { }, }, }, + scheduledJobs: { + title: "Feines programades", + enableNotifications: + "Activa les notificacions del navegador per als resultats de la cerca de feina.", + description: + "Crea tasques d'IA recurrents que s'executen segons un horari. Cada tasca executa una consulta amb eines opcionals i guarda el resultat per a la seva revisió.", + newJob: "Nou lloc de treball", + loading: "Cargant...", + emptyTitle: "No hi ha tasques programades.", + emptySubtitle: "Creeu un per començar.", + table: { + name: "Nom", + schedule: "Horari", + status: "Estat", + lastRun: "Última sortida", + nextRun: "Proper següent", + actions: "Accions", + }, + confirmDelete: "Estàs segur que vols eliminar aquesta tasca programada?", + toast: { + deleted: "Emple o eliminat", + triggered: "La tasca s'ha completat amb èxit.", + triggerFailed: "No s'ha pogut iniciar la tasca.", + triggerSkipped: "Ja s'ha iniciat la feina per a aquest projecte.", + killed: "La feina s'ha completat amb èxit.", + killFailed: "No va poder evitar que es fes la feina.", + }, + row: { + neverRun: "Mai no corres", + viewRuns: "Horaris de funcionament", + runNow: "Corre ara", + enable: "Activar", + disable: "Desactivar", + edit: "Editar", + delete: "Eliminar", + }, + modal: { + titleEdit: "Modificar tasca programada", + titleNew: "Nova tasca programada", + nameLabel: "Nom", + namePlaceholder: 'Per exemple, "Resum diari de notícies"', + promptLabel: "Indicació", + promptPlaceholder: "L'instrucció per executar-se en cada execució...", + scheduleLabel: "Horari", + modeBuilder: "Constructor", + modeCustom: "Personalitzat", + cronPlaceholder: "Execució de la tasca (per exemple, 0 9 * * *)", + currentSchedule: "Horari actual:", + toolsLabel: "Eines (opcional)", + toolsDescription: + "Seleccioneu quins eines d'agent pot utilitzar aquest treball. Si cap, seleccioneu, el treball es realitzarà sense cap eina.", + toolsSearch: "Cerca", + toolsNoResults: "No hi ha cap eina que coincideixi.", + required: "Obligatori", + requiredFieldsBanner: + "Si us plau, compliu tots els camps obligatoris per crear l'oferta de treball.", + cancel: "Cancel·lar", + saving: "Guardant...", + updateJob: "Actualitzar lloc de treball", + createJob: "Crear un lloc de treball", + jobUpdated: "Pàgina actualitzada", + jobCreated: "Creació d'un lloc de treball", + }, + builder: { + fallbackWarning: + 'Aquesta expressió no es pot modificar visualment. Per mantenir-la, utilitzeu l\'opció "Personalitzat". Si voleu, podeu modificar qualsevol element de sota per sobrescribir-la.', + run: "Corre", + frequency: { + minute: "cada minut", + hour: "per hora", + day: "diari", + week: "setmanal", + month: "mensal", + }, + every: "Cada", + minuteOne: "1 minut", + minuteOther: "{{count}} minuts", + atMinute: "En el moment", + pastEveryHour: "cada hora", + at: "A", + on: "Sobre", + onDay: "En un dia", + ofEveryMonth: "de cada mes", + weekdays: { + sun: "Sol", + mon: "Mon", + tue: "Dimarts", + wed: "Dijous", + thu: "Dijous", + fri: "Divendres", + sat: "Dissabte", + }, + }, + runHistory: { + back: "Torna a les feines", + title: "Històric de curses: {{name}}", + schedule: "Horari:", + emptyTitle: "Aún no hi ha candidats per a aquesta posició.", + emptySubtitle: "Inicia la tasca ara i consulta els resultats.", + runNow: "Comença ara", + table: { + status: "Estat", + started: "Comencat", + duration: "Durada", + error: "Error", + }, + stopJob: "Aturar la feina", + }, + runDetail: { + loading: "Càrrec detalls de la sessió...", + notFound: "No s'ha trobat la sortida.", + back: "Cap endavant", + unknownJob: "Posició sense especificar", + runHeading: "{{name}} — Executar la tasca {{id}}", + duration: "Durada: {{value}}", + continueInThread: "Segueix la discussió", + creating: "Creant...", + threadFailed: "No s'ha pogut crear el fil.", + sections: { + prompt: "Indicació", + error: "Error", + thinking: "Pensaments ({{count}})", + toolCalls: "Crides a les eines ({{count}})", + files: "Fitxers ({{count}})", + response: "Resposta", + metrics: "Mètriques", + }, + metrics: { + promptTokens: "Tokens de desencadenament:", + completionTokens: "Tokens de finalització:", + }, + stopJob: "Finalitzar feina", + killing: "Aturar...", + }, + toolCall: { + arguments: "Argumentacions:", + showResult: "Mostrar resultat", + hideResult: "Ocultar resultat", + }, + file: { + unknown: "Fitxer desconegut", + download: "Descarregar", + downloadFailed: "No s'ha pogut descarregar el fitxer.", + types: { + powerpoint: "PowerPoint", + pdf: "Document en format PDF", + word: "Document de text", + spreadsheet: "Fulla de càlcul", + generic: "Fitxer", + }, + }, + status: { + completed: "Complet", + failed: "Fallit", + timed_out: "Ha expirat el temps", + running: "Correu", + queued: "En la fila d'espera", + }, + }, }; export default TRANSLATIONS; diff --git a/frontend/src/locales/cs/common.js b/frontend/src/locales/cs/common.js index dd400ec7..d3a21fc7 100644 --- a/frontend/src/locales/cs/common.js +++ b/frontend/src/locales/cs/common.js @@ -116,6 +116,7 @@ const TRANSLATIONS = { "available-channels": { telegram: "Telegram", }, + "scheduled-jobs": "Naplánované úlohy", }, login: { "multi-user": { @@ -1507,6 +1508,166 @@ const TRANSLATIONS = { unknown: "Neznámé", }, }, + scheduledJobs: { + title: "Naplánované úkoly", + enableNotifications: + "Povolte oznámení v prohlížeči pro výsledky hledání práce", + description: + "Vytvořte opakující se úkoly s umělou inteligencí, které se spouští podle stanoveného harmonogramu. Každý úkol provede zadaný požadavek s volitelnými nástroji a uloží výsledek pro pozdější kontrolu.", + newJob: "Nová pracovní pozice", + loading: "Načítání...", + emptyTitle: "V současné době nejsou naplánovány žádné úkoly.", + emptySubtitle: "Vytvořte si jeden, abyste začali.", + table: { + name: "Jméno", + schedule: "Harmonogram", + status: "Stav", + lastRun: "Poslední běh", + nextRun: "Další běh", + actions: "Akce", + }, + confirmDelete: "Jste si jisti, že chcete tento naplánovaný úkol smazat?", + toast: { + deleted: "Práce smazána", + triggered: "Úkol byl úspěšně spuštěn.", + triggerFailed: "Nepodařilo se spustit danou úlohu.", + triggerSkipped: "Práce na tomto projektu již probíhá.", + killed: "Práce byla úspěšně ukončena", + killFailed: "Nebylo možné zastavit pracovní činnost.", + }, + row: { + neverRun: "Nikdy nespěchejte", + viewRuns: "Prohlídky", + runNow: "Začněte hned", + enable: "Povolit", + disable: "Vypnout", + edit: "Upravit", + delete: "Smazat", + }, + modal: { + titleEdit: "Upravit naplánovanou úlohu", + titleNew: "Nový naplánovaný úkol", + nameLabel: "Jméno", + namePlaceholder: "např. Denní přehled novinek", + promptLabel: "Výzva", + promptPlaceholder: "Instrukce k provedení při každém spuštění...", + scheduleLabel: "Harmonogram", + modeBuilder: "Stavební firma", + modeCustom: "Na míru vyrobené", + cronPlaceholder: + "Výraz pro vyjadřování časového intervalu (např. 0 9 * * *)", + currentSchedule: "Současný harmonogram:", + toolsLabel: "Nářadí (volitelné)", + toolsDescription: + "Vyberte, které nástroje lze pro tuto úlohu použít. Pokud žádný není vybrán, úloha bude spuštěna bez použití jakýchkoli nástrojů.", + toolsSearch: "Vyhledávání", + toolsNoResults: "Žádný nástroj neodpovídá", + required: "Nutné", + requiredFieldsBanner: + "Prosím, vyplňte všechny povinné pole, abyste mohli vytvořit inzerát.", + cancel: "Zrušit", + saving: "Úspora...", + updateJob: "Aktualizovat pracovní pozici", + createJob: "Vytvořte pracovní pozici", + jobUpdated: "Pozice byla aktualizována", + jobCreated: "Vytvořena pozice", + }, + builder: { + fallbackWarning: + 'Tento výraz nelze upravit vizuálně. Pokud jej chcete zachovat, přejděte do režimu "Custom". Jinak můžete změnit cokoliv níže, abyste jej nahradili.', + run: "Běhat", + frequency: { + minute: "každou minutu", + hour: "za hodinu", + day: "denně", + week: "každý týden", + month: "měsíční", + }, + every: "Každý", + minuteOne: "1 minuta", + minuteOther: "{{count}} minut", + atMinute: "V minutě", + pastEveryHour: "v každou hodinu", + at: "V", + on: "Na", + onDay: "Jednoho dne", + ofEveryMonth: "každého měsíce", + weekdays: { + sun: "Slunce", + mon: "Pondělí", + tue: "Úterý", + wed: "Středa", + thu: "Čtvrtek", + fri: "Pátek", + sat: "Sobota", + }, + }, + runHistory: { + back: "Zpět na nabídku práce", + title: "Historie běhu: {{name}}", + schedule: "Harmonogram:", + emptyTitle: "Dosud nebyla žádná úspěšná realizace tohoto projektu.", + emptySubtitle: "Spusťte úlohu nyní a zkontrolujte její výsledky.", + runNow: "Začněte hned", + table: { + status: "Stav", + started: "Začal", + duration: "Délka", + error: "Chyba", + }, + stopJob: "Zastavit práci", + }, + runDetail: { + loading: "Načítám podrobnosti o běhu...", + notFound: "Nemožná nalezení běhu.", + back: "Zpět", + unknownJob: "Neznámá pracovní pozice", + runHeading: "{{name}} — Spustit #{{id}}", + duration: "Doba trvání: {{value}}", + continueInThread: "Pokračovat v tématu", + creating: "Vytváření...", + threadFailed: "Nedaří se vytvořit vlákno.", + sections: { + prompt: "Návod", + error: "Chyba", + thinking: "Myšlenky ({{count}})", + toolCalls: "Volání nástrojů ({{count}})", + files: "Soubory ({{count}})", + response: "Reakce", + metrics: "Metriky", + }, + metrics: { + promptTokens: "Klíčová slova:", + completionTokens: "Tokeny pro dokončení:", + }, + stopJob: "Zastavení práce", + killing: "Zastavte...", + }, + toolCall: { + arguments: "Argumenty:", + showResult: "Zobrazit výsledek", + hideResult: "Skryt výsledek", + }, + file: { + unknown: "Neznámý soubor", + download: "Stáhnout", + downloadFailed: "Nepodařilo se stáhnout soubor", + types: { + powerpoint: "Prezentace v programu PowerPoint", + pdf: "Dokument ve formátu PDF", + word: "Dokument ve formátu Word", + spreadsheet: "Tabulka (v programu)", + generic: "Soubor", + }, + }, + status: { + completed: "Dokončeno", + failed: "Neúspěšné", + timed_out: "Časový limit dosáhl", + running: "Běh", + queued: "Na čekací listině", + }, + }, }; export default TRANSLATIONS; diff --git a/frontend/src/locales/da/common.js b/frontend/src/locales/da/common.js index 6a89a9ca..7c2e0441 100644 --- a/frontend/src/locales/da/common.js +++ b/frontend/src/locales/da/common.js @@ -110,6 +110,7 @@ const TRANSLATIONS = { "available-channels": { telegram: "Telegram", }, + "scheduled-jobs": "Planlagte opgaver", }, login: { "multi-user": { @@ -1510,6 +1511,166 @@ const TRANSLATIONS = { unknown: "Ukendt", }, }, + scheduledJobs: { + title: "Planlagte opgaver", + enableNotifications: + "Aktiver notifikationer i browseren for at modtage resultater af jobsøgning", + description: + "Opret gentagne AI-opgaver, der kører efter en plan. Hver opgave udfører en forespørgsel med eventuelle tilgængelige værktøjer og gemmer resultatet til senere gennemgang.", + newJob: "Ny still", + loading: "Indlæses...", + emptyTitle: "Ingen planlagte opgaver endnu", + emptySubtitle: "Opret et for at komme i gang.", + table: { + name: "Navn", + schedule: "Tidsplan", + status: "Status", + lastRun: "Sidste tur", + nextRun: "Næste tur", + actions: "Handlinger", + }, + confirmDelete: + "Er du sikker på, at du ønsker at slette denne planlagte opgave?", + toast: { + deleted: "Job slettet", + triggered: "Job blev korrekt initieret.", + triggerFailed: "Mislykkedes med at starte jobbet", + triggerSkipped: "Arbejdet er allerede i gang.", + killed: "Arbejdet blev afbrudt med succes.", + killFailed: "Mislykkedes med at stoppe arbejdet", + }, + row: { + neverRun: "Aldrig køre", + viewRuns: "Visning af løb", + runNow: "Gå nu", + enable: "Aktiver", + disable: "Deaktiver", + edit: "Rediger", + delete: "Slet", + }, + modal: { + titleEdit: "Rediger planlagt opgave", + titleNew: "Ny planlagt opgave", + nameLabel: "Navn", + namePlaceholder: "f.eks. Daglig nyhedsindsamling", + promptLabel: "Anmodning", + promptPlaceholder: "Instruktionen om at køre på hver eksekvering...", + scheduleLabel: "Tidsplan", + modeBuilder: "Bygger", + modeCustom: "Tilpasset", + cronPlaceholder: "Udtryk for tidsplan (f.eks. 0 9 * * *)", + currentSchedule: "Nuværende tidsplan:", + toolsLabel: "Værktøjer (valgfrit)", + toolsDescription: + "Vælg hvilke agentværktøjer denne opgave kan bruge. Hvis ingen værktøjer er valgt, vil opgaven køre uden nogen værktøjer.", + toolsSearch: "Søg", + toolsNoResults: "Ingen værktøjer matcher", + required: "Nødvendigt", + requiredFieldsBanner: + "Venligst udfyld alle de obligatoriske felter for at oprette en stilling.", + cancel: "Annullér", + saving: "Spar...", + updateJob: "Opdater stillingen", + createJob: "Opret stilling", + jobUpdated: "Job er opdateret", + jobCreated: "Job blev skabt", + }, + builder: { + fallbackWarning: + 'Denne tekstfelt kan ikke redigeres visuelt. Vælg "Tilpas" for at bevare den, eller ændr noget nedenfor for at overskrive den.', + run: "Løb", + frequency: { + minute: "hvert minut", + hour: "per time", + day: "dagligt", + week: "hver uge", + month: "månedligt", + }, + every: "Hver", + minuteOne: "1 minut", + minuteOther: "{{count}} minutter", + atMinute: "I minutter", + pastEveryHour: "hvert time", + at: "Her", + on: "Om", + onDay: "På en dag", + ofEveryMonth: "af hver måned", + weekdays: { + sun: "Sol", + mon: "Mandag", + tue: "Tirsdag", + wed: "Onsdag", + thu: "Torsdag", + fri: "Fri", + sat: "Lørdag", + }, + }, + runHistory: { + back: "Tilbage til stillingsopslag", + title: "Historik: {{name}}", + schedule: "Tidsplan:", + emptyTitle: "Ingen resultater endnu for denne opgave.", + emptySubtitle: "Kør jobbet nu og se resultaterne.", + runNow: "Start nu", + table: { + status: "Status", + started: "Startede", + duration: "Varighed", + error: "Fejl", + }, + stopJob: "Afbryd ansættelsen", + }, + runDetail: { + loading: "Indlæsning af detaljer om kørslen...", + notFound: "Fejl: Kørsel ikke fundet.", + back: "Tilbage", + unknownJob: "Ukendt stilling", + runHeading: "{{name}} — Kør #{{id}}", + duration: "Varighed: {{value}}", + continueInThread: "Fortsæt i tråden", + creating: "Oprettelse...", + threadFailed: "Kunne ikke oprette tråd", + sections: { + prompt: "Opfordring", + error: "Fejl", + thinking: "Tanker ({{count}})", + toolCalls: "Opkald til værktøjer ({{count}})", + files: "Filer ({{count}})", + response: "Svar", + metrics: "Målinger", + }, + metrics: { + promptTokens: "Prompt-ord:", + completionTokens: "Afslutningsmarkører:", + }, + stopJob: "Afslut stillingen", + killing: "Afbryde...", + }, + toolCall: { + arguments: "Argumenter:", + showResult: "Vis resultat", + hideResult: "Skjul resultat", + }, + file: { + unknown: "Ukendt fil", + download: "Download", + downloadFailed: "Kunne ikke hente filen", + types: { + powerpoint: "PowerPoint", + pdf: "PDF-dokument", + word: "Ord-dokument", + spreadsheet: "Regneark", + generic: "Fil", + }, + }, + status: { + completed: "Afsluttet", + failed: "Mislykket", + timed_out: "Tidsudløb", + running: "Løb", + queued: "I venter", + }, + }, }; export default TRANSLATIONS; diff --git a/frontend/src/locales/de/common.js b/frontend/src/locales/de/common.js index 2ab01117..ecfd757c 100644 --- a/frontend/src/locales/de/common.js +++ b/frontend/src/locales/de/common.js @@ -109,6 +109,7 @@ const TRANSLATIONS = { "available-channels": { telegram: "Telegram", }, + "scheduled-jobs": "Geplante Aufgaben", }, login: { "multi-user": { @@ -1543,6 +1544,168 @@ const TRANSLATIONS = { unknown: "Unbekannt", }, }, + scheduledJobs: { + title: "Geplante Aufgaben", + enableNotifications: + "Aktivieren Sie Benachrichtigungen im Browser für Stellenangebote", + description: + "Erstellen Sie wiederkehrende KI-Aufgaben, die zu einem bestimmten Zeitpunkt ausgeführt werden. Jede Aufgabe führt eine Anfrage aus, optional mit zusätzlichen Werkzeugen, und speichert das Ergebnis zur Überprüfung.", + newJob: "Neue Arbeitsstelle", + loading: "Laden...", + emptyTitle: "Noch keine geplante Aufgaben", + emptySubtitle: "Erstellen Sie eines, um anzufangen.", + table: { + name: "Name", + schedule: "Zeitplan", + status: "Status", + lastRun: "Letzter Lauf", + nextRun: "Nächster Lauf", + actions: "Aktionen", + }, + confirmDelete: + "Sind Sie sicher, dass Sie diesen geplanten Job löschen möchten?", + toast: { + deleted: "Stellenanzeige gelöscht", + triggered: "Die Aufgabe wurde erfolgreich gestartet.", + triggerFailed: "Fehlgeschlagenes Auslösen der Aufgabe", + triggerSkipped: "Die Arbeiten für dieses Projekt sind bereits in Gang", + killed: "Die Arbeit wurde erfolgreich beendet.", + killFailed: "Nicht in der Lage, die Arbeit zu beenden", + }, + row: { + neverRun: "Bitte niemals laufen", + viewRuns: "Laufstrecken", + runNow: "Beginnen Sie jetzt", + enable: "Aktivieren", + disable: "Deaktivieren", + edit: "Bearbeiten", + delete: "Löschen", + }, + modal: { + titleEdit: "Geplante Aufgabe bearbeiten", + titleNew: "Neuer geplanter Job", + nameLabel: "Name", + namePlaceholder: "z.B. Tages-Nachrichten-Zusammenfassung", + promptLabel: "Anweisung", + promptPlaceholder: + "Die Anweisung, dass es bei jeder Ausführung erfolgen soll…", + scheduleLabel: "Zeitplan", + modeBuilder: "Bauunternehmer", + modeCustom: "Maßgeschneidert", + cronPlaceholder: "Ausdruck für die Ausführungszeit (z. B. 0 9 * * *)", + currentSchedule: "Aktueller Zeitplan:", + toolsLabel: "Werkzeuge (optional)", + toolsDescription: + "Wählen Sie, welche Agenten-Tools für diese Aufgabe verwendet werden können. Wenn keine Tools ausgewählt sind, wird die Aufgabe ohne Verwendung von Tools ausgeführt.", + toolsSearch: "Suche", + toolsNoResults: "Keine der verfügbaren Werkzeuge passen", + required: "Erforderlich", + requiredFieldsBanner: + "Bitte füllen Sie alle erforderlichen Felder aus, um die Stellenanzeige zu erstellen.", + cancel: "Abbrechen", + saving: "Sparen...", + updateJob: "Stellenanzeige aktualisieren", + createJob: "Stellenanzeige erstellen", + jobUpdated: "Stellenanzeige aktualisiert", + jobCreated: "Arbeitsstelle geschaffen", + }, + builder: { + fallbackWarning: + 'Dieser Text kann nicht visuell bearbeitet werden. Verwenden Sie die Option "Benutzerdefiniert", um ihn beizubehalten, oder ändern Sie die entsprechenden Felder unten, um ihn zu überschreiben.', + run: "Laufen", + frequency: { + minute: "jede Minute", + hour: "pro Stunde", + day: "täglich", + week: "wöchentlich", + month: "monatlich", + }, + every: "Jeder", + minuteOne: "1 Minute", + minuteOther: "{{count}} Minuten", + atMinute: "In der Minute", + pastEveryHour: "in jeder Stunde", + at: "Bei", + on: "Über", + onDay: "An einem Tag", + ofEveryMonth: "für jeden Monat", + weekdays: { + sun: "Sonne", + mon: "Montag", + tue: "Dienstag", + wed: "Mittwoch", + thu: "Donnerstag", + fri: "Freitag", + sat: "Samstag", + }, + }, + runHistory: { + back: "Zurück zu Stellen", + title: "Verlauf: {{name}}", + schedule: "Zeitplan:", + emptyTitle: "Noch keine Fortschritte bei dieser Aufgabe.", + emptySubtitle: + "Führen Sie die Aufgabe jetzt aus und überprüfen Sie die Ergebnisse.", + runNow: "Jetzt los!", + table: { + status: "Status", + started: "Angefangen", + duration: "Dauer", + error: "Fehler", + }, + stopJob: "Arbeitsplatz verlassen", + }, + runDetail: { + loading: "Details zum Ladevorgang werden geladen...", + notFound: "Fehler: Befehl nicht gefunden.", + back: "Zurück", + unknownJob: "Unbekannte Stellenbezeichnung", + runHeading: "{{name}} – Ausführung #{{id}}", + duration: "Dauer: {{value}}", + continueInThread: "Weiter in diesem Thread", + creating: "Erstellen...", + threadFailed: "Fehlgeschlagen beim Erstellen des Threads", + sections: { + prompt: "Anfrage", + error: "Fehler", + thinking: "Gedanken ({{count}})", + toolCalls: "Funktionsaufrufe ({{count}})", + files: "Dateien ({{count}})", + response: "Antwort", + metrics: "Kennzahlen", + }, + metrics: { + promptTokens: "Auslöse-Token:", + completionTokens: "Abschluss-Token:", + }, + stopJob: "Arbeitsplatz verlassen", + killing: "Anhalten...", + }, + toolCall: { + arguments: "Argumente:", + showResult: "Ergebnis anzeigen", + hideResult: "Ergebnis ausblenden", + }, + file: { + unknown: "Unbekannte Datei", + download: "Herunterladen", + downloadFailed: "Datei konnte nicht heruntergeladen werden", + types: { + powerpoint: "PowerPoint", + pdf: "PDF-Dokument", + word: "Word-Dokument", + spreadsheet: "Tabellenkalkulation", + generic: "Datei", + }, + }, + status: { + completed: "Abgeschlossen", + failed: "Fehlgeschlagen", + timed_out: "Zeitüberschreitung", + running: "Laufen", + queued: "Warteschlange", + }, + }, }; export default TRANSLATIONS; diff --git a/frontend/src/locales/en/common.js b/frontend/src/locales/en/common.js index 84729458..45812934 100644 --- a/frontend/src/locales/en/common.js +++ b/frontend/src/locales/en/common.js @@ -94,6 +94,7 @@ const TRANSLATIONS = { embeds: "Chat Embed", security: "Security", "event-logs": "Event Logs", + "scheduled-jobs": "Scheduled Jobs", privacy: "Privacy & Data", "ai-providers": "AI Providers", "agent-skills": "Agent Skills", @@ -1489,6 +1490,164 @@ const TRANSLATIONS = { }, }, }, + scheduledJobs: { + title: "Scheduled Jobs", + enableNotifications: "Enable browser notifications for job results", + description: + "Create recurring AI tasks that run on a schedule. Each job runs a prompt with optional tools and saves the result for review.", + newJob: "New Job", + loading: "Loading...", + emptyTitle: "No Scheduled Jobs yet", + emptySubtitle: "Create one to get started.", + table: { + name: "Name", + schedule: "Schedule", + status: "Status", + lastRun: "Last Run", + nextRun: "Next Run", + actions: "Actions", + }, + confirmDelete: "Are you sure you want to delete this scheduled job?", + status: { + completed: "Completed", + failed: "Failed", + timed_out: "Timed out", + running: "Running", + queued: "Queued", + }, + toast: { + deleted: "Job deleted", + triggered: "Job triggered successfully", + triggerFailed: "Failed to trigger job", + triggerSkipped: "A run is already in progress for this job", + killed: "Job stopped successfully", + killFailed: "Failed to stop job", + }, + row: { + neverRun: "Never run", + viewRuns: "View runs", + runNow: "Run now", + enable: "Enable", + disable: "Disable", + edit: "Edit", + delete: "Delete", + }, + modal: { + titleEdit: "Edit Scheduled Job", + titleNew: "New Scheduled Job", + nameLabel: "Name", + namePlaceholder: "e.g. Daily News Digest", + promptLabel: "Prompt", + promptPlaceholder: "The instruction to run on each execution...", + scheduleLabel: "Schedule", + modeBuilder: "Builder", + modeCustom: "Custom", + cronPlaceholder: "Cron expression (e.g. 0 9 * * *)", + currentSchedule: "Current schedule:", + toolsLabel: "Tools (Optional)", + toolsDescription: + "Select which agent tools this job can use. If none are selected, the job runs without any tools.", + toolsSearch: "Search", + toolsNoResults: "No tools match", + required: "Required", + requiredFieldsBanner: + "Please fill out all required fields in order to create job.", + cancel: "Cancel", + saving: "Saving...", + updateJob: "Update Job", + createJob: "Create Job", + jobUpdated: "Job updated", + jobCreated: "Job created", + }, + builder: { + fallbackWarning: + "This expression can't be edited visually. Switch to Custom to keep it, or change anything below to overwrite it.", + run: "Run", + frequency: { + minute: "every minute", + hour: "hourly", + day: "daily", + week: "weekly", + month: "monthly", + }, + every: "Every", + minuteOne: "1 minute", + minuteOther: "{{count}} minutes", + atMinute: "At minute", + pastEveryHour: "past every hour", + at: "At", + on: "On", + onDay: "On day", + ofEveryMonth: "of every month", + weekdays: { + sun: "Sun", + mon: "Mon", + tue: "Tue", + wed: "Wed", + thu: "Thu", + fri: "Fri", + sat: "Sat", + }, + }, + runHistory: { + back: "Back to jobs", + title: "Run History: {{name}}", + schedule: "Schedule:", + emptyTitle: "No runs yet for this job", + emptySubtitle: "Run the job now and view its results.", + runNow: "Run Now", + stopJob: "Stop job", + table: { + status: "Status", + started: "Started", + duration: "Duration", + error: "Error", + }, + }, + runDetail: { + loading: "Loading run details...", + notFound: "Run not found.", + back: "Back", + unknownJob: "Unknown Job", + runHeading: "{{name}} — Run #{{id}}", + duration: "Duration: {{value}}", + continueInThread: "Continue in Thread", + creating: "Creating...", + threadFailed: "Failed to create thread", + stopJob: "Stop Job", + killing: "Stopping...", + sections: { + prompt: "Prompt", + error: "Error", + thinking: "Thoughts ({{count}})", + toolCalls: "Tool Calls ({{count}})", + files: "Files ({{count}})", + response: "Response", + metrics: "Metrics", + }, + metrics: { + promptTokens: "Prompt tokens:", + completionTokens: "Completion tokens:", + }, + }, + toolCall: { + arguments: "Arguments:", + showResult: "Show result", + hideResult: "Hide result", + }, + file: { + unknown: "Unknown file", + download: "Download", + downloadFailed: "Failed to download file", + types: { + powerpoint: "PowerPoint", + pdf: "PDF Document", + word: "Word Document", + spreadsheet: "Spreadsheet", + generic: "File", + }, + }, + }, }; export default TRANSLATIONS; diff --git a/frontend/src/locales/es/common.js b/frontend/src/locales/es/common.js index c81d95a9..479d787c 100644 --- a/frontend/src/locales/es/common.js +++ b/frontend/src/locales/es/common.js @@ -109,6 +109,7 @@ const TRANSLATIONS = { "available-channels": { telegram: "Telegram", }, + "scheduled-jobs": "Tareas programadas", }, login: { "multi-user": { @@ -1554,6 +1555,165 @@ const TRANSLATIONS = { unknown: "Desconocido", }, }, + scheduledJobs: { + title: "Tareas programadas", + enableNotifications: + "Activar las notificaciones del navegador para los resultados de búsqueda de empleo.", + description: + "Cree tareas de IA recurrentes que se ejecuten según un horario. Cada tarea ejecuta una instrucción con herramientas opcionales y guarda el resultado para su revisión.", + newJob: "Nuevo trabajo", + loading: "Cargando...", + emptyTitle: "Aún no hay tareas programadas.", + emptySubtitle: "Cree uno para empezar.", + table: { + name: "Nombre", + schedule: "Horario", + status: "Estado", + lastRun: "Última carrera", + nextRun: "Próxima carrera", + actions: "Acciones", + }, + confirmDelete: "¿Está seguro de que desea eliminar esta tarea programada?", + toast: { + deleted: "Trabajo eliminado", + triggered: "La tarea se ha completado con éxito.", + triggerFailed: "No se pudo iniciar la tarea.", + triggerSkipped: "Ya se ha iniciado el trabajo.", + killed: "La tarea se completó con éxito.", + killFailed: "No logró detener el trabajo.", + }, + row: { + neverRun: "Nunca corras", + viewRuns: "Ejecución de pruebas", + runNow: "¡Corre ahora!", + enable: "Habilitar", + disable: "Desactivar", + edit: "Editar", + delete: "Eliminar", + }, + modal: { + titleEdit: "Modificar tarea programada", + titleNew: "Nueva tarea programada", + nameLabel: "Nombre", + namePlaceholder: "p. ej., Resumen diario de noticias", + promptLabel: "Solicitud", + promptPlaceholder: "La instrucción para ejecutarlo en cada ejecución...", + scheduleLabel: "Horario", + modeBuilder: "Constructor", + modeCustom: "Personalizado", + cronPlaceholder: "Expresión de cron (por ejemplo, 0 9 * * *)", + currentSchedule: "Horario actual:", + toolsLabel: "Herramientas (opcional)", + toolsDescription: + "Seleccione las herramientas disponibles para esta tarea. Si ninguna herramienta está seleccionada, la tarea se ejecutará sin utilizar ninguna herramienta.", + toolsSearch: "Buscar", + toolsNoResults: "No se encontraron herramientas que coincidan.", + required: "Requerido", + requiredFieldsBanner: + "Por favor, complete todos los campos obligatorios para crear el anuncio de empleo.", + cancel: "Cancelar", + saving: "Ahorrando...", + updateJob: "Actualizar puesto de trabajo", + createJob: "Crear empleo", + jobUpdated: "Puesto actualizado", + jobCreated: "Puesto creado", + }, + builder: { + fallbackWarning: + 'Esta expresión no se puede modificar visualmente. Cambie a "Personalizado" para mantenerla, o modifique cualquier cosa debajo para reemplazarla.', + run: "Correr", + frequency: { + minute: "cada minuto", + hour: "por hora", + day: "diario", + week: "semanal", + month: "mensual", + }, + every: "Cada", + minuteOne: "1 minuto", + minuteOther: "{{count}} minutos", + atMinute: "En el minuto", + pastEveryHour: "cada hora", + at: "A partir de", + on: "Sobre", + onDay: "En un día", + ofEveryMonth: "de cada mes", + weekdays: { + sun: "Sol", + mon: "Una", + tue: "Martes", + wed: "Miércoles", + thu: "Jueves", + fri: "Viernes", + sat: "Sábado", + }, + }, + runHistory: { + back: "Volver a las ofertas de empleo", + title: "Historial de ejecuciones: {{name}}", + schedule: "Horario:", + emptyTitle: "Aún no hay candidatos para este puesto.", + emptySubtitle: "Ejecute la tarea ahora y vea los resultados.", + runNow: "¡Corre ahora!", + table: { + status: "Estado", + started: "Comenzó", + duration: "Duración", + error: "Error", + }, + stopJob: "Suspender el empleo", + }, + runDetail: { + loading: "Cargando detalles de la ejecución...", + notFound: "No se encontró la ejecución.", + back: "Regreso; Atrás", + unknownJob: "Puesto sin especificar", + runHeading: "{{name}} — Ejecutar la prueba #{{id}}", + duration: "Duración: {{value}}", + continueInThread: "Continuar en esta conversación", + creating: "Creando...", + threadFailed: "No se pudo crear el hilo.", + sections: { + prompt: "Indicación", + error: "Error", + thinking: "Ideas ({{count}})", + toolCalls: "Llamadas a herramientas ({{count}})", + files: "Archivos ({{count}})", + response: "Respuesta", + metrics: "Indicadores", + }, + metrics: { + promptTokens: "Palabras clave:", + completionTokens: "Tokens de finalización:", + }, + stopJob: "Suspender el empleo", + killing: "Detener...", + }, + toolCall: { + arguments: "Argumentos:", + showResult: "Mostrar resultado", + hideResult: "Ocultar resultado", + }, + file: { + unknown: "Archivo desconocido", + download: "Descargar", + downloadFailed: "No se pudo descargar el archivo.", + types: { + powerpoint: "Presentación de diapositivas", + pdf: "Documento en formato PDF", + word: "Documento de Word", + spreadsheet: "Hoja de cálculo", + generic: "Archivo", + }, + }, + status: { + completed: "Completado", + failed: "Fracasado", + timed_out: "Tiempo agotado", + running: "Correr", + queued: "En cola", + }, + }, }; export default TRANSLATIONS; diff --git a/frontend/src/locales/et/common.js b/frontend/src/locales/et/common.js index 770555c3..679d07b3 100644 --- a/frontend/src/locales/et/common.js +++ b/frontend/src/locales/et/common.js @@ -108,6 +108,7 @@ const TRANSLATIONS = { "available-channels": { telegram: "Telegram", }, + "scheduled-jobs": "Planeeritud tööd", }, login: { "multi-user": { @@ -1464,6 +1465,166 @@ const TRANSLATIONS = { unknown: "Tuntud pole", }, }, + scheduledJobs: { + title: "Planeeritud tööd", + enableNotifications: "Aktiveeri braiseri teavitused tööväljundite kohta", + description: + "Loo korduvad AI-ülesanded, mis töötavad eeldatud ajakavaga. Iga ülesanne käitab promp, kasutades valikuvõimalusega tööriistu, ja salvestab tulemuse kontrollimiseks.", + newJob: "Uus töö", + loading: "Laadimine...", + emptyTitle: "Hetkel pole planeeritud tööde nimekirja.", + emptySubtitle: "Loo üks, et alustada.", + table: { + name: "Nimi", + schedule: "Ajavälja", + status: "Статус", + lastRun: "Viimne sõit", + nextRun: "Järgmine üritus", + actions: "Meetmed", + }, + confirmDelete: + "Kas olete kindel, et soovite seda planeeritud tööd kustutada?", + toast: { + deleted: "Töö kustutatud", + triggered: "Töö on edukalt käivitunud.", + triggerFailed: "Ei õnnestunud töö käivitada", + triggerSkipped: "Töö on juba alguses.", + killed: "Töö lõpetati edukalt", + killFailed: "Edasi töötamist ei suutnud peatada", + }, + row: { + neverRun: "Ära kunagi kiirusta", + viewRuns: "Vaatamise marsrid", + runNow: "Alustage kohe", + enable: "Aktiveerida", + disable: "Välja lülitada", + edit: "Redigeerimine", + delete: "Hüvida", + }, + modal: { + titleEdit: "Muuda planeeritud tööd", + titleNew: "Uus planeeritud töö", + nameLabel: "Nimi", + namePlaceholder: "nt. Päevase uudiste kokkuvõte", + promptLabel: "Järgmis", + promptPlaceholder: + "Juhend, mis käsitleb programmi käivitamist iga kord, kui seda kasutatakse...", + scheduleLabel: "Ajavälja", + modeBuilder: "Ehitaj, ehitaja", + modeCustom: "Kohandatud", + cronPlaceholder: "Cron väljendus (näiteks 0 9 * * *)", + currentSchedule: "Praegune ajakava:", + toolsLabel: "Vahendid (valikuline)", + toolsDescription: + "Valige välja need agenti vahendid, mida see töö saab kasutada. Kui ühtki vahendit ei ole valitud, siis töö toimub ilma vahenditeta.", + toolsSearch: "otsing", + toolsNoResults: "Midagi sellist ei ole", + required: "Nõutav", + requiredFieldsBanner: + "Palun täitke kõik vajalikud väljad, et töö avaldamine oleks võimalik.", + cancel: "Katkuda", + saving: "Säästmine...", + updateJob: "Töö avaldamise uuendamine", + createJob: "Loo töökoht", + jobUpdated: "Töö on uuendatud", + jobCreated: "Töö loodud", + }, + builder: { + fallbackWarning: + 'See väljend ei ole võimalik muuta visuaalselt. Valige "Custom" režiim, et seda säilitada, või muutke allolevaid elemente, et seda asendada.', + run: "Jooksa", + frequency: { + minute: "iga minut", + hour: "iga tunn", + day: "iga päev", + week: "iga nädal", + month: "kuukohane", + }, + every: "Igal", + minuteOne: "1 minut", + minuteOther: "{{count}} minut", + atMinute: "Minutil", + pastEveryHour: "iga tunni järel", + at: "Samal ajal", + on: "On", + onDay: "Ühel päeval", + ofEveryMonth: "iga kuu", + weekdays: { + sun: "Päev", + mon: "Päev", + tue: "teisipäev", + wed: "Keskpäev", + thu: "Reede", + fri: "Reede", + sat: "Laud", + }, + }, + runHistory: { + back: "Tagasi töökohtadele", + title: "Täitmise ajalugu: {{name}}", + schedule: "Ajavõrdlus:", + emptyTitle: "Hetkel pole selle töös midagi saavutatud.", + emptySubtitle: "Alustage tööd kohe ja vaadake selle tulemisi.", + runNow: "Alustage kohe", + table: { + status: "Статус", + started: "Algas", + duration: "Kestvus", + error: "Viga", + }, + stopJob: "Töö peatamine", + }, + runDetail: { + loading: "Laadimise ajal saadaval on sõidu üksikasjad...", + notFound: "Programm ei leitud.", + back: "Tagasi", + unknownJob: "Tuntmatu amet", + runHeading: "{{name}} — Üritus #{{id}}", + duration: "Kestvus: {{value}}", + continueInThread: "Jätka teemas", + creating: "Loomine...", + threadFailed: "Epäõnnes teema loomist", + sections: { + prompt: "Järgmis", + error: "Viga", + thinking: "Mõtisklused ({{count}})", + toolCalls: "Vahendite kutsumised ({{count}})", + files: "Failid ({{count}})", + response: "Vastus", + metrics: "Mõõdised", + }, + metrics: { + promptTokens: "Algatusmärgid:", + completionTokens: "Lõpetamisandmed:", + }, + stopJob: "Töö peatamine", + killing: "Peatumine...", + }, + toolCall: { + arguments: "Argumentid:", + showResult: "Näita tulemust", + hideResult: "Peida tulemus", + }, + file: { + unknown: "Tuntmatu fail", + download: "Laadige alla", + downloadFailed: "Faili ei õnnestunud alla laadida", + types: { + powerpoint: "PowerPoint", + pdf: "PDF-dokumend", + word: "Dokumend", + spreadsheet: "Lehtaraken", + generic: "Fail", + }, + }, + status: { + completed: "Lõpitatud", + failed: "Epäõnnestunud", + timed_out: "Aja täitunud", + running: "Jooksmine", + queued: "Ootel", + }, + }, }; export default TRANSLATIONS; diff --git a/frontend/src/locales/fa/common.js b/frontend/src/locales/fa/common.js index 8df21159..6c373a9f 100644 --- a/frontend/src/locales/fa/common.js +++ b/frontend/src/locales/fa/common.js @@ -110,6 +110,7 @@ const TRANSLATIONS = { "available-channels": { telegram: "تلگرام", }, + "scheduled-jobs": "وظایف برنامه‌ریزی شده", }, login: { "multi-user": { @@ -1498,6 +1499,166 @@ const TRANSLATIONS = { unknown: "نامشخص", }, }, + scheduledJobs: { + title: "وظایف برنامه‌ریزی‌شده", + enableNotifications: "فعال کردن اعلان‌های مرورگر برای نتایج جستجوی شغل", + description: + "ایجاد وظایف هوش مصنوعی تکراری که در یک برنامه زمانی اجرا می‌شوند. هر وظیفه یک دستورالعمل را با استفاده از ابزارهای اختیاری اجرا کرده و نتیجه را برای بررسی ذخیره می‌کند.", + newJob: "یک موقعیت شغلی جدید", + loading: "در حال بارگذاری...", + emptyTitle: "هیچ کار برنامه‌ریزی‌شده‌ای در حال حاضر وجود ندارد.", + emptySubtitle: "برای شروع، یک نمونه ایجاد کنید.", + table: { + name: "نام", + schedule: "برنامه زمانی", + status: "وضعیت", + lastRun: "آخرین اجرا", + nextRun: "مسیر بعدی", + actions: "اقدامات", + }, + confirmDelete: + "آیا مطمئن هستید که می‌خواهید این وظیفه برنامه‌ریزی شده را حذف کنید؟", + toast: { + deleted: "حذف شده", + triggered: "وظیفه با موفقیت انجام شد.", + triggerFailed: "عدم اجرای وظیفه", + triggerSkipped: "کار مربوط به این پروژه از قبل آغاز شده است.", + killed: "کار با موفقیت به پایان رسید.", + killFailed: "عدم توانایی در متوقف کردن کار", + }, + row: { + neverRun: "هرگز سرعت خود را افزایش ندهید.", + viewRuns: "نمایش‌ها", + runNow: "فوری، همین حالا", + enable: "فعال کردن", + disable: "غیرفعال کردن", + edit: "ویرایش کردن", + delete: "حذف", + }, + modal: { + titleEdit: "ویرایش وظیفه برنامه‌ریزی شده", + titleNew: "وظیفه جدید برنامه ریزی شده", + nameLabel: "نام", + namePlaceholder: "به عنوان مثال، خلاصه‌ای از اخبار روز", + promptLabel: "دستورالعمل", + promptPlaceholder: "دستورالعمل برای اجرای یک فعالیت در هر بار اجرا...", + scheduleLabel: "برنامه زمانی", + modeBuilder: "مهندس، سازنده", + modeCustom: "سفارشی", + cronPlaceholder: "عبارت زمانی (به عنوان مثال، 0 9 * * *)", + currentSchedule: "برنامه فعلی:", + toolsLabel: "ابزارها (اختیاری)", + toolsDescription: + "انتخاب ابزارهای مورد استفاده برای این وظیفه. اگر هیچ ابزایی انتخاب نشده باشد، وظیفه بدون استفاده از ابزار اجرا می‌شود.", + toolsSearch: "جستجو", + toolsNoResults: "هیچ ابزاری وجود ندارد", + required: "الزامی", + requiredFieldsBanner: + "لطفاً تمام فیلدهای مورد نیاز را پر کنید تا بتوانید شغل را ایجاد کنید.", + cancel: "لغو کردن", + saving: "ذخیره...", + updateJob: "به‌روزرسانی درخواست", + createJob: "ایجاد شغل", + jobUpdated: "وضعیت شغلی به‌روز شده است", + jobCreated: "یک شغل ایجاد شد", + }, + builder: { + fallbackWarning: + 'این عبارت نمی‌تواند به صورت بصری ویرایش شود. برای حفظ آن، از گزینه "کامل" استفاده کنید، یا هر چیزی زیر آن را تغییر دهید تا جایگزین شود.', + run: "دویدن", + frequency: { + minute: "هر دقیقه", + hour: "به ازای هر ساعت", + day: "روزانه", + week: "هفتگی", + month: "ماهانه", + }, + every: "هر", + minuteOne: "۱ دقیقه", + minuteOther: "{{count}} دقیقه", + atMinute: "در دقیقه", + pastEveryHour: "هر ساعت از گذشته", + at: "در", + on: "در", + onDay: "در روز", + ofEveryMonth: "از هر ماه", + weekdays: { + sun: "خورشید", + mon: "یک", + tue: "دوشنبه", + wed: "روز سه شنبه", + thu: "روز چهارشنبه", + fri: "جمعه", + sat: "جمعه", + }, + }, + runHistory: { + back: "بازگشت به بخش فرصت‌های شغلی", + title: "تاریخ اجرای: {{name}}", + schedule: "برنامه زمانی:", + emptyTitle: "هنوز هیچ پیشرفتی در این پروژه حاصل نشده است.", + emptySubtitle: + "اجرای وظیفه را اکنون انجام دهید و نتایج آن را مشاهده کنید.", + runNow: "فوری", + table: { + status: "وضعیت", + started: "شروع", + duration: "مدت زمان", + error: "خطا", + }, + stopJob: "متوقف کردن کار", + }, + runDetail: { + loading: "بارگذاری جزئیات اجرای تمرین...", + notFound: "دستورالعمل یافت نشد.", + back: "بازگشت", + unknownJob: "عنوان شغلی نامشخص", + runHeading: "{{name}} — اجرای شماره {{id}}", + duration: "مدت زمان: {{value}}", + continueInThread: "ادامه در این موضوع", + creating: "ایجاد...", + threadFailed: "امکان ایجاد نخ (thread) وجود نداشت.", + sections: { + prompt: "دستورالعمل", + error: "خطا", + thinking: "اندیشه‌ها ({{count}})", + toolCalls: "فراخوانی ابزار ({{count}})", + files: "فایل‌ها ({{count}})", + response: "پاسخ", + metrics: "معیارها", + }, + metrics: { + promptTokens: "توکن‌های آغازین:", + completionTokens: "توکن‌های تکمیل:", + }, + stopJob: "متوقف کردن کار", + killing: "توقف...", + }, + toolCall: { + arguments: "استدلال‌ها:", + showResult: "نتایج را نشان دهید", + hideResult: "پنهان کردن نتیجه", + }, + file: { + unknown: "فایلی ناشناخته", + download: "دانلود", + downloadFailed: "عدم امکان دانلود فایل", + types: { + powerpoint: "پاورپوینت", + pdf: "فایل به فرمت PDF", + word: "فایل ورد", + spreadsheet: "جدول داده‌ها", + generic: "فایل", + }, + }, + status: { + completed: "تکمیل شده", + failed: "شکست", + timed_out: "زمان به پایان رسید", + running: "دویدن", + queued: "در صف انتظار", + }, + }, }; export default TRANSLATIONS; diff --git a/frontend/src/locales/fr/common.js b/frontend/src/locales/fr/common.js index cb9cb883..e6921568 100644 --- a/frontend/src/locales/fr/common.js +++ b/frontend/src/locales/fr/common.js @@ -108,6 +108,7 @@ const TRANSLATIONS = { "available-channels": { telegram: "Telegram", }, + "scheduled-jobs": "Tâches planifiées", }, login: { "multi-user": { @@ -1531,6 +1532,167 @@ const TRANSLATIONS = { unknown: "Inconnu", }, }, + scheduledJobs: { + title: "Tâches planifiées", + enableNotifications: + "Activer les notifications du navigateur pour les résultats de recherche d'emploi.", + description: + "Créez des tâches d'IA récurrentes qui s'exécutent selon un calendrier. Chaque tâche exécute une requête avec des outils optionnels et enregistre le résultat pour examen ultérieur.", + newJob: "Nouvelle offre d'emploi", + loading: "Chargement...", + emptyTitle: "Aucune tâche planifiée pour le moment.", + emptySubtitle: "Créez-en un pour commencer.", + table: { + name: "Nom", + schedule: "Calendrier", + status: "État", + lastRun: "Dernière course", + nextRun: "Prochaine course", + actions: "Actions", + }, + confirmDelete: + "Êtes-vous certain de vouloir supprimer cette tâche planifiée ?", + toast: { + deleted: "Emploi supprimé", + triggered: "La tâche a été exécutée avec succès.", + triggerFailed: "N'a pas réussi à lancer la tâche", + triggerSkipped: "Le projet est déjà en cours.", + killed: "La tâche a été exécutée avec succès.", + killFailed: "Impossible d'arrêter le travail.", + }, + row: { + neverRun: "Ne jamais courir", + viewRuns: "Afficher les résultats", + runNow: "Partons maintenant", + enable: "Activer", + disable: "Désactiver", + edit: "Modifier", + delete: "Supprimer", + }, + modal: { + titleEdit: "Modifier la tâche planifiée", + titleNew: "Nouvelle tâche planifiée", + nameLabel: "Nom", + namePlaceholder: 'par exemple, "Résumé quotidien des actualités"', + promptLabel: "Demande", + promptPlaceholder: "L'instruction de s'exécuter à chaque exécution...", + scheduleLabel: "Calendrier", + modeBuilder: "Constructeur", + modeCustom: "Personnalisé", + cronPlaceholder: "Expression de temps (par exemple, 0 9 * * *)", + currentSchedule: "Planning actuel :", + toolsLabel: "Outils (facultatifs)", + toolsDescription: + "Sélectionnez les outils d'agent que cette tâche peut utiliser. Si aucun outil n'est sélectionné, la tâche s'exécutera sans aucun outil.", + toolsSearch: "Rechercher", + toolsNoResults: "Aucun outil ne correspond", + required: "Nécessaire", + requiredFieldsBanner: + "Veuillez remplir tous les champs obligatoires afin de créer l'annonce.", + cancel: "Annuler", + saving: "Économiser...", + updateJob: "Mettre à jour l'emploi", + createJob: "Créer un emploi", + jobUpdated: "Poste mis à jour", + jobCreated: "Emploi créé", + }, + builder: { + fallbackWarning: + 'Cette expression ne peut pas être modifiée visuellement. Pour la conserver, passez en mode "Personnalisé". Sinon, modifiez tout ce qui se trouve en dessous pour la remplacer.', + run: "Courir", + frequency: { + minute: "chaque minute", + hour: "par heure", + day: "quotidien", + week: "hebdomadaire", + month: "mensuel", + }, + every: "Chaque", + minuteOne: "1 minute", + minuteOther: "{{count}} minutes", + atMinute: "À la minute", + pastEveryHour: "chaque heure", + at: "À", + on: "Sur", + onDay: "Un jour", + ofEveryMonth: "chaque mois", + weekdays: { + sun: "Soleil", + mon: "Moi", + tue: "Mardi", + wed: "Mercredi", + thu: "Jeudi", + fri: "Vendredi", + sat: "Samedi", + }, + }, + runHistory: { + back: "Retour à la liste des offres d'emploi", + title: "Historique des exécutions : {{name}}", + schedule: "Calendrier :", + emptyTitle: "Aucun résultat pour cette tâche.", + emptySubtitle: + "Exécutez la tâche immédiatement et consultez les résultats.", + runNow: "Partir maintenant", + table: { + status: "Statut", + started: "Débuté", + duration: "Durée", + error: "Erreur", + }, + stopJob: "Arrêter le travail", + }, + runDetail: { + loading: "Affichage des détails de la course...", + notFound: "La commande n'a pas été trouvée.", + back: "Retour", + unknownJob: "Poste non spécifié", + runHeading: "{{name}} — Exécution n°{{id}}", + duration: "Durée : {{value}}", + continueInThread: "Continuer la discussion", + creating: "Créer...", + threadFailed: "Impossible de créer le thread.", + sections: { + prompt: "Demande", + error: "Erreur", + thinking: "Pensées ({{count}})", + toolCalls: "Appels aux outils ({{count}})", + files: "Fichiers ({{count}})", + response: "Réponse", + metrics: "Indicateurs", + }, + metrics: { + promptTokens: "Mots-clés de requête:", + completionTokens: "Jetons de complétion :", + }, + stopJob: "Arrêter le travail", + killing: "Arrêt...", + }, + toolCall: { + arguments: "Arguments:", + showResult: "Afficher le résultat", + hideResult: "Masquer le résultat", + }, + file: { + unknown: "Fichier inconnu", + download: "Télécharger", + downloadFailed: "Échec du téléchargement du fichier", + types: { + powerpoint: "PowerPoint", + pdf: "Document au format PDF", + word: "Document Word", + spreadsheet: "Tableur", + generic: "Fichier", + }, + }, + status: { + completed: "Terminé", + failed: "Échoué", + timed_out: "Temps écoulé", + running: "Course à pied", + queued: "En attente", + }, + }, }; export default TRANSLATIONS; diff --git a/frontend/src/locales/he/common.js b/frontend/src/locales/he/common.js index afe1843e..f2842f2e 100644 --- a/frontend/src/locales/he/common.js +++ b/frontend/src/locales/he/common.js @@ -106,6 +106,7 @@ const TRANSLATIONS = { "available-channels": { telegram: "טלגרם", }, + "scheduled-jobs": "משימות מתוכננות", }, login: { "multi-user": { @@ -1452,6 +1453,163 @@ const TRANSLATIONS = { unknown: "לא ידוע", }, }, + scheduledJobs: { + title: "משימות מתוכננות", + enableNotifications: "אפשר להפעיל התראות בדפדפן עבור תוצאות חיפוש עבודה.", + description: + "צור משימות AI חוזרות שיופעלו על פי לוח זמנים. כל משימה תפעיל שאילתה עם כלים אופציונליים ותשמור על התוצאה לבדיקה.", + newJob: "תפקיד חדש", + loading: "טעינה...", + emptyTitle: "אין משימות מתוכננות עדיין", + emptySubtitle: "צרו אחד כדי להתחיל.", + table: { + name: "שם", + schedule: "לוח זמנים", + status: "סטטוס", + lastRun: "הפעולה האחרונה", + nextRun: "הפעולה הבאה", + actions: "פעולות", + }, + confirmDelete: "האם אתה בטוח שאתה רוצה למחוק משימה זו שתוכננה?", + toast: { + deleted: "המשרה נמחקת", + triggered: "התעסוקה בוצעה בהצלחה.", + triggerFailed: "לא הצליח להפעיל את העבודה", + triggerSkipped: "העבודה כבר נמצאת בשלבי ביצוע.", + killed: "העבודה הסתיימה בהצלחה.", + killFailed: "לא הצלחתי לעצור את העבודה", + }, + row: { + neverRun: "לעולם אל תרוץ", + viewRuns: "מסלולים", + runNow: "תתחילו עכשיו", + enable: "אפשר", + disable: "לכבות", + edit: "עריכה", + delete: "מחיקה", + }, + modal: { + titleEdit: "עריכת משימה מתוכננת", + titleNew: "משימה מתוזמנת חדשה", + nameLabel: "שם", + namePlaceholder: "לדוגמה: תקציר חדשות יומי", + promptLabel: "הוראה", + promptPlaceholder: "ההוראה להפעיל בכל ביצוע...", + scheduleLabel: "לוח זמנים", + modeBuilder: "מבנה", + modeCustom: "מותאם אישית", + cronPlaceholder: "ביטוי לזמני ביצוע (למשל, 0 9 * * *)", + currentSchedule: "לוח הזמנים הנוכחי:", + toolsLabel: "כלים (אופציונלי)", + toolsDescription: + "בחר את כלי העבודה שניתן להשתמש בהם עבור משימה זו. אם לא נבחרו כלים, המשימה תפעל ללא שימוש בכלים.", + toolsSearch: "חיפוש", + toolsNoResults: "אין כלים המתאימים", + required: "נדרש", + requiredFieldsBanner: "אנא מלאו את כל השדות הנדרשים כדי ליצור משרה.", + cancel: "בטל", + saving: "חיסכון...", + updateJob: "עדכון משרה", + createJob: "יצירת משרה", + jobUpdated: "פרסום מעודכן", + jobCreated: "תפקיד נוצר", + }, + builder: { + fallbackWarning: + 'הביטוי הזה אינו ניתן לעריכה באופן ויזואלי. עברו למצב "מותאם אישית" כדי לשמור עליו, או שנה את כל מה שמתחת כדי להחליף אותו.', + run: "לרוץ", + frequency: { + minute: "כל דקה", + hour: "לכל שעה", + day: "יומי", + week: "שבועי", + month: "חודשי", + }, + every: "כל", + minuteOne: "1 דקה", + minuteOther: "{{count}} דקות", + atMinute: "במהלך", + pastEveryHour: "כל שעה", + at: "במהלך", + on: "על", + onDay: "ביום", + ofEveryMonth: "של כל חודש", + weekdays: { + sun: "שמש", + mon: "יום שני", + tue: "יום שני", + wed: "יום רביעי", + thu: "יום רביעי", + fri: "יום שישי", + sat: "שבת", + }, + }, + runHistory: { + back: "חזרה למחיפוש עבודה", + title: "היסטוריית ריצות: {{name}}", + schedule: "לוח זמנים:", + emptyTitle: "עדיין לא בוצעו עבודות עבור פרויקט זה.", + emptySubtitle: "הפעל את העבודה עכשיו וראה את התוצאות שלה.", + runNow: "התחילו עכשיו", + table: { + status: "סטטוס", + started: "התחיל", + duration: "משך זמן", + error: "שגיאה", + }, + stopJob: "להפסיק עבודה", + }, + runDetail: { + loading: "טעינת פרטי הריצה...", + notFound: "לא נמצאה פעולה.", + back: "חזרה", + unknownJob: "תפקיד לא ידוע", + runHeading: "{{name}} — הפעל את מספר {{id}}", + duration: "משך: {{value}}", + continueInThread: "להמשיך בדיון", + creating: "יצירה...", + threadFailed: "לא הצליח ליצור דיון.", + sections: { + prompt: "הוראה", + error: "שגיאה", + thinking: "מחשבות ({{count}})", + toolCalls: "קריאות לכלי ({{count}})", + files: "קבצים ({{count}})", + response: "תגובה", + metrics: "מדדים", + }, + metrics: { + promptTokens: "מילות מפתח:", + completionTokens: "טוקנים לסיום:", + }, + stopJob: "הפסק עבודה", + killing: "עצירה...", + }, + toolCall: { + arguments: "טיעונים:", + showResult: "הצג תוצאה", + hideResult: "הסתר תוצאה", + }, + file: { + unknown: "קובץ לא מזוהה", + download: "הורדה", + downloadFailed: "לא הצליח להוריד את הקובץ", + types: { + powerpoint: "פאוורPoint", + pdf: "מסמך PDF", + word: "מסמך מילה", + spreadsheet: "טבלה", + generic: "קובץ", + }, + }, + status: { + completed: "הושלם", + failed: "נכשל", + timed_out: "הזמן פג", + running: "ריצה", + queued: "באי תור", + }, + }, }; export default TRANSLATIONS; diff --git a/frontend/src/locales/it/common.js b/frontend/src/locales/it/common.js index 7182998e..b9646064 100644 --- a/frontend/src/locales/it/common.js +++ b/frontend/src/locales/it/common.js @@ -110,6 +110,7 @@ const TRANSLATIONS = { "available-channels": { telegram: "Telegram", }, + "scheduled-jobs": "Lavori pianificati", }, login: { "multi-user": { @@ -1552,6 +1553,168 @@ const TRANSLATIONS = { unknown: "Sconosciuto", }, }, + scheduledJobs: { + title: "Lavori pianificati", + enableNotifications: + "Abilitare le notifiche del browser per i risultati delle ricerche di lavoro", + description: + "Definisci attività di intelligenza artificiale ricorrenti che vengono eseguite su un programma prestabilito. Ogni attività esegue un prompt con strumenti opzionali e salva il risultato per la revisione.", + newJob: "Nuovo lavoro", + loading: "Caricamento...", + emptyTitle: "Al momento non ci sono attività pianificate.", + emptySubtitle: "Crea uno per iniziare.", + table: { + name: "Nome", + schedule: "Programma", + status: "Stato", + lastRun: "Ultima corsa", + nextRun: "Prossima corsa", + actions: "Azioni", + }, + confirmDelete: + "È sicuro che desideri eliminare questa attività programmata?", + toast: { + deleted: "Lavoro eliminato", + triggered: "L'attività è stata avviata correttamente.", + triggerFailed: "Non è stato possibile avviare il lavoro.", + triggerSkipped: "Sono già in corso i lavori per questo progetto.", + killed: "L'attività è stata interrotta con successo.", + killFailed: "Non è stato possibile interrompere l'attività lavorativa.", + }, + row: { + neverRun: "Non correre mai", + viewRuns: "Orari", + runNow: "Inizia ora", + enable: "Abilitare", + disable: "Disattivare", + edit: "Modifica", + delete: "Elimina", + }, + modal: { + titleEdit: "Modifica attività programmata", + titleNew: "Nuovo lavoro programmato", + nameLabel: "Nome", + namePlaceholder: 'ad esempio, "Rassegna giornalistica quotidiana"', + promptLabel: "Suggerimento", + promptPlaceholder: + "L'istruzione per eseguire l'operazione ad ogni esecuzione...", + scheduleLabel: "Programma", + modeBuilder: "Costruttore", + modeCustom: "Personalizzato", + cronPlaceholder: "Espressione della cron (ad esempio, 0 9 * * *)", + currentSchedule: "Orario attuale:", + toolsLabel: "Strumenti (opzionali)", + toolsDescription: + "Seleziona quali strumenti di automazione possono essere utilizzati per questo lavoro. Se nessuno strumento è selezionato, il lavoro verrà eseguito senza l'utilizzo di alcun strumento.", + toolsSearch: "Ricerca", + toolsNoResults: "Non sono state trovate corrispondenze.", + required: "Necessario", + requiredFieldsBanner: + "Si prega di compilare tutti i campi obbligatori per creare l'annuncio di lavoro.", + cancel: "Annulla", + saving: "Risparmio...", + updateJob: "Aggiorna l'annuncio di lavoro", + createJob: "Creare un'offerta di lavoro", + jobUpdated: "Posizione aggiornata", + jobCreated: "Posizione creata", + }, + builder: { + fallbackWarning: + 'Questa espressione non può essere modificata visivamente. Seleziona "Personalizzato" per mantenerla, oppure modifica qualsiasi elemento sottostante per sovrascriverla.', + run: "Correre", + frequency: { + minute: "ogni minuto", + hour: "a ore", + day: "quotidiano", + week: "settimanale", + month: "mensile", + }, + every: "Ogni", + minuteOne: "1 minuto", + minuteOther: "{{count}} minuti", + atMinute: "A ogni minuto", + pastEveryHour: "ogni ora", + at: "A", + on: "Su", + onDay: "In un giorno", + ofEveryMonth: "di ogni mese", + weekdays: { + sun: "Sole", + mon: "Mon", + tue: "Domenica", + wed: "Mercoledì", + thu: "Giovedì", + fri: "Venerdì", + sat: "Sabato", + }, + }, + runHistory: { + back: "Ritorna alle offerte di lavoro", + title: "Cronologia: {{name}}", + schedule: "Programma:", + emptyTitle: + "Al momento, non ci sono stati risultati positivi per questa posizione.", + emptySubtitle: "Avvia l'operazione e visualizza i risultati.", + runNow: "Inizia subito", + table: { + status: "Stato", + started: "Iniziato", + duration: "Durata", + error: "Errore", + }, + stopJob: "Interrompere l'attività lavorativa", + }, + runDetail: { + loading: "Caricamento dei dettagli dell'esecuzione...", + notFound: "Comando non trovato.", + back: "Indietro", + unknownJob: "Posizione non specificata", + runHeading: "{{name}} — Esecuzione #{{id}}", + duration: "Durata: {{value}}", + continueInThread: "Continua nella discussione", + creating: "Creazione...", + threadFailed: "Impossibile creare il thread.", + sections: { + prompt: "Richiesta", + error: "Errore", + thinking: "Pensieri ({{count}})", + toolCalls: "Chiamate a strumenti ({{count}})", + files: "File ({{count}})", + response: "Risposta", + metrics: "Metriche", + }, + metrics: { + promptTokens: "Parole chiave:", + completionTokens: "Token di completamento:", + }, + stopJob: "Interruzione del lavoro", + killing: "Fermare...", + }, + toolCall: { + arguments: "Argomentazioni:", + showResult: "Mostra risultato", + hideResult: "Nascondi risultato", + }, + file: { + unknown: "File sconosciuto", + download: "Scarica", + downloadFailed: "Impossibile scaricare il file.", + types: { + powerpoint: "PowerPoint", + pdf: "Documento in formato PDF", + word: "Documento Word", + spreadsheet: "Foglio di calcolo", + generic: "File", + }, + }, + status: { + completed: "Completato", + failed: "Fallito", + timed_out: "Tempo scaduto", + running: "Corsa", + queued: "In attesa", + }, + }, }; export default TRANSLATIONS; diff --git a/frontend/src/locales/ja/common.js b/frontend/src/locales/ja/common.js index 009f31c4..6593fe97 100644 --- a/frontend/src/locales/ja/common.js +++ b/frontend/src/locales/ja/common.js @@ -108,6 +108,7 @@ const TRANSLATIONS = { "available-channels": { telegram: "テレグラム", }, + "scheduled-jobs": "計画された作業", }, login: { "multi-user": { @@ -1496,6 +1497,164 @@ const TRANSLATIONS = { unknown: "不明", }, }, + scheduledJobs: { + title: "予定されている作業", + enableNotifications: "求人情報の通知をブラウザで許可する", + description: + "定期的に実行されるAIタスクを作成します。これらのタスクは、指定されたスケジュールに従って実行され、オプションのツールを使用してプロンプトを実行し、結果を保存してレビューします。", + newJob: "新しい仕事", + loading: "読み込み中...", + emptyTitle: "現時点で予定されている作業はありません。", + emptySubtitle: "まずは、簡単なものから始めてみましょう。", + table: { + name: "名前", + schedule: "スケジュール", + status: "ステータス", + lastRun: "最後の走行", + nextRun: "次回の開催", + actions: "行動", + }, + confirmDelete: "本当にこの予定された作業を削除してもよろしいですか?", + toast: { + deleted: "求人情報が削除されました", + triggered: "ジョブが正常に実行されました", + triggerFailed: "ジョブの実行が失敗しました", + triggerSkipped: "この仕事については、すでに作業が進んでいます。", + killed: "作業は正常に終了しました", + killFailed: "仕事をやめることができなかった", + }, + row: { + neverRun: "絶対に走らない", + viewRuns: "実行例", + runNow: "今すぐ行動を", + enable: "有効にする", + disable: "無効化", + edit: "編集", + delete: "削除", + }, + modal: { + titleEdit: "予定されたタスクの編集", + titleNew: "新規スケジュールされた作業", + nameLabel: "名前", + namePlaceholder: "例:デイリーニュースダイジェスト", + promptLabel: "指示", + promptPlaceholder: "「各実行時に実行する」という指示...", + scheduleLabel: "スケジュール", + modeBuilder: "建設業者", + modeCustom: "オーダーメイド", + cronPlaceholder: "Cron 形式の指定 (例: 0 9 * * *)", + currentSchedule: "現在のスケジュール:", + toolsLabel: "道具(任意)", + toolsDescription: + "このタスクで使用できるエージェントツールを選択してください。 ツールが選択されていない場合、タスクはツールなしで実行されます。", + toolsSearch: "検索", + toolsNoResults: "該当するツールは見つかりませんでした。", + required: "必要", + requiredFieldsBanner: + "求人を作成するには、必要なすべての項目を記入してください。", + cancel: "キャンセル", + saving: "保存中...", + updateJob: "求人情報の更新", + createJob: "求人を作成する", + jobUpdated: "求人情報が更新されました", + jobCreated: "雇用が創出された", + }, + builder: { + fallbackWarning: + "このテキストは、視覚的に編集することはできません。元のテキストを維持するには、「カスタム」モードに切り替えてください。または、以下の項目を変更することで、このテキストを上書きできます。", + run: "走る", + frequency: { + minute: "1分ごとに", + hour: "時間ごと", + day: "毎日", + week: "毎週", + month: "毎月", + }, + every: "すべて", + minuteOne: "1分", + minuteOther: "{{count}} 分", + atMinute: "分単位で", + pastEveryHour: "過去の、1時間ごとに", + at: "~に", + on: "~について", + onDay: "ある日", + ofEveryMonth: "毎月", + weekdays: { + sun: "太陽", + mon: "月", + tue: "火曜日", + wed: "水曜日", + thu: "木曜日", + fri: "金曜日", + sat: "土曜日", + }, + }, + runHistory: { + back: "求人情報に戻る", + title: "実行履歴: {{name}}", + schedule: "スケジュール:", + emptyTitle: "現時点では、この仕事に対してまだ成果は出ていません。", + emptySubtitle: "現在ジョブを実行し、その結果を確認してください。", + runNow: "今すぐ実行", + table: { + status: "ステータス", + started: "開始", + duration: "期間", + error: "エラー", + }, + stopJob: "仕事の停止", + }, + runDetail: { + loading: "ロード実行の詳細を読み込んでいます...", + notFound: "指定されたプログラムが見つかりませんでした。", + back: "背面", + unknownJob: "不明な職種", + runHeading: "{{name}} — 実行: #{{id}}", + duration: "期間: {{value}}", + continueInThread: "スレッドへの書き込みを続ける", + creating: "作成中...", + threadFailed: "スレッドの作成に失敗しました", + sections: { + prompt: "指示", + error: "エラー", + thinking: "考え ({{count}})", + toolCalls: "ツール呼び出し ({{count}})", + files: "ファイル ({{count}})", + response: "返答", + metrics: "指標", + }, + metrics: { + promptTokens: "プロンプトトークン:", + completionTokens: "完了トークン:", + }, + stopJob: "求人停止", + killing: "停止…", + }, + toolCall: { + arguments: "主張:", + showResult: "結果を表示", + hideResult: "結果を非表示にする", + }, + file: { + unknown: "不明なファイル", + download: "ダウンロード", + downloadFailed: "ファイルのダウンロードに失敗しました", + types: { + powerpoint: "パワーポイント", + pdf: "PDFドキュメント", + word: "Wordドキュメント", + spreadsheet: "スプレッドシート", + generic: "ファイル", + }, + }, + status: { + completed: "完了", + failed: "失敗", + timed_out: "時間切れ", + running: "ランニング", + queued: "待ち列", + }, + }, }; export default TRANSLATIONS; diff --git a/frontend/src/locales/ko/common.js b/frontend/src/locales/ko/common.js index 101882ae..c0aa5eb8 100644 --- a/frontend/src/locales/ko/common.js +++ b/frontend/src/locales/ko/common.js @@ -107,6 +107,7 @@ const TRANSLATIONS = { "available-channels": { telegram: "텔레그램", }, + "scheduled-jobs": "예정된 작업", }, login: { "multi-user": { @@ -1470,6 +1471,164 @@ const TRANSLATIONS = { unknown: "알 수 없음", }, }, + scheduledJobs: { + title: "예정된 작업", + enableNotifications: "채용 결과에 대한 브라우저 알림 활성화", + description: + "정기적으로 실행되는 AI 작업을 생성하고, 일정을 설정합니다. 각 작업은 선택적으로 도구를 사용하여 프롬프트를 실행하고, 결과를 저장하여 검토합니다.", + newJob: "새로운 직업", + loading: "로딩 중...", + emptyTitle: "현재 예약된 작업은 없습니다.", + emptySubtitle: "시작하기 위해 하나를 만들어 보세요.", + table: { + name: "이름", + schedule: "일정", + status: "상태", + lastRun: "마지막 기록", + nextRun: "다음 경주", + actions: "행동", + }, + confirmDelete: "정말로 이 예약된 작업을 삭제하시겠습니까?", + toast: { + deleted: "직책 삭제", + triggered: "직업이 성공적으로 시작되었습니다.", + triggerFailed: "작업 실행에 실패", + triggerSkipped: "이 프로젝트는 이미 진행 중입니다.", + killed: "직업이 성공적으로 종료되었습니다.", + killFailed: "일자리를 유지하지 못함", + }, + row: { + neverRun: "절대 질주하지 마세요", + viewRuns: "실행 횟수", + runNow: "지금 실행하세요", + enable: "활성화", + disable: "비활성화", + edit: "편집", + delete: "삭제", + }, + modal: { + titleEdit: "예정된 작업 수정", + titleNew: "새로 예약된 작업", + nameLabel: "이름", + namePlaceholder: "예: 매일 뉴스 요약", + promptLabel: "요청", + promptPlaceholder: "각 실행 시 실행 지시", + scheduleLabel: "일정", + modeBuilder: "건축업자", + modeCustom: "맞춤형", + cronPlaceholder: "Cron 표현 (예: 0 9 * * *)", + currentSchedule: "현재 일정:", + toolsLabel: "(선택 사항) 도구", + toolsDescription: + "이 작업에서 사용할 수 있는 에이전트 도구를 선택합니다. 선택 사항이 없으면 작업은 어떤 도구 없이 실행됩니다.", + toolsSearch: "검색", + toolsNoResults: "어떤 도구도 해당되지 않습니다", + required: "필수", + requiredFieldsBanner: + "직업을 생성하려면 모든 필수 항목을 정확하게 작성해 주세요.", + cancel: "취소", + saving: "저축 중...", + updateJob: "업데이트", + createJob: "일 만들기", + jobUpdated: "직책 변경", + jobCreated: "새로운 직책 생성", + }, + builder: { + fallbackWarning: + '이 표현은 시각적으로 수정할 수 없습니다. 원본을 유지하려면 "사용자 지정" 모드로 변경하거나, 아래의 내용을 변경하여 덮어쓰십시오.', + run: "달리기", + frequency: { + minute: "매 분마다", + hour: "시간당", + day: "매일", + week: "매주", + month: "매월", + }, + every: "모든", + minuteOne: "1분", + minuteOther: "{{count}} 분", + atMinute: "분", + pastEveryHour: "과거, 매 시간", + at: "~에서", + on: "~에 대해", + onDay: "특정 날", + ofEveryMonth: "매달", + weekdays: { + sun: "태양", + mon: "월요일", + tue: "화요일", + wed: "수요일", + thu: "목요일", + fri: "금요일", + sat: "토", + }, + }, + runHistory: { + back: "이전으로", + title: "실행 기록: {{name}}", + schedule: "일정:", + emptyTitle: "이 업무에 아직 성과가 없습니다.", + emptySubtitle: "현재 작업을 실행하고 결과를 확인하세요.", + runNow: "지금 실행", + table: { + status: "상태", + started: "시작", + duration: "기간", + error: "오류", + }, + stopJob: "직업 중단", + }, + runDetail: { + loading: "로딩 중: 실행 세부 정보...", + notFound: "실행 명령을 찾을 수 없습니다.", + back: "뒤", + unknownJob: "알 수 없는 직업", + runHeading: "{{name}} — 실행: #{{id}}", + duration: "기간: {{value}}", + continueInThread: "스레드에 계속 참여", + creating: "만들기...", + threadFailed: "스레드를 생성하는 데 실패했습니다.", + sections: { + prompt: "요청", + error: "오류", + thinking: "생각 ({{count}})", + toolCalls: "도구 호출 ({{count}})", + files: "파일 ({{count}})", + response: "응답", + metrics: "지표", + }, + metrics: { + promptTokens: "프롬프트 토큰:", + completionTokens: "완료 토큰:", + }, + stopJob: "직업 중단", + killing: "멈추다...", + }, + toolCall: { + arguments: "논거", + showResult: "결과 표시", + hideResult: "결과 숨기기", + }, + file: { + unknown: "알 수 없는 파일", + download: "다운로드", + downloadFailed: "파일 다운로드 실패", + types: { + powerpoint: "파워포인트", + pdf: "PDF 문서", + word: "워드 문서", + spreadsheet: "스프레드시트", + generic: "파일", + }, + }, + status: { + completed: "완료", + failed: "실패", + timed_out: "시간 초과", + running: "달리기", + queued: "대기 중", + }, + }, }; export default TRANSLATIONS; diff --git a/frontend/src/locales/lt/common.js b/frontend/src/locales/lt/common.js index 8ff14200..544c936d 100644 --- a/frontend/src/locales/lt/common.js +++ b/frontend/src/locales/lt/common.js @@ -115,6 +115,7 @@ const TRANSLATIONS = { "available-channels": { telegram: "„Telegram“", }, + "scheduled-jobs": "Planuojami darbai", }, login: { "multi-user": { @@ -1513,6 +1514,166 @@ const TRANSLATIONS = { unknown: "Nenurodytas", }, }, + scheduledJobs: { + title: "Planuotos užduotys", + enableNotifications: + "Įgalinkite naršyklės pranešimus dėl darbo paieškos rezultatų", + description: + "Sukurkite nuolatines AI užduotis, kurios vyks pagal nustatytą grafiką. Kiekviena užduotis atliks užduotį su galimais įrankiais ir išsaugos rezultatą, kad galėtų būti peržiūrėta.", + newJob: "Nauja darbo pozicija", + loading: "Įkėlimas...", + emptyTitle: "Nėra nurodytų uždučių.", + emptySubtitle: "Sukurkite vieną, kad pradėtumėte.", + table: { + name: "Pavadinimas", + schedule: "Programinė tvarka", + status: "Statusas", + lastRun: "Paskutinė kelionė", + nextRun: "Kitas maršrutas", + actions: "Veikmai", + }, + confirmDelete: + "Ar esate tikri, kad norite ištrinti šią užduotį, kurią įtraukėte į planą?", + toast: { + deleted: "Darbas ištrintas", + triggered: "Darbas buvo sėkmingai inicijuotas.", + triggerFailed: "Nepavyko inicijuoti užduoties", + triggerSkipped: "Šis projektas jau pradėtas vykdyti.", + killed: "Darbas sėkmingai baigtas", + killFailed: "Nepavyko sustabdyti darbą", + }, + row: { + neverRun: "Niekada nesnydžkite", + viewRuns: "Vaizdo įrašai", + runNow: "Pradėkite dabar", + enable: "Aaktyvinti", + disable: "Išjungti", + edit: "Redaguo", + delete: "Ištrinkti", + }, + modal: { + titleEdit: "Redaguoti užplanuotą užduotį", + titleNew: "Naujas planuojamas darbas", + nameLabel: "Pavadinimas", + namePlaceholder: "Pavyzdžiui, kasdieninė naujienų apžvalga", + promptLabel: "Instrukcija", + promptPlaceholder: "Instrukcija, kad reikia vykdyti kiekvieną kartą...", + scheduleLabel: "Programėlė", + modeBuilder: "Statybininkas", + modeCustom: "Individualus", + cronPlaceholder: "Laiko išraiška (pvz., 0 9 * * *)", + currentSchedule: "Dabartinė tvarka:", + toolsLabel: "Įrankės (neprivalomi)", + toolsDescription: + "Pasirinkite, kokius agento įrankius šiandiena gali naudoti. Jei nė vienas įrankis nėra pasirinktas, šiandiena veiks be jokių įrankių.", + toolsSearch: "Paieška", + toolsNoResults: "Nėra įrankių, kurie atitinka", + required: "Reikalingas", + requiredFieldsBanner: + "Prašome užpildyti visus reikalingus laukelius, kad būtų galima sukurti darbo skelbimą.", + cancel: "Anuliu", + saving: "Taupymas...", + updateJob: "Atnaujinti darbą", + createJob: "Sukurti darbo poziciją", + jobUpdated: "Darbas atnaujintas", + jobCreated: "Sukurtas darbas", + }, + builder: { + fallbackWarning: + 'Šią frazę negalima redaguoti vizualiai. Norėdami ją išsaugoti, pasirinkite "Individualus" režimą, arba pakeiskite žemiau esančias ​​vertimes, kad ją pakeistumėte.', + run: "Bėgti", + frequency: { + minute: "būdami kiekvieną minutę", + hour: "valandinės", + day: "kasdieninis", + week: "kas savaitę", + month: "kas mėnesį", + }, + every: "Kiekvienas", + minuteOne: "1 minutė", + minuteOther: "{{count}} minučių", + atMinute: "Minutė po minutės", + pastEveryHour: "būdami kiekvieną valandą", + at: "At", + on: "Apie", + onDay: "Jau", + ofEveryMonth: "iš kiekvieno mėnesio", + weekdays: { + sun: "Saulė", + mon: "Moneta", + tue: "Trečiadienis", + wed: "Trečiadienis", + thu: "Ketvirtadienis", + fri: "Penktadienis", + sat: "Sekmadienis", + }, + }, + runHistory: { + back: "Grįžti į paieškas", + title: "Paleidimo istorija: {{name}}", + schedule: "Programinė tvarka:", + emptyTitle: "Kol šis darbas nėra baigtas", + emptySubtitle: "Atlikite šią užduotį dabar ir peržiūrėkite rezultatus.", + runNow: "Pradėkite dabar", + table: { + status: "Statusas", + started: "Pradėtas", + duration: "Tr উপক", + error: "Klaida", + }, + stopJob: "Pamesti darbas", + }, + runDetail: { + loading: "Įkraudami važiavimo duomenis...", + notFound: "Nepavyko rasti.", + back: "Atgal", + unknownJob: "Nenurodytas darbas", + runHeading: "{{name}} – Treniruotė #{{id}}", + duration: "Trūkumas: {{value}}", + continueInThread: "Toliau diskusijoje", + creating: "Kurimas...", + threadFailed: "Nepavyko sukurti temą", + sections: { + prompt: "Įspūdis", + error: "Klaida", + thinking: "Mintys ({{count}})", + toolCalls: "Naudojamų įrankių kvietimai ({{count}})", + files: "Failai ({{count}})", + response: "Atgarsas", + metrics: "Matmenys", + }, + metrics: { + promptTokens: "Įspūdingos žymės:", + completionTokens: "Baigimo žymekliai:", + }, + stopJob: "Nutraukite darbą", + killing: "Sustabdyti...", + }, + toolCall: { + arguments: "Argumentai:", + showResult: "Parodyti rezultatą", + hideResult: "Slėpti rezultatą", + }, + file: { + unknown: "Nežinomas failas", + download: "Atsisiųsti", + downloadFailed: "Nepavyko atsisiųsti failą", + types: { + powerpoint: "PowerPoint", + pdf: "PDF dokumentas", + word: "Dokumentas, kurį galima redaguoti Microsoft Word programoje", + spreadsheet: "Spalvotas lapas (tabelis)", + generic: "Failas", + }, + }, + status: { + completed: "Baigtas", + failed: "Nepavyko", + timed_out: "Laikas baigėsi", + running: "Bėgimas", + queued: "Apsisukęs", + }, + }, }; export default TRANSLATIONS; diff --git a/frontend/src/locales/lv/common.js b/frontend/src/locales/lv/common.js index 8060aab9..2620b3a4 100644 --- a/frontend/src/locales/lv/common.js +++ b/frontend/src/locales/lv/common.js @@ -108,6 +108,7 @@ const TRANSLATIONS = { "available-channels": { telegram: "Telegram", }, + "scheduled-jobs": "Plānotas darba uzdevumi", }, login: { "multi-user": { @@ -1523,6 +1524,166 @@ const TRANSLATIONS = { unknown: "Nezināms", }, }, + scheduledJobs: { + title: "Plānotas darba uzdevumi", + enableNotifications: + "Ievērojiet pārlūkprogrammā atgādinājumus par darbā pieņemšanas rezultātiem", + description: + "Izveidot atkārtotas AI uzdevumus, kas tiek veiktas saskaņā ar noteiktu grafiku. Katrs uzdevums izmanto norādīto, kā arī var izmantot papildu rīkus, un rezultāts tiek saglabāts, lai to varētu pārskatīt.", + newJob: "Jauna darba pozīcija", + loading: "Ielāde...", + emptyTitle: "Vēl nav plānotu darbu", + emptySubtitle: "Izveidot vienu, lai sāktu.", + table: { + name: "Vārds", + schedule: "Kalendārs", + status: "Statuss", + lastRun: "Pēdējā brauciens", + nextRun: "Nākamā trase", + actions: "Rīcības", + }, + confirmDelete: "Vai jūs noteikti vēlaties dzēst šo plānoto darbu?", + toast: { + deleted: "Darbs izdzēsts", + triggered: "Darbs veiksmīgi izpildīts.", + triggerFailed: "Neizdevās aktivizēt darbu", + triggerSkipped: "Šī darba izpilde jau ir sākusies.", + killed: "Darbs veiksmīgi pārtraukts", + killFailed: "Neizdevās pārtraukt darbu", + }, + row: { + neverRun: "Nekad neiet sprintā", + viewRuns: "Skatīšanās reģīni", + runNow: "Runāt tagad", + enable: "Aktivizēt", + disable: "Atspējot", + edit: "Rediģēt", + delete: "Dzēst", + }, + modal: { + titleEdit: "Ieraksta plānoto darbu", + titleNew: "Jauna plānota darba", + nameLabel: "Vārds", + namePlaceholder: "piemēram, ikdienas ziņu apkopojums", + promptLabel: "Iekšēja aicinājums", + promptPlaceholder: + "Instrukcija, kas norāda, ka programmu jāizpilda katrā darbībā...", + scheduleLabel: "Kalendārs", + modeBuilder: "Celtniejs", + modeCustom: "Pielāgotais", + cronPlaceholder: "Laika izteiksme (piemēram, 0 9 * * *)", + currentSchedule: "Pašreizējais grafiks:", + toolsLabel: "Rīki (pēc izvēles)", + toolsDescription: + "Izvēlieties, kādas programmatūras rīkus šis darbs var izmantot. Ja neviens rīks nav izvēlēts, darbs tiks veikts bez jebkurām programmatūras rīkiem.", + toolsSearch: "Meklēšana", + toolsNoResults: "Nav atrasts neviens atbilstošs rīks.", + required: "Nepieciešams", + requiredFieldsBanner: + "Lūdzu, aizpildiet visus obligātās laukus, lai izveidotu darbā.", + cancel: "Atcelt", + saving: "Ietaupīt...", + updateJob: "Atjaunināt darbu", + createJob: "Izveidot darbu", + jobUpdated: "Darba statuss atjaunināts", + jobCreated: "Izveidots darbs", + }, + builder: { + fallbackWarning: + 'Šo izteikumu nevar redzami rediģēt. Izmantojiet "Custom" režīmu, lai to saglabātu, vai mainiet jebko zemāk, lai to pārvietotu.', + run: "Runāt", + frequency: { + minute: "katru minūti", + hour: "laika periodā", + day: "katru dienu", + week: "katru nedēļu", + month: "mēnesī", + }, + every: "Katrs", + minuteOne: "1 minūte", + minuteOther: "{{count}} minūtes", + atMinute: "Katrā minūtē", + pastEveryHour: "katru stundu", + at: "At", + on: "Par", + onDay: "Vienā dienā", + ofEveryMonth: "katram mēnešam", + weekdays: { + sun: "Suņa", + mon: "Ikdiena", + tue: "Otrdiena", + wed: "Trešdiena", + thu: "Ceturtdien", + fri: "Svētdiena", + sat: "Sastāvējums", + }, + }, + runHistory: { + back: "Atgriezties uz darba sludinājumiem", + title: "Runas vēsture: {{name}}", + schedule: "Kalendārs:", + emptyTitle: "Vēl nav veikti nekādi darbi", + emptySubtitle: "Veiciet darbu šajā laikā un apskatiet tā rezultātus.", + runNow: "Runā tagad", + table: { + status: "Statuss", + started: "Sācies", + duration: "Laiks", + error: "Kļūda", + }, + stopJob: "Aizstāt darbu", + }, + runDetail: { + loading: "Ievadīšanas darbību apraksts...", + notFound: "Nav atrasta.", + back: "Atpakaļ", + unknownJob: "Nevērtēts darbs", + runHeading: "{{name}} – Runa Nr. {{id}}", + duration: "Laiks: {{value}}", + continueInThread: "Turpināt diskusiju", + creating: "Izveidot...", + threadFailed: "Izdevās izveidot tēmu", + sections: { + prompt: "Iekšējais stimuls", + error: "Kļūda", + thinking: "Domas ({{count}})", + toolCalls: "Rīku izmantošana ({{count}})", + files: "Faili ({{count}})", + response: "Atbilde", + metrics: "Mērījumi", + }, + metrics: { + promptTokens: "Ievade:", + completionTokens: "Pilnībā aprakstīti elementi:", + }, + stopJob: "Aizstāt darbu", + killing: "Apstādam...", + }, + toolCall: { + arguments: "Argumenti:", + showResult: "Rādīt rezultātu", + hideResult: "Noslēgt rezultātu", + }, + file: { + unknown: "Nezināms failss", + download: "Lejupielādēt", + downloadFailed: "Neizdevās lejupielādēt failu", + types: { + powerpoint: "PowerPoint", + pdf: "PDF dokumenta", + word: "Vārdu dokumenta faila", + spreadsheet: "Tabulas veidols", + generic: "Faila", + }, + }, + status: { + completed: "Pilnots", + failed: "Neizdevies", + timed_out: "Laiks esot beidzies", + running: "Skriešana", + queued: "Iekļauts rindā", + }, + }, }; export default TRANSLATIONS; diff --git a/frontend/src/locales/nl/common.js b/frontend/src/locales/nl/common.js index 427b0b1b..404c4527 100644 --- a/frontend/src/locales/nl/common.js +++ b/frontend/src/locales/nl/common.js @@ -109,6 +109,7 @@ const TRANSLATIONS = { "available-channels": { telegram: "Telegram", }, + "scheduled-jobs": "Geplande taken", }, login: { "multi-user": { @@ -1524,6 +1525,167 @@ const TRANSLATIONS = { unknown: "Onbekend", }, }, + scheduledJobs: { + title: "Geplande taken", + enableNotifications: + "Activeer browser notificaties voor resultaten van vacatures.", + description: + "Maak herhaalde AI-taken die volgens een schema worden uitgevoerd. Elke taak voert een prompt uit met optionele tools en slaat het resultaat op voor beoordeling.", + newJob: "Nieuwe baan", + loading: "Laad...", + emptyTitle: "Er zijn nog geen geplande taken.", + emptySubtitle: "Maak er één om aan de slag te gaan.", + table: { + name: "Naam", + schedule: "Planning/Tijdschema", + status: "Status", + lastRun: "Laatste rit", + nextRun: "Volgende keer", + actions: "Acties", + }, + confirmDelete: + "Bent u er zeker van dat u deze geplande taak wilt verwijderen?", + toast: { + deleted: "Vacature verwijderd", + triggered: "De werkzaamheden zijn succesvol afgerond.", + triggerFailed: "Niet mogelijk om de taak uit te voeren", + triggerSkipped: "Er is al begonnen met het uitvoeren van dit project.", + killed: "De werkzaamheden zijn succesvol beëindigd.", + killFailed: "Niet in staat geweest om het werk te stoppen.", + }, + row: { + neverRun: "Nooit versnellen", + viewRuns: "Bekijk de resultaten", + runNow: "Begin nu", + enable: "Aan zetten/Activeren", + disable: "Uitschakelen", + edit: "Bewerk", + delete: "Verwijderen", + }, + modal: { + titleEdit: "Wijzig geplande taak", + titleNew: "Nieuwe geplande taak", + nameLabel: "Naam", + namePlaceholder: "bijvoorbeeld: Dagelijkse nieuwsbrief", + promptLabel: "Aanvraag", + promptPlaceholder: + "De instructie om uit te voeren bij elke uitvoering...", + scheduleLabel: "Planning/Tijdschema", + modeBuilder: "Bouwer", + modeCustom: "Op maat gemaakt", + cronPlaceholder: "Cron-expressie (bijvoorbeeld 0 9 * * *)", + currentSchedule: "Huidelijk schema:", + toolsLabel: "Benodigde hulpmiddelen (optioneel)", + toolsDescription: + "Selecteer welke agent-tools deze taak kan gebruiken. Als er geen tools zijn geselecteerd, voert de taak uit zonder enige tools.", + toolsSearch: "Zoeken", + toolsNoResults: "Geen van de beschikbare gereedschappen komt overeen.", + required: "Vereist", + requiredFieldsBanner: + "Vul al de vereiste velden in om een vacature aan te maken.", + cancel: "Annuleren", + saving: "Opslaan...", + updateJob: "Werk bijwerken", + createJob: "Vacature aanmaken", + jobUpdated: "Functie bijgewerkt", + jobCreated: "Werk gecreëerd", + }, + builder: { + fallbackWarning: + 'Deze tekst kan niet visueel worden bewerkt. Kies voor "Aanpassen" om deze te behouden, of wijzig hieronder om deze te vervangen.', + run: "Lopen", + frequency: { + minute: "per minuut", + hour: "per uur", + day: "dagelijks", + week: "wekelijks", + month: "maandelijks", + }, + every: "Elke", + minuteOne: "1 minuut", + minuteOther: "{{count}} minuten", + atMinute: "Bij het begin van", + pastEveryHour: "elke uur", + at: "Bij", + on: "Op", + onDay: "Op een dag", + ofEveryMonth: "of per maand", + weekdays: { + sun: "Zon", + mon: "Maandag", + tue: "Maandag", + wed: "Wedstrijd", + thu: "Donderdag", + fri: "Vrijdag", + sat: "Zaterdag", + }, + }, + runHistory: { + back: "Terug naar vacatures", + title: "Historie: {{name}}", + schedule: "Planning:", + emptyTitle: "Er zijn nog geen resultaten behaald voor deze opdracht.", + emptySubtitle: "Voer de taak nu uit en bekijk de resultaten.", + runNow: "Start nu", + table: { + status: "Status", + started: "Begonnen", + duration: "Duur", + error: "Fout", + }, + stopJob: "Werkonderbreking", + }, + runDetail: { + loading: "Laad details van de uitvoering in...", + notFound: "Geen uitvoering gevonden.", + back: "Terug", + unknownJob: "Onbekende functie", + runHeading: "{{name}} — Uitvoering #{{id}}", + duration: "Duur: {{value}}", + continueInThread: "Blijf reageren in dit gesprek", + creating: "Creëren...", + threadFailed: "Niet in staat om een nieuwe thread te creëren.", + sections: { + prompt: "Aanvraag", + error: "Fout", + thinking: "Denken ({{count}})", + toolCalls: "Aanroepen van tools ({{count}})", + files: "Bestanden ({{count}})", + response: "Antwoord", + metrics: "Meetwaarden", + }, + metrics: { + promptTokens: "Aanwijstokens:", + completionTokens: "Voltooiingstokens:", + }, + stopJob: "Werkonderbreking", + killing: "Stoppen...", + }, + toolCall: { + arguments: "Argumenten:", + showResult: "Toon resultaat", + hideResult: "Resultaat verbergen", + }, + file: { + unknown: "Onbekend bestand", + download: "Downloaden", + downloadFailed: "Fout bij het downloaden van het bestand", + types: { + powerpoint: "PowerPoint", + pdf: "PDF-document", + word: "Word-document", + spreadsheet: "Spreadsheet (tabellenblad)", + generic: "Bestand", + }, + }, + status: { + completed: "Afgerond", + failed: "Mislukt", + timed_out: "Tijdslimiet bereikt", + running: "Hardlopen", + queued: "In de wachtrij", + }, + }, }; export default TRANSLATIONS; diff --git a/frontend/src/locales/pl/common.js b/frontend/src/locales/pl/common.js index c49fe36c..f4133431 100644 --- a/frontend/src/locales/pl/common.js +++ b/frontend/src/locales/pl/common.js @@ -109,6 +109,7 @@ const TRANSLATIONS = { "available-channels": { telegram: "Telegram", }, + "scheduled-jobs": "Zaplanowane zadania", }, login: { "multi-user": { @@ -1527,6 +1528,166 @@ const TRANSLATIONS = { unknown: "Nieznany", }, }, + scheduledJobs: { + title: "Zaplanowane zadania", + enableNotifications: + "Włącz powiadomienia w przeglądarce dotyczące wyników rekrutacji", + description: + "Stwórz powtarzalne zadania oparte na sztucznej inteligencji, które będą wykonywane według zadanego harmonogramu. Każde zadanie będzie zawierało zapytanie oraz opcjonalne narzędzia, a także zapisze wynik, który będzie można później przejrzeć.", + newJob: "Nowa praca", + loading: "Ładowanie...", + emptyTitle: "Na razie nie ma zaplanowanych zadań.", + emptySubtitle: "Stwórz jeden, aby zacząć.", + table: { + name: "Imię", + schedule: "Harmonogram", + status: "Stan", + lastRun: "Ostatnia przejażdżka", + nextRun: "Następna rozgrywka", + actions: "Działania", + }, + confirmDelete: "Czy na pewno chcesz usunąć tę zaplanowaną czynność?", + toast: { + deleted: "Usunięto pracę", + triggered: "Zlecenie zostało pomyślnie uruchomione.", + triggerFailed: "Nie udało się uruchomić zadania.", + triggerSkipped: "Prace nad tym projektem są już w toku.", + killed: "Praca została pomyślnie zakończona.", + killFailed: "Nie udało się zatrzymać pracy", + }, + row: { + neverRun: "Nigdy nie należy jechać zbyt szybko.", + viewRuns: "Wyświetlanie/Odtwarzanie", + runNow: "Zacznij od razu", + enable: "Włącz", + disable: "Wyłączyć", + edit: "Edytuj", + delete: "Usuń", + }, + modal: { + titleEdit: "Edytuj zaplanowaną pracę", + titleNew: "Nowe zaplanowane zadanie", + nameLabel: "Imię", + namePlaceholder: "np. Codzienne podsumowanie wiadomości", + promptLabel: "Instrukcja", + promptPlaceholder: + "Instrukcja dotycząca uruchamiania w każdym przypadku...", + scheduleLabel: "Harmonogram", + modeBuilder: "Budowniczy", + modeCustom: "Dostosowane", + cronPlaceholder: "Wyrażenie crona (np. 0 9 * * *)", + currentSchedule: "Obecny harmonogram:", + toolsLabel: "Narzędzia (opcjonalne)", + toolsDescription: + "Wybierz, które narzędzia dla agentów mogą być używane w tym przypadku. Jeśli żadne narzędzia nie zostaną wybrane, praca będzie wykonywana bez żadnych narzędzi.", + toolsSearch: "Wyszukaj", + toolsNoResults: "Żaden z dostępnych narzędzi nie pasuje.", + required: "Wymagane", + requiredFieldsBanner: + "Prosimy o wypełnienie wszystkich wymaganych pól, aby utworzyć ogłoszenie o pracę.", + cancel: "Anuluj", + saving: "Oszczędzanie...", + updateJob: "Aktualizacja oferty pracy", + createJob: "Utwórz ofertę pracy", + jobUpdated: "Informacja o aktualizacji oferty pracy", + jobCreated: "Utworzono stanowisko", + }, + builder: { + fallbackWarning: + 'To wyrażenie nie można edytować graficznie. Jeśli chcesz je zachować, przejdź do opcji "Custom". W przeciwnym razie możesz zmienić dowolne elementy poniżej, aby je zastąpić.', + run: "Bieg", + frequency: { + minute: "co minutę", + hour: "godzinne", + day: "codzienny", + week: "tygodniowy", + month: "miesięczny", + }, + every: "Każdy", + minuteOne: "1 minuta", + minuteOther: "{{count}} minut", + atMinute: "W określonym momencie", + pastEveryHour: "przeszłe, co godzinę", + at: "W", + on: "Na", + onDay: "W dniu", + ofEveryMonth: "każdego miesiąca", + weekdays: { + sun: "Słońce", + mon: "Monety", + tue: "Wtorek", + wed: "Środa", + thu: "Czwartek", + fri: "Piątek", + sat: "Sobota", + }, + }, + runHistory: { + back: "Powrót do ofert pracy", + title: "Historia uruchomień: {{name}}", + schedule: "Harmonogram:", + emptyTitle: "Na razie nie udało się wykonać żadnych zadań.", + emptySubtitle: "Uruchom teraz zadanie i sprawdź jego wyniki.", + runNow: "Rozpocznij teraz", + table: { + status: "Stan", + started: "Zaczęto", + duration: "Czas trwania", + error: "Błąd", + }, + stopJob: "Zakończ pracę", + }, + runDetail: { + loading: "Wczytywanie szczegółów przebiegu...", + notFound: "Nie znaleziono.", + back: "Wstecz", + unknownJob: "Nieznane stanowisko", + runHeading: "{{name}} – Uruchom {{id}}", + duration: "Czas trwania: {{value}}", + continueInThread: "Kontynuuj dyskusję", + creating: "Tworzenie...", + threadFailed: "Nie udało się utworzyć wątku.", + sections: { + prompt: "Instrukcja", + error: "Błąd", + thinking: "Przemyślenia ({{count}})", + toolCalls: "Wywoływanie narzędzi ({{count}})", + files: "Pliki ({{count}})", + response: "Odpowiedź", + metrics: "Wskaźniki", + }, + metrics: { + promptTokens: "Słowa kluczowe:", + completionTokens: "Tokeny zakończenia:", + }, + stopJob: "Zakończ pracę", + killing: "Przestań...", + }, + toolCall: { + arguments: "Argumenty:", + showResult: "Wyświetl wynik", + hideResult: "Ukryj wynik", + }, + file: { + unknown: "Nieznany plik", + download: "Pobierz", + downloadFailed: "Nie udało się pobrać pliku", + types: { + powerpoint: "Prezentacja w programie PowerPoint", + pdf: "Dokument w formacie PDF", + word: "Dokument w formacie Word", + spreadsheet: "Arkusz kalkulacyjny", + generic: "Plik", + }, + }, + status: { + completed: "Zakończone", + failed: "Nie udało się", + timed_out: "Czas wymarł", + running: "Bieganie", + queued: "W kolejce", + }, + }, }; export default TRANSLATIONS; diff --git a/frontend/src/locales/pt_BR/common.js b/frontend/src/locales/pt_BR/common.js index 540503e3..21f95f2d 100644 --- a/frontend/src/locales/pt_BR/common.js +++ b/frontend/src/locales/pt_BR/common.js @@ -108,6 +108,7 @@ const TRANSLATIONS = { "available-channels": { telegram: "Telegram", }, + "scheduled-jobs": "Tarefas Agendadas", }, login: { "multi-user": { @@ -1501,6 +1502,165 @@ const TRANSLATIONS = { unknown: "Desconhecido", }, }, + scheduledJobs: { + title: "Tarefas Agendadas", + enableNotifications: + "Ative as notificações do navegador para resultados de emprego.", + description: + "Crie tarefas de IA recorrentes que sejam executadas em um determinado horário. Cada tarefa executa um prompt com ferramentas opcionais e salva o resultado para revisão.", + newJob: "Novo emprego", + loading: "Carregando...", + emptyTitle: "Ainda não há tarefas agendadas.", + emptySubtitle: "Crie um para começar.", + table: { + name: "Nome", + schedule: "Horário", + status: "Estado", + lastRun: "Última corrida", + nextRun: "Próxima corrida", + actions: "Ações", + }, + confirmDelete: "Tem certeza de que deseja excluir esta tarefa agendada?", + toast: { + deleted: "Emprego excluído", + triggered: "A tarefa foi executada com sucesso.", + triggerFailed: "Não foi possível iniciar a tarefa.", + triggerSkipped: "O projeto já está em andamento.", + killed: "A tarefa foi concluída com sucesso.", + killFailed: "Não conseguiu impedir a demissão.", + }, + row: { + neverRun: "Nunca corri", + viewRuns: "Visualizações/Reproduções", + runNow: "Corra agora", + enable: "Ativar", + disable: "Desativar", + edit: "Editar", + delete: "Excluir", + }, + modal: { + titleEdit: "Editar tarefa agendada", + titleNew: "Novo Trabalho Agendado", + nameLabel: "Nome", + namePlaceholder: "ex: Resumo diário de notícias", + promptLabel: "Solicitação", + promptPlaceholder: "A instrução para executar em cada execução...", + scheduleLabel: "Cronograma", + modeBuilder: "Construtor", + modeCustom: "Personalizado", + cronPlaceholder: "Expressão de cron (por exemplo, 0 9 * * *)", + currentSchedule: "Agenda atual:", + toolsLabel: "Ferramentas (Opcional)", + toolsDescription: + "Selecione quais ferramentas do agente podem ser utilizadas nesta tarefa. Se nenhuma ferramenta for selecionada, a tarefa será executada sem o uso de nenhuma ferramenta.", + toolsSearch: "Pesquisar", + toolsNoResults: "Não foram encontradas ferramentas correspondentes.", + required: "Requerido", + requiredFieldsBanner: + "Por favor, preencha todos os campos obrigatórios para criar o anúncio de emprego.", + cancel: "Cancelar", + saving: "Economizando...", + updateJob: "Atualizar Vaga", + createJob: "Criar Vaga", + jobUpdated: "Emprego atualizado", + jobCreated: "Emprego criado", + }, + builder: { + fallbackWarning: + 'Esta expressão não pode ser editada visualmente. Se desejar mantê-la, selecione "Personalizado". Caso contrário, altere qualquer um dos campos abaixo para substituí-la.', + run: "Correr", + frequency: { + minute: "a cada minuto", + hour: "por hora", + day: "diário", + week: "semanal", + month: "mensal", + }, + every: "Cada", + minuteOne: "1 minuto", + minuteOther: "{{count}} minutos", + atMinute: "Em minuto", + pastEveryHour: "a cada hora", + at: "Em", + on: "Sobre", + onDay: "Em um dia", + ofEveryMonth: "de cada mês", + weekdays: { + sun: "Sol", + mon: "Individual", + tue: "Terça-feira", + wed: "Quarta-feira", + thu: "Quinta-feira", + fri: "Dia de sexta-feira", + sat: "Sábado", + }, + }, + runHistory: { + back: "Voltar para as vagas", + title: "Histórico de Execuções: {{name}}", + schedule: "Horário:", + emptyTitle: "Ainda não houve progresso nesta tarefa.", + emptySubtitle: "Execute a tarefa agora e visualize os resultados.", + runNow: "Comece agora", + table: { + status: "Estado", + started: "Começou", + duration: "Duração", + error: "Erro", + }, + stopJob: "Interromper o emprego", + }, + runDetail: { + loading: "Carregando detalhes da execução...", + notFound: "Não foi encontrado nenhum resultado.", + back: "Retorno", + unknownJob: "Cargo não especificado", + runHeading: "{{name}} — Executar a tarefa #{{id}}", + duration: "Duração: {{value}}", + continueInThread: "Continue na discussão", + creating: "Criando...", + threadFailed: "Falhou ao criar a thread.", + sections: { + prompt: "Solicitação", + error: "Erro", + thinking: "Pensamentos ({{count}})", + toolCalls: "Chamadas de ferramentas ({{count}})", + files: "Arquivos ({{count}})", + response: "Resposta", + metrics: "Métricas", + }, + metrics: { + promptTokens: "Palavras-chave de gatilho:", + completionTokens: "Tokens de conclusão:", + }, + stopJob: "Interromper o emprego", + killing: "Parar...", + }, + toolCall: { + arguments: "Argumentos:", + showResult: "Exibir resultado", + hideResult: "Esconder resultado", + }, + file: { + unknown: "Arquivo desconhecido", + download: "Baixar", + downloadFailed: "Falha ao baixar o arquivo", + types: { + powerpoint: "PowerPoint", + pdf: "Documento em formato PDF", + word: "Documento em formato Word", + spreadsheet: "Planilha", + generic: "Arquivo", + }, + }, + status: { + completed: "Concluído", + failed: "Falhou", + timed_out: "Tempo esgotado", + running: "Corrida", + queued: "Em espera", + }, + }, }; export default TRANSLATIONS; diff --git a/frontend/src/locales/ro/common.js b/frontend/src/locales/ro/common.js index 1cc156f3..540a1bde 100644 --- a/frontend/src/locales/ro/common.js +++ b/frontend/src/locales/ro/common.js @@ -109,6 +109,7 @@ const TRANSLATIONS = { "available-channels": { telegram: "Telegram", }, + "scheduled-jobs": "Sarcini programate", }, login: { "multi-user": { @@ -1530,6 +1531,167 @@ const TRANSLATIONS = { unknown: "Necunoscut", }, }, + scheduledJobs: { + title: "Sarcini programate", + enableNotifications: + "Activați notificările din browser pentru rezultatele căutării de locuri de muncă.", + description: + "Creați sarcini AI repetitive, care rulează conform unui program. Fiecare sarcină execută un prompt, folosind opțional anumite instrumente, și salvează rezultatul pentru a fi revizuit ulterior.", + newJob: "Loc de muncă nou", + loading: "Se încarcă...", + emptyTitle: "Momentan, nu există sarcini programate.", + emptySubtitle: "Creați unul pentru a începe.", + table: { + name: "Nume", + schedule: "Program", + status: "Stare", + lastRun: "Ultima cursă", + nextRun: "Următoarea cursă", + actions: "Acțiuni", + }, + confirmDelete: + "Sunteți sigur că doriți să eliminați această sarcină programată?", + toast: { + deleted: "Locul de muncă a fost șters", + triggered: "Job-ul a fost executat cu succes.", + triggerFailed: "Nu a reușit să declanșeze execuția.", + triggerSkipped: + "Procesul de licitație pentru acest proiect este deja în desfășurare.", + killed: "Procesul de angajare s-a încheiat cu succes.", + killFailed: "Nu am reușit să opresc activitatea.", + }, + row: { + neverRun: "Nu alerga", + viewRuns: "Rute disponibile", + runNow: "Începeți acum", + enable: "Activează", + disable: "Dezactivează", + edit: "Editează", + delete: "Șterge", + }, + modal: { + titleEdit: "Modifică programarea unei sarcini", + titleNew: "Job nou programat", + nameLabel: "Nume", + namePlaceholder: "de exemplu, „Rezumatul zilnic de știri”", + promptLabel: "Solicitare", + promptPlaceholder: "Instrucțiunea de a rula la fiecare execuție...", + scheduleLabel: "Program", + modeBuilder: "Constructor", + modeCustom: "Personalizat", + cronPlaceholder: "Expresia cron (de exemplu, 0 9 * * *)", + currentSchedule: "Programul actual:", + toolsLabel: "Unelte (opționale)", + toolsDescription: + "Selectați instrumentele disponibile pentru acest job. Dacă niciun instrument nu este selectat, job-ul va rula fără a utiliza niciun instrument.", + toolsSearch: "Caută", + toolsNoResults: "Nu există unelte potrivite.", + required: "Necesare", + requiredFieldsBanner: + "Vă rugăm să completați toate câmpurile obligatorii pentru a crea o ofertă de loc de muncă.", + cancel: "Anula", + saving: "Economisire...", + updateJob: "Actualizare post", + createJob: "Creați o nouă poziție", + jobUpdated: "Postul a fost actualizat", + jobCreated: "Loc de muncă creat", + }, + builder: { + fallbackWarning: + 'Această expresie nu poate fi modificată vizual. Selectați "Personalizat" pentru a o păstra, sau modificați orice element de mai jos pentru a o suprascrie.', + run: "Alătura", + frequency: { + minute: "în fiecare minut", + hour: "pe oră", + day: "zilnic", + week: "săptămânal", + month: "lunar", + }, + every: "Fiecare", + minuteOne: "1 minut", + minuteOther: "{{count}} minute(s)", + atMinute: "La minut", + pastEveryHour: "în fiecare oră", + at: "La", + on: "În", + onDay: "Într-o zi", + ofEveryMonth: "pentru fiecare lună", + weekdays: { + sun: "Soare", + mon: "O singură", + tue: "Marți", + wed: "Miercuri", + thu: "Joi", + fri: "Ziua de vineri", + sat: "Sat", + }, + }, + runHistory: { + back: "Înapoi la anunțuri de angajare", + title: "Istoric de rulare: {{name}}", + schedule: "Program:", + emptyTitle: "Nu am obținut încă rezultate pentru acest proiect.", + emptySubtitle: "Executați sarcina acum și verificați rezultatele.", + runNow: "Începeți acum", + table: { + status: "Stare", + started: "A început", + duration: "Durată", + error: "Eroare", + }, + stopJob: "Întrerupeți activitatea", + }, + runDetail: { + loading: "Încărcare detalii despre rulare...", + notFound: "Nu s-a găsit.", + back: "Înapoi", + unknownJob: "Loc de muncă necunoscut", + runHeading: "{{name}} — Executarea #{{id}}", + duration: "Durată: {{value}}", + continueInThread: "Continuă în acest thread", + creating: "Crearea...", + threadFailed: "Nu a reușit să creeze thread-ul.", + sections: { + prompt: "Solicitare", + error: "Eroare", + thinking: "Gânduri ({{count}})", + toolCalls: "Apeluri către instrumente ({{count}})", + files: "Fișiere ({{count}})", + response: "Răspuns", + metrics: "Indicatori", + }, + metrics: { + promptTokens: "Cuvinte-cheie:", + completionTokens: "Token-uri de finalizare:", + }, + stopJob: "Încetarea activității", + killing: "Oprire...", + }, + toolCall: { + arguments: "Argumente:", + showResult: "Afișează rezultatul", + hideResult: "Ascunde rezultatul", + }, + file: { + unknown: "Fișier necunoscut", + download: "Descarcă", + downloadFailed: "Nu a reușit să descarce fișierul", + types: { + powerpoint: "PowerPoint", + pdf: "Fișier PDF", + word: "Fișier Word", + spreadsheet: "Fișă de calcul", + generic: "Fișier", + }, + }, + status: { + completed: "Finalizat", + failed: "Eșuat", + timed_out: "Timpul a expirat", + running: "Cursa", + queued: "În așteptare", + }, + }, }; export default TRANSLATIONS; diff --git a/frontend/src/locales/ru/common.js b/frontend/src/locales/ru/common.js index 7391c169..f25f9661 100644 --- a/frontend/src/locales/ru/common.js +++ b/frontend/src/locales/ru/common.js @@ -108,6 +108,7 @@ const TRANSLATIONS = { "available-channels": { telegram: "Телеграм", }, + "scheduled-jobs": "Запланированные задачи", }, login: { "multi-user": { @@ -1536,6 +1537,166 @@ const TRANSLATIONS = { unknown: "Неизвестно", }, }, + scheduledJobs: { + title: "Запланированные задачи", + enableNotifications: + "Включите уведомления в браузере о результатах поиска работы", + description: + "Создавайте повторяющиеся задачи на основе искусственного интеллекта, которые будут выполняться по расписанию. Каждая задача включает в себя запрос, а также необязательные инструменты, и сохраняет результат для последующего просмотра.", + newJob: "Новая работа", + loading: "Загрузка...", + emptyTitle: "В настоящее время нет запланированных задач.", + emptySubtitle: "Создайте один, чтобы начать.", + table: { + name: "Имя", + schedule: "График", + status: "Статус", + lastRun: "Последний запуск", + nextRun: "Следующая тренировка", + actions: "Действия", + }, + confirmDelete: "Вы уверены, что хотите удалить эту запланированную задачу?", + toast: { + deleted: "Вакансия удалена", + triggered: "Задача успешно выполнена.", + triggerFailed: "Не удалось запустить задачу.", + triggerSkipped: "Работа уже начата.", + killed: "Работа была успешно завершена.", + killFailed: "Не удалось остановить работу", + }, + row: { + neverRun: "Никогда не бегите", + viewRuns: "Просмотры", + runNow: "Начните сейчас", + enable: "Включить", + disable: "Отключить", + edit: "Редактировать", + delete: "Удалить", + }, + modal: { + titleEdit: "Изменить запланированную задачу", + titleNew: "Новая запланированная задача", + nameLabel: "Имя", + namePlaceholder: "Например, ежедневный дайджест новостей", + promptLabel: "Запрос", + promptPlaceholder: "Инструкция о выполнении каждой операции...", + scheduleLabel: "График", + modeBuilder: "Строитель", + modeCustom: "Индивидуальный", + cronPlaceholder: "Выражение Cron (например, 0 9 * * *)", + currentSchedule: "Текущий график:", + toolsLabel: "Инструменты (необязательно)", + toolsDescription: + "Выберите, какие инструменты могут использоваться для выполнения этой задачи. Если ни один инструмент не выбран, задача будет выполнена без использования каких-либо инструментов.", + toolsSearch: "Поиск", + toolsNoResults: "Не найдено подходящих инструментов.", + required: "Необходимо", + requiredFieldsBanner: + "Пожалуйста, заполните все обязательные поля, чтобы создать объявление о работе.", + cancel: "Отменить", + saving: "Сохранение...", + updateJob: "Обновить информацию о работе", + createJob: "Создать вакансию", + jobUpdated: "Информация о работе обновлена", + jobCreated: "Создана работа", + }, + builder: { + fallbackWarning: + 'Эта опция не может быть изменена визуально. Чтобы сохранить её, перейдите в режим "Настройка". В противном случае, вы можете изменить что-либо ниже, чтобы заменить её.', + run: "Бег", + frequency: { + minute: "каждую минуту", + hour: "почасовая", + day: "ежедневно", + week: "еженедельный", + month: "ежемесячный", + }, + every: "Каждый", + minuteOne: "1 минута", + minuteOther: "{{count}} минут", + atMinute: "В определенный момент", + pastEveryHour: "каждый час", + at: "В", + on: "О", + onDay: "В один день", + ofEveryMonth: "каждого месяца", + weekdays: { + sun: "Солнце", + mon: "Понедельник", + tue: "Вторник", + wed: "Среда", + thu: "Четверг", + fri: "Пятница", + sat: "Суббота", + }, + }, + runHistory: { + back: "Вернуться к вакансиям", + title: "История выполнения: {{name}}", + schedule: "График:", + emptyTitle: + "На данный момент никаких результатов или успехов в этом проекте.", + emptySubtitle: "Запустите задание сейчас и просмотрите его результаты.", + runNow: "Начните сейчас", + table: { + status: "Статус", + started: "Началось", + duration: "Продолжительность", + error: "Ошибка", + }, + stopJob: "Прекратить работу", + }, + runDetail: { + loading: "Загрузка информации о ходе выполнения...", + notFound: "Не найдено.", + back: "Назад", + unknownJob: "Неизвестная должность", + runHeading: "{{name}} — Запуск №{{id}}", + duration: "Продолжительность: {{value}}", + continueInThread: "Продолжить обсуждение в этой теме", + creating: "Создание...", + threadFailed: "Не удалось создать нить.", + sections: { + prompt: "Инструкция", + error: "Ошибка", + thinking: "Мысли ({{count}})", + toolCalls: "Вызовы инструментов ({{count}})", + files: "Файлы ({{count}})", + response: "Ответ", + metrics: "Показатели", + }, + metrics: { + promptTokens: "Ключевые слова:", + completionTokens: "Токены завершения:", + }, + stopJob: "Прекратить работу", + killing: "Остановка...", + }, + toolCall: { + arguments: "Аргументы:", + showResult: "Отобразить результат", + hideResult: "Скрыть результат", + }, + file: { + unknown: "Неизвестный файл", + download: "Скачать", + downloadFailed: "Не удалось скачать файл", + types: { + powerpoint: "Презентация PowerPoint", + pdf: "Документ в формате PDF", + word: "Текстовый документ", + spreadsheet: "Таблица (в электронных таблицах)", + generic: "Файл", + }, + }, + status: { + completed: "Завершено", + failed: "Неудачный", + timed_out: "Время вышло", + running: "Бег", + queued: "В очереди", + }, + }, }; export default TRANSLATIONS; diff --git a/frontend/src/locales/tr/common.js b/frontend/src/locales/tr/common.js index d8192538..3973ef62 100644 --- a/frontend/src/locales/tr/common.js +++ b/frontend/src/locales/tr/common.js @@ -109,6 +109,7 @@ const TRANSLATIONS = { "available-channels": { telegram: "Telegram", }, + "scheduled-jobs": "Planlanan İşler", }, login: { "multi-user": { @@ -1520,6 +1521,165 @@ const TRANSLATIONS = { unknown: "Bilinmiyor", }, }, + scheduledJobs: { + title: "Planlanan İşler", + enableNotifications: + "İş ilanları sonuçları için tarayıcı bildirimlerini etkinleştirin.", + description: + "Tekrarlayan yapay zeka görevlerini, belirli bir zaman çizelgesine göre otomatik olarak çalıştırın. Her görev, isteğe bağlı araçlarla birlikte bir sorguyu çalıştırır ve sonuçları inceleme için kaydeder.", + newJob: "Yeni iş", + loading: "Yükleniyor...", + emptyTitle: "Şu anda planlanan herhangi bir iş yok.", + emptySubtitle: "Başlamak için bir tane oluşturun.", + table: { + name: "Ad", + schedule: "Program", + status: "Durum", + lastRun: "Son Çalışma", + nextRun: "Sonuç", + actions: "Eylemler", + }, + confirmDelete: "Bu planlanan görevi silmekten emin misiniz?", + toast: { + deleted: "İş kayboldu", + triggered: "İş başarıyla başlatıldı.", + triggerFailed: "İşin başlatılması başarısız oldu.", + triggerSkipped: "Bu iş için zaten bir çalışma süreci başlamıştır.", + killed: "İş, başarıyla tamamlandı.", + killFailed: "İşten ayrılmayı başaramadı", + }, + row: { + neverRun: "Asla hızlanmayın.", + viewRuns: "Çalışma seansları", + runNow: "Hemen harekete geçin", + enable: "Etkinleştir", + disable: "Devre dışı bırak", + edit: "Düzenle", + delete: "Sil", + }, + modal: { + titleEdit: "Planlanan Görevi Düzenle", + titleNew: "Yeni Planlanan İş", + nameLabel: "Ad", + namePlaceholder: "Örneğin, Günlük Haber Özeti", + promptLabel: "Talep", + promptPlaceholder: "Her çalışmada çalıştırılma talimatı...", + scheduleLabel: "Program", + modeBuilder: "İnşaatçı", + modeCustom: "Özel", + cronPlaceholder: "Cron ifadesi (örneğin, 0 9 * * *)", + currentSchedule: "Mevcut program:", + toolsLabel: "Araçlar (İsteğe Bağlı)", + toolsDescription: + "Bu iş için kullanılabilen ajan araçlarını seçin. Eğer hiçbir araç seçilmezse, iş herhangi bir araç olmadan çalışacaktır.", + toolsSearch: "Arama", + toolsNoResults: "Hiçbir araç bulunamadı", + required: "Gereklidir", + requiredFieldsBanner: + "Lütfen iş ilanını oluşturmak için gerekli tüm alanları doldurunuz.", + cancel: "İptal et", + saving: "Kaydet...", + updateJob: "İş Tanımını Güncelle", + createJob: "İş Tanımı Oluştur", + jobUpdated: "İş pozisyonu güncellendi", + jobCreated: "İş pozisyonu oluşturuldu", + }, + builder: { + fallbackWarning: + 'Bu ifade görsel olarak düzenlenemez. Mevcut haliyle bırakmak için "Özel" seçeneğine geçin veya aşağıdaki herhangi bir alanı değiştirerek üzerine yazın.', + run: "Koş", + frequency: { + minute: "her dakika", + hour: "saatlik", + day: "günlük", + week: "haftalık", + month: "aylık", + }, + every: "Her", + minuteOne: "1 dakika", + minuteOther: "{{count}} dakika", + atMinute: "Dakikada", + pastEveryHour: "geçmişte, her saat", + at: "Saat", + on: "On", + onDay: "Bir gün", + ofEveryMonth: "her ayın", + weekdays: { + sun: "Güneş", + mon: "Pazartesi", + tue: "Salı", + wed: "Salı", + thu: "Perşembe", + fri: "Cuma", + sat: "Satmak", + }, + }, + runHistory: { + back: "İş ilanlarına dön", + title: "Geçmiş Çalışmalar: {{name}}", + schedule: "Program:", + emptyTitle: "Bu iş için henüz herhangi bir ilerleme kaydedilmedi.", + emptySubtitle: "Şimdi işlemi başlatın ve sonuçlarını görüntüleyin.", + runNow: "Şimdi koşun", + table: { + status: "Durum", + started: "Başlangıç", + duration: "Süre", + error: "Hata", + }, + stopJob: "İşten ayrıl", + }, + runDetail: { + loading: "Yükleme işleminin ayrıntıları yükleniyor...", + notFound: "İstenen komut bulunamadı.", + back: "Geri", + unknownJob: "Bilinmeyen İş", + runHeading: "{{name}} — Çalışma #{{id}}", + duration: "Süre: {{value}}", + continueInThread: "İlgili başlıkta devam et", + creating: "Yaratmak...", + threadFailed: "İşlem başlatma başarısız oldu.", + sections: { + prompt: "Uyarı", + error: "Hata", + thinking: "Düşünceler ({{count}})", + toolCalls: "Araç Çağrıları ({{count}})", + files: "Dosyalar ({{count}})", + response: "Cevap", + metrics: "Ölçüm değerleri", + }, + metrics: { + promptTokens: "Başlangıç belirteçleri:", + completionTokens: "Tamamlanmış token'lar:", + }, + stopJob: "İşten Çık", + killing: "Dur...", + }, + toolCall: { + arguments: "Tartışmalar:", + showResult: "Sonuçları göster", + hideResult: "Sonucu gizle", + }, + file: { + unknown: "Bilinmeyen dosya", + download: "İndir", + downloadFailed: "Dosya indirme işlemi başarısız oldu", + types: { + powerpoint: "PowerPoint", + pdf: "PDF belgesi", + word: "Kelime Belgesi", + spreadsheet: "Tablo", + generic: "Dosya", + }, + }, + status: { + completed: "Tamamlandı", + failed: "Başarısız", + timed_out: "Zaman aşımı", + running: "Koşmak", + queued: "Bekleme halinde", + }, + }, }; export default TRANSLATIONS; diff --git a/frontend/src/locales/vn/common.js b/frontend/src/locales/vn/common.js index a5449739..88651533 100644 --- a/frontend/src/locales/vn/common.js +++ b/frontend/src/locales/vn/common.js @@ -109,6 +109,7 @@ const TRANSLATIONS = { "available-channels": { telegram: "Telegram", }, + "scheduled-jobs": "Công việc theo lịch trình", }, login: { "multi-user": { @@ -1506,6 +1507,165 @@ const TRANSLATIONS = { unknown: "Không xác định", }, }, + scheduledJobs: { + title: "Công việc theo lịch trình", + enableNotifications: + "Kích hoạt thông báo trình duyệt để nhận kết quả tìm kiếm việc làm", + description: + "Tạo các tác vụ AI lặp đi lặp lại, chạy theo lịch trình. Mỗi tác vụ sẽ thực hiện một yêu cầu với các công cụ tùy chọn và lưu kết quả để xem xét.", + newJob: "Vị trí công việc mới", + loading: "Đang tải...", + emptyTitle: "Hiện chưa có công việc nào được lên lịch.", + emptySubtitle: "Tạo một cái để bắt đầu.", + table: { + name: "Tên", + schedule: "Lịch trình", + status: "Trạng thái", + lastRun: "Lần chạy cuối", + nextRun: "Chuyến đi tiếp theo", + actions: "Hành động", + }, + confirmDelete: "Bạn có chắc chắn muốn xóa công việc này?", + toast: { + deleted: "Việc xóa công việc", + triggered: "Việc tìm kiếm việc làm đã thành công.", + triggerFailed: "Không thể kích hoạt công việc", + triggerSkipped: "Công việc này đã bắt đầu triển khai.", + killed: "Việc làm đã được hoàn thành thành công.", + killFailed: "Không thể ngăn chặn việc chấm dứt hợp đồng", + }, + row: { + neverRun: "Không bao giờ chạy", + viewRuns: "Xem theo các lượt", + runNow: "Hãy bắt đầu ngay bây giờ.", + enable: "Kích hoạt", + disable: "Tắt", + edit: "Chỉnh sửa", + delete: "Xóa", + }, + modal: { + titleEdit: "Chỉnh sửa công việc theo lịch", + titleNew: "Công việc mới theo lịch trình", + nameLabel: "Tên", + namePlaceholder: "Ví dụ: Tóm tắt tin tức hàng ngày", + promptLabel: "Yêu cầu", + promptPlaceholder: "Hướng dẫn để chạy trong mỗi lần thực hiện...", + scheduleLabel: "Lịch trình", + modeBuilder: "Nhà xây dựng", + modeCustom: "Tùy chỉnh", + cronPlaceholder: "Biểu thức Cron (ví dụ: 0 9 * * *)", + currentSchedule: "Lịch trình hiện tại:", + toolsLabel: "Dụng cụ (Tùy chọn)", + toolsDescription: + "Chọn các công cụ hỗ trợ mà công việc này có thể sử dụng. Nếu không chọn công cụ nào, công việc sẽ chạy mà không sử dụng bất kỳ công cụ nào.", + toolsSearch: "Tìm kiếm", + toolsNoResults: "Không có công cụ nào phù hợp", + required: "Yêu cầu", + requiredFieldsBanner: + "Vui lòng điền đầy đủ các trường thông tin bắt buộc để tạo công việc.", + cancel: "Hủy", + saving: "Tiết kiệm...", + updateJob: "Cập nhật công việc", + createJob: "Tạo công việc", + jobUpdated: "Thông tin công việc đã được cập nhật", + jobCreated: "Vị trí được tạo", + }, + builder: { + fallbackWarning: + 'Biểu thức này không thể chỉnh sửa trực quan. Để giữ nguyên, hãy chuyển sang chế độ "Tùy chỉnh". Hoặc, bạn có thể thay đổi bất kỳ nội dung nào bên dưới để ghi đè lên biểu thức này.', + run: "Chạy", + frequency: { + minute: "mỗi phút", + hour: "theo giờ", + day: "hàng ngày", + week: "hàng tuần", + month: "hàng tháng", + }, + every: "Mỗi", + minuteOne: "1 phút", + minuteOther: "{{count}} phút", + atMinute: "Tại phút", + pastEveryHour: "hàng giờ", + at: "Tại", + on: "Về", + onDay: "Trong ngày", + ofEveryMonth: "của mỗi tháng", + weekdays: { + sun: "Mặt trời", + mon: "Thứ hai", + tue: "Thứ hai", + wed: "Thứ Ba", + thu: "Tháng", + fri: "Thứ Sáu", + sat: "Thứ Sáu", + }, + }, + runHistory: { + back: "Quay lại tìm việc", + title: "Lịch sử hoạt động: {{name}}", + schedule: "Lịch trình:", + emptyTitle: "Hiện tại, công việc này chưa có kết quả cụ thể.", + emptySubtitle: "Thực hiện công việc ngay bây giờ và xem kết quả.", + runNow: "Hãy bắt đầu ngay bây giờ.", + table: { + status: "Trạng thái", + started: "Bắt đầu", + duration: "Thời gian", + error: "Lỗi", + }, + stopJob: "Ngừng làm", + }, + runDetail: { + loading: "Hiển thị chi tiết chạy...", + notFound: "Không tìm thấy lệnh.", + back: "Quay lại", + unknownJob: "Vị trí công việc chưa được xác định", + runHeading: "{{name}} — Chạy lệnh #{{id}}", + duration: "Thời gian: {{value}}", + continueInThread: "Tiếp tục thảo luận trong chủ đề này", + creating: "Tạo ra...", + threadFailed: "Không thể tạo ra luồng (thread).", + sections: { + prompt: "Yêu cầu", + error: "Lỗi", + thinking: "Ý kiến ({{count}})", + toolCalls: "Gọi công cụ ({{count}})", + files: "Tệp tin ({{count}})", + response: "Phản hồi", + metrics: "Các chỉ số", + }, + metrics: { + promptTokens: "Từ gợi ý:", + completionTokens: "Các token hoàn thành:", + }, + stopJob: "Dừng việc", + killing: "Dừng lại...", + }, + toolCall: { + arguments: "Các lập luận:", + showResult: "Hiển thị kết quả", + hideResult: "Ẩn kết quả", + }, + file: { + unknown: "Tệp không xác định", + download: "Tải xuống", + downloadFailed: "Không thể tải xuống tệp", + types: { + powerpoint: "Trình chiếu PowerPoint", + pdf: "Tài liệu PDF", + word: "Tệp Word", + spreadsheet: "Bảng tính", + generic: "Tệp", + }, + }, + status: { + completed: "Hoàn thành", + failed: "Thất bại", + timed_out: "Thời gian đã hết", + running: "Chạy bộ", + queued: "Đang chờ", + }, + }, }; export default TRANSLATIONS; diff --git a/frontend/src/locales/zh/common.js b/frontend/src/locales/zh/common.js index 7ef6af71..a6e526f8 100644 --- a/frontend/src/locales/zh/common.js +++ b/frontend/src/locales/zh/common.js @@ -105,6 +105,7 @@ const TRANSLATIONS = { "available-channels": { telegram: "电报", }, + "scheduled-jobs": "计划好的任务", }, login: { "multi-user": { @@ -1409,6 +1410,163 @@ const TRANSLATIONS = { unknown: "未知", }, }, + scheduledJobs: { + title: "计划好的任务", + enableNotifications: "启用浏览器通知,以便及时获取招聘结果", + description: + "创建可重复执行的 AI 任务,并设置执行时间表。每个任务会执行一个提示,并可以选择使用辅助工具,然后保存结果供后续审查。", + newJob: "新工作", + loading: "正在加载...", + emptyTitle: "目前没有计划好的任务。", + emptySubtitle: "创建一个,开始吧。", + table: { + name: "姓名", + schedule: "时间表", + status: "状态", + lastRun: "最后一次", + nextRun: "下一次尝试", + actions: "行动", + }, + confirmDelete: "您确定要删除这个已计划的任务吗?", + toast: { + deleted: "已删除工作", + triggered: "工作已成功启动", + triggerFailed: "未能启动任务", + triggerSkipped: "目前,这项工作已经开始进行中。", + killed: "工作已成功停止。", + killFailed: "未能阻止工作", + }, + row: { + neverRun: "切勿奔跑", + viewRuns: "观看记录", + runNow: "现在就行动", + enable: "启用", + disable: "禁用", + edit: "编辑", + delete: "删除", + }, + modal: { + titleEdit: "编辑计划任务", + titleNew: "新建任务", + nameLabel: "姓名", + namePlaceholder: "例如:每日新闻摘要", + promptLabel: "提示", + promptPlaceholder: "“在每次执行时执行以下指令…”", + scheduleLabel: "时间表", + modeBuilder: "建筑师", + modeCustom: "定制", + cronPlaceholder: "Cron 表达式(例如:0 9 * * *)", + currentSchedule: "当前时间表:", + toolsLabel: "工具(可选)", + toolsDescription: + "选择此任务可以使用的任何代理工具。如果未选择任何工具,则任务将不会使用任何工具。", + toolsSearch: "搜索", + toolsNoResults: "没有合适的工具", + required: "必需", + requiredFieldsBanner: "请务必填写所有必填字段,以便创建职位。", + cancel: "取消", + saving: "节省...", + updateJob: "更新职位", + createJob: "创建工作", + jobUpdated: "工作信息已更新", + jobCreated: "创造了工作", + }, + builder: { + fallbackWarning: + "这个表达式无法通过图形界面进行编辑。请选择“自定义”选项来保留它,或者修改下面的内容来覆盖它。", + run: "跑步", + frequency: { + minute: "每分钟", + hour: "每小时", + day: "每日", + week: "每周", + month: "每月", + }, + every: "每一个", + minuteOne: "1 分钟", + minuteOther: "{{count}} 分钟", + atMinute: "在…分", + pastEveryHour: "过去每个小时", + at: "在", + on: "关于", + onDay: "在某一天", + ofEveryMonth: "每个月", + weekdays: { + sun: "太阳", + mon: "周一", + tue: "周二", + wed: "周三", + thu: "星期四", + fri: "周五", + sat: "星期六", + }, + }, + runHistory: { + back: "返回工作列表", + title: "运行历史:{{name}}", + schedule: "时间表:", + emptyTitle: "目前为止,这项工作还没有取得任何成果。", + emptySubtitle: "立即运行任务,并查看其结果。", + runNow: "立即行动", + table: { + status: "状态", + started: "开始", + duration: "时长", + error: "错误", + }, + stopJob: "停止工作", + }, + runDetail: { + loading: "正在加载运行详情...", + notFound: "未找到。", + back: "返回", + unknownJob: "未知的职位", + runHeading: "{{name}} — 运行 #{{id}}", + duration: "时长:{{value}}", + continueInThread: "继续参与该主题讨论", + creating: "创作...", + threadFailed: "未能创建线程", + sections: { + prompt: "提示", + error: "错误", + thinking: "想法 ({{count}})", + toolCalls: "工具调用 ({{count}})", + files: "文件 ({{count}})", + response: "回应", + metrics: "指标", + }, + metrics: { + promptTokens: "提示词:", + completionTokens: "完成标记:", + }, + stopJob: "停止工作", + killing: "停止...", + }, + toolCall: { + arguments: "论点:", + showResult: "显示结果", + hideResult: "隐藏结果", + }, + file: { + unknown: "未知的文件", + download: "下载", + downloadFailed: "未能下载文件", + types: { + powerpoint: "幻灯片", + pdf: "PDF 格式文档", + word: "文档", + spreadsheet: "电子表格", + generic: "文件", + }, + }, + status: { + completed: "已完成", + failed: "失败", + timed_out: "超时", + running: "跑步", + queued: "排队", + }, + }, }; export default TRANSLATIONS; diff --git a/frontend/src/locales/zh_TW/common.js b/frontend/src/locales/zh_TW/common.js index dac115bd..951e9360 100644 --- a/frontend/src/locales/zh_TW/common.js +++ b/frontend/src/locales/zh_TW/common.js @@ -105,6 +105,7 @@ const TRANSLATIONS = { "available-channels": { telegram: "電訊", }, + "scheduled-jobs": "預約排定的工作", }, login: { "multi-user": { @@ -1405,6 +1406,163 @@ const TRANSLATIONS = { unknown: "未知的", }, }, + scheduledJobs: { + title: "預約排定的工作", + enableNotifications: "啟用瀏覽器通知,以便收到工作結果", + description: + "建立可重複執行的 AI 任務,並設定執行時間表。每個任務會執行一個指令,並可選地使用工具,然後將結果儲存以供後續檢閱。", + newJob: "新的工作", + loading: "載入中...", + emptyTitle: "目前沒有預約的任務", + emptySubtitle: "先從一個開始。", + table: { + name: "姓名", + schedule: "時間表", + status: "狀態", + lastRun: "最後一次", + nextRun: "下一次活動", + actions: "行動", + }, + confirmDelete: "您確定要刪除這個預約的任務嗎?", + toast: { + deleted: "已刪除", + triggered: "工作已成功啟動", + triggerFailed: "未能觸發工作", + triggerSkipped: "目前,這個項目已經啟動。", + killed: "工作已成功停止", + killFailed: "未能成功終止工作", + }, + row: { + neverRun: "請勿急於完成", + viewRuns: "觀看次數", + runNow: "現在就開始行動", + enable: "啟用", + disable: "停用", + edit: "編輯", + delete: "刪除", + }, + modal: { + titleEdit: "編輯排程工作", + titleNew: "新的排程工作", + nameLabel: "姓名", + namePlaceholder: "例如:每日新聞摘要", + promptLabel: "提示", + promptPlaceholder: "指示內容為:在每次執行時執行...", + scheduleLabel: "時間表", + modeBuilder: "建築師", + modeCustom: "客製化", + cronPlaceholder: "Cron 運算式(例如:0 9 * * *)", + currentSchedule: "目前時間表:", + toolsLabel: "工具(可選)", + toolsDescription: + "請選擇此工作可以使用的工具。如果沒有選擇任何工具,則工作將不使用任何工具。", + toolsSearch: "搜尋", + toolsNoResults: "沒有任何工具符合", + required: "必需", + requiredFieldsBanner: "請務必填寫所有必要欄位,以便建立工作。", + cancel: "取消", + saving: "儲存...", + updateJob: "更新工作", + createJob: "建立工作", + jobUpdated: "工作資訊已更新", + jobCreated: "已創建工作", + }, + builder: { + fallbackWarning: + "這個表達方式無法透過視覺編輯來修改。請選擇「自訂」選項來保留它,或修改以下內容以覆蓋它。", + run: "跑步", + frequency: { + minute: "每分鐘", + hour: "每小時", + day: "每天", + week: "每週", + month: "每月", + }, + every: "每一個", + minuteOne: "1 分鐘", + minuteOther: "{{count}} 分鐘", + atMinute: "每分鐘", + pastEveryHour: "過去每小時", + at: "在", + on: "關於", + onDay: "在某一天", + ofEveryMonth: "每個月", + weekdays: { + sun: "太陽", + mon: "星期一", + tue: "週二", + wed: "星期三", + thu: "星期四", + fri: "週五", + sat: "星期六", + }, + }, + runHistory: { + back: "返回工作機會", + title: "運行歷史:{{name}}", + schedule: "時間表:", + emptyTitle: "目前這個工作尚未有任何進展。", + emptySubtitle: "現在執行此任務,並查看結果。", + runNow: "立即行動", + table: { + status: "狀態", + started: "開始", + duration: "時間", + error: "錯誤", + }, + stopJob: "停止工作", + }, + runDetail: { + loading: "載入執行記錄詳細...", + notFound: "未找到程式。", + back: "返回", + unknownJob: "未知的職位", + runHeading: "{{name}} — 執行 #{{id}}", + duration: "時間:{{value}}", + continueInThread: "繼續追蹤此主題", + creating: "創作...", + threadFailed: "未能建立執行緒", + sections: { + prompt: "提示", + error: "錯誤", + thinking: "想法 ({{count}})", + toolCalls: "工具呼叫 ({{count}})", + files: "檔案 ({{count}})", + response: "回覆", + metrics: "指標", + }, + metrics: { + promptTokens: "提示詞:", + completionTokens: "完成標記:", + }, + stopJob: "停止工作", + killing: "停止...", + }, + toolCall: { + arguments: "論點:", + showResult: "顯示結果", + hideResult: "隱藏結果", + }, + file: { + unknown: "未知的檔案", + download: "下載", + downloadFailed: "無法下載檔案", + types: { + powerpoint: "PowerPoint", + pdf: "PDF 文件", + word: "Word 檔案", + spreadsheet: "電子表格", + generic: "檔案", + }, + }, + status: { + completed: "已完成", + failed: "失敗", + timed_out: "時間到", + running: "跑步", + queued: "排隊", + }, + }, }; export default TRANSLATIONS; diff --git a/frontend/src/main.jsx b/frontend/src/main.jsx index 22b375b4..d7778627 100644 --- a/frontend/src/main.jsx +++ b/frontend/src/main.jsx @@ -5,6 +5,7 @@ import App from "@/App.jsx"; import PrivateRoute, { AdminRoute, ManagerRoute, + SingleUserRoute, } from "@/components/PrivateRoute"; import Login from "@/pages/Login"; import SimpleSSOPassthrough from "@/pages/Login/SSO/simple"; @@ -381,6 +382,35 @@ const router = createBrowserRouter([ return { element: }; }, }, + { + path: "/settings/scheduled-jobs", + lazy: async () => { + const { default: ScheduledJobs } = await import( + "@/pages/GeneralSettings/ScheduledJobs" + ); + return { element: }; + }, + }, + { + path: "/settings/scheduled-jobs/:id/runs", + lazy: async () => { + const { default: ScheduledJobRuns } = await import( + "@/pages/GeneralSettings/ScheduledJobs/RunHistoryPage" + ); + return { element: }; + }, + }, + { + path: "/settings/scheduled-jobs/:id/runs/:runId", + lazy: async () => { + const { default: ScheduledJobRunDetail } = await import( + "@/pages/GeneralSettings/ScheduledJobs/RunDetailPage" + ); + return { + element: , + }; + }, + }, // Catch-all route for 404s { path: "*", diff --git a/frontend/src/models/scheduledJobs.js b/frontend/src/models/scheduledJobs.js new file mode 100644 index 00000000..1047e3ee --- /dev/null +++ b/frontend/src/models/scheduledJobs.js @@ -0,0 +1,127 @@ +import { API_BASE } from "@/utils/constants"; +import { baseHeaders } from "@/utils/request"; + +const ScheduledJobs = { + list: async function () { + return await fetch(`${API_BASE}/scheduled-jobs`, { + headers: baseHeaders(), + }) + .then((res) => res.json()) + .catch(() => ({ jobs: [] })); + }, + + create: async function (data) { + return await fetch(`${API_BASE}/scheduled-jobs/new`, { + method: "POST", + headers: baseHeaders(), + body: JSON.stringify(data), + }) + .then((res) => res.json()) + .catch(() => ({ job: null, error: "Failed to create scheduled job" })); + }, + + get: async function (id) { + return await fetch(`${API_BASE}/scheduled-jobs/${id}`, { + headers: baseHeaders(), + }) + .then((res) => res.json()) + .catch(() => ({ job: null })); + }, + + update: async function (id, data) { + return await fetch(`${API_BASE}/scheduled-jobs/${id}`, { + method: "PUT", + headers: baseHeaders(), + body: JSON.stringify(data), + }) + .then((res) => res.json()) + .catch((e) => ({ + job: null, + error: e.message, + })); + }, + + delete: async function (id) { + return await fetch(`${API_BASE}/scheduled-jobs/${id}`, { + method: "DELETE", + headers: baseHeaders(), + }) + .then((res) => res.json()) + .catch(() => ({ success: false })); + }, + + toggle: async function (id) { + return await fetch(`${API_BASE}/scheduled-jobs/${id}/toggle`, { + method: "POST", + headers: baseHeaders(), + }) + .then((res) => res.json()) + .catch(() => ({ job: null })); + }, + + trigger: async function (id) { + return await fetch(`${API_BASE}/scheduled-jobs/${id}/trigger`, { + method: "POST", + headers: baseHeaders(), + }) + .then((res) => res.json()) + .catch((e) => ({ success: false, error: e.message })); + }, + + runs: async function (id) { + return await fetch(`${API_BASE}/scheduled-jobs/${id}/runs`, { + headers: baseHeaders(), + }) + .then((res) => res.json()) + .catch(() => ({ runs: [] })); + }, + + getRun: async function (runId) { + return await fetch(`${API_BASE}/scheduled-jobs/runs/${runId}`, { + headers: baseHeaders(), + }) + .then((res) => res.json()) + .catch(() => ({ run: null, job: null })); + }, + + markRunRead: async function (runId) { + return await fetch(`${API_BASE}/scheduled-jobs/runs/${runId}/read`, { + method: "POST", + headers: baseHeaders(), + }) + .then((res) => res.json()) + .catch(() => ({ success: false })); + }, + + continueInThread: async function (runId) { + return await fetch(`${API_BASE}/scheduled-jobs/runs/${runId}/continue`, { + method: "POST", + headers: baseHeaders(), + }) + .then((res) => res.json()) + .catch((e) => ({ + workspaceSlug: null, + threadSlug: null, + error: e.message, + })); + }, + + availableTools: async function () { + return await fetch(`${API_BASE}/scheduled-jobs/available-tools`, { + headers: baseHeaders(), + }) + .then((res) => res.json()) + .catch(() => ({ tools: [] })); + }, + + killRun: async function (runId) { + return await fetch(`${API_BASE}/scheduled-jobs/runs/${runId}/kill`, { + method: "POST", + headers: baseHeaders(), + }) + .then((res) => res.json()) + .catch((e) => ({ success: false, error: e.message })); + }, +}; + +export default ScheduledJobs; diff --git a/frontend/src/pages/GeneralSettings/ScheduledJobs/JobFormModal/CronBuilder.jsx b/frontend/src/pages/GeneralSettings/ScheduledJobs/JobFormModal/CronBuilder.jsx new file mode 100644 index 00000000..3f451398 --- /dev/null +++ b/frontend/src/pages/GeneralSettings/ScheduledJobs/JobFormModal/CronBuilder.jsx @@ -0,0 +1,196 @@ +import { useState } from "react"; +import { useTranslation } from "react-i18next"; +import { + parseCronToBuilderState, + buildCronFromBuilderState, +} from "../utils/cron"; + +const MINUTE_INTERVALS = [1, 2, 5, 10, 15, 20, 30]; +const MINUTES = Array.from({ length: 60 }, (_, i) => i); +const DAYS_OF_MONTH = Array.from({ length: 31 }, (_, i) => i + 1); + +const pad2 = (n) => String(n).padStart(2, "0"); + +const inputClass = + "border-none bg-theme-settings-input-bg text-theme-text-primary text-sm rounded-lg focus:outline-primary-button outline-none p-2.5"; + +const labelClass = "text-sm text-theme-text-secondary"; + +// Visual cron builder. Maintains its own state derived from the incoming +// `value` on mount, and emits a fresh 5-field cron string via `onChange` +// whenever the user changes any sub-field. +export default function CronBuilder({ value, onChange }) { + const { t } = useTranslation(); + const WEEKDAYS = [ + { value: 0, label: t("scheduledJobs.builder.weekdays.sun") }, + { value: 1, label: t("scheduledJobs.builder.weekdays.mon") }, + { value: 2, label: t("scheduledJobs.builder.weekdays.tue") }, + { value: 3, label: t("scheduledJobs.builder.weekdays.wed") }, + { value: 4, label: t("scheduledJobs.builder.weekdays.thu") }, + { value: 5, label: t("scheduledJobs.builder.weekdays.fri") }, + { value: 6, label: t("scheduledJobs.builder.weekdays.sat") }, + ]; + const initial = parseCronToBuilderState(value); + const [state, setState] = useState(initial.state); + const [wasFallback, setWasFallback] = useState(initial.wasFallback); + + const update = (patch) => { + const next = { ...state, ...patch }; + setState(next); + if (wasFallback) setWasFallback(false); + const cron = buildCronFromBuilderState(next); + if (cron !== value) onChange(cron); + }; + + return ( +
+ {wasFallback && ( +

+ {t("scheduledJobs.builder.fallbackWarning")} +

+ )} + +
+ {t("scheduledJobs.builder.run")} + +
+ + {state.frequency === "minute" && ( +
+ {t("scheduledJobs.builder.every")} + +
+ )} + + {state.frequency === "hour" && ( +
+ + {t("scheduledJobs.builder.atMinute")} + + + + {t("scheduledJobs.builder.pastEveryHour")} + +
+ )} + + {(state.frequency === "day" || + state.frequency === "week" || + state.frequency === "month") && ( +
+ {t("scheduledJobs.builder.at")} + { + const [h, m] = e.target.value.split(":"); + update({ + hour: parseInt(h, 10) || 0, + minute: parseInt(m, 10) || 0, + }); + }} + className={`${inputClass} [color-scheme:dark] light:[color-scheme:light]`} + /> +
+ )} + + {state.frequency === "week" && ( +
+ {t("scheduledJobs.builder.on")} +
+ {WEEKDAYS.map((day) => { + const selected = state.weekdays.includes(day.value); + return ( + + ); + })} +
+
+ )} + + {state.frequency === "month" && ( +
+ {t("scheduledJobs.builder.onDay")} + + + {t("scheduledJobs.builder.ofEveryMonth")} + +
+ )} +
+ ); +} diff --git a/frontend/src/pages/GeneralSettings/ScheduledJobs/JobFormModal/FormActions.jsx b/frontend/src/pages/GeneralSettings/ScheduledJobs/JobFormModal/FormActions.jsx new file mode 100644 index 00000000..57c1b829 --- /dev/null +++ b/frontend/src/pages/GeneralSettings/ScheduledJobs/JobFormModal/FormActions.jsx @@ -0,0 +1,28 @@ +import { useTranslation } from "react-i18next"; + +export default function FormActions({ isEditing, saving, onClose }) { + const { t } = useTranslation(); + + return ( +
+ + +
+ ); +} diff --git a/frontend/src/pages/GeneralSettings/ScheduledJobs/JobFormModal/JobDescription.jsx b/frontend/src/pages/GeneralSettings/ScheduledJobs/JobFormModal/JobDescription.jsx new file mode 100644 index 00000000..49503873 --- /dev/null +++ b/frontend/src/pages/GeneralSettings/ScheduledJobs/JobFormModal/JobDescription.jsx @@ -0,0 +1,57 @@ +import { useTranslation } from "react-i18next"; + +export default function JobDescription({ form, errors, onChange }) { + const { t } = useTranslation(); + + return ( + <> +
+ + +
+ +
+ +