fix(translate,automation): fix schedule import/param errors, add translation schedules to Automation view

- Fix ModuleNotFoundError: 4 lazy imports in _schedule_routes.py changed
  from 3-dot to 4-dot relative imports (src.api.dependencies -> src.dependencies)
- Fix TypeError: update_schedule() param name mismatch (timezone -> timezone_str)
- Add add_translation_job/remove_translation_job to SchedulerService
  to register translation schedules with APScheduler via execute_scheduled_translation
- Add GET /settings/automation/translation-schedules endpoint
  returning all translation schedules with joined job names
- Update Automation settings page to display translation schedules
  (cron, timezone, active badge, last run) below validation policies
This commit is contained in:
2026-05-13 21:08:10 +03:00
parent 1f8b8c9f47
commit d1695fe536
6 changed files with 172 additions and 8 deletions

View File

@@ -418,6 +418,7 @@ export const api = {
createValidationPolicy: (policy) => postApi('/settings/automation/policies', policy),
updateValidationPolicy: (id, policy) => requestApi(`/settings/automation/policies/${id}`, 'PATCH', policy),
deleteValidationPolicy: (id) => requestApi(`/settings/automation/policies/${id}`, 'DELETE'),
getTranslationSchedules: () => fetchApi('/settings/automation/translation-schedules'),
// Health
getHealthSummary: (environmentId) => {
@@ -458,4 +459,5 @@ export const getValidationPolicies = api.getValidationPolicies;
export const createValidationPolicy = api.createValidationPolicy;
export const updateValidationPolicy = api.updateValidationPolicy;
export const deleteValidationPolicy = api.deleteValidationPolicy;
export const getTranslationSchedules = api.getTranslationSchedules;
export const getHealthSummary = api.getHealthSummary;

View File

@@ -18,12 +18,14 @@
createValidationPolicy,
updateValidationPolicy,
deleteValidationPolicy,
getEnvironments
getEnvironments,
getTranslationSchedules
} from '$lib/api';
import { addToast } from '$lib/toasts';
let policies = $state([]);
let environments = $state([]);
let translationSchedules = $state([]);
let isLoading = $state(true);
let showForm = $state(false);
let selectedPolicy = $state(null);
@@ -35,9 +37,10 @@
async function loadData() {
isLoading = true;
try {
const [policiesData, envsData] = await Promise.all([
const [policiesData, envsData, schedulesData] = await Promise.all([
getValidationPolicies(),
getEnvironments()
getEnvironments(),
getTranslationSchedules()
]);
const policyArray = Array.isArray(policiesData) ? policiesData : [];
@@ -82,6 +85,7 @@
policies = policiesData;
environments = envsData;
translationSchedules = Array.isArray(schedulesData) ? schedulesData : [];
} catch (error) {
console.error('Failed to load automation data:', error);
} finally {
@@ -280,6 +284,53 @@
{/each}
</ul>
</div>
{#if translationSchedules.length > 0}
<h2 class="text-xl font-semibold text-gray-900 dark:text-white mb-4 mt-8">Translation Schedules</h2>
<div class="bg-white dark:bg-gray-800 shadow overflow-hidden sm:rounded-md border border-gray-200 dark:border-gray-700">
<ul class="divide-y divide-gray-200 dark:divide-gray-700">
{#each translationSchedules as ts}
<li>
<div class="px-4 py-4 sm:px-6">
<div class="flex items-center justify-between">
<div class="flex items-center">
<div class="flex-shrink-0">
<div class="h-10 w-10 rounded-full bg-amber-100 dark:bg-amber-900 flex items-center justify-center text-amber-600 dark:text-amber-300">
<svg class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
</div>
</div>
<div class="ml-4">
<h3 class="text-sm font-medium text-gray-900 dark:text-white">{ts.job_name}</h3>
<div class="mt-1 flex items-center text-xs text-gray-500 dark:text-gray-400">
<span class="font-mono">{ts.cron_expression}</span>
<span class="mx-2"></span>
<span>{ts.timezone}</span>
</div>
</div>
</div>
<div class="flex items-center space-x-3">
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium {ts.is_active ? 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200' : 'bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-gray-300'}">
{ts.is_active ? 'Active' : 'Inactive'}
</span>
{#if ts.last_run_at}
<span class="text-xs text-gray-500 dark:text-gray-400">
Last: {new Date(ts.last_run_at).toLocaleString()}
</span>
{/if}
</div>
</div>
</div>
</li>
{:else}
<li class="px-4 py-8 text-center text-sm text-gray-500 dark:text-gray-400">
No translation schedules configured
</li>
{/each}
</ul>
</div>
{/if}
{/if}
</div>
<!-- #endregion AutomationPage -->