Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 16 additions & 4 deletions messages/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,7 @@
"relative_time_years_ago_plural": "{count} years ago",
"sidebar_admin": "Admin",
"sidebar_teacher": "Teacher",
"sidebar_admin_monitoring": "System Monitoring",
"sidebar_admin_monitoring": "Worker Monitoring",
"sidebar_admin_contests": "Contests",
"sidebar_admin_tasks": "Tasks",
"sidebar_admin_users": "Users",
Expand Down Expand Up @@ -506,7 +506,7 @@
"admin_contest_submissions_placeholder_user": "Search by username, name...",
"admin_contest_submissions_placeholder_task": "Search by task name or ID...",
"admin_contests_card_view_submissions": "View Submissions",
"admin_dashboard_title": "Admin Dashboard",
"admin_dashboard_title": "Worker Monitoring",
"admin_dashboard_worker_status": "Worker Status",
"admin_dashboard_total_workers": "Total Workers",
"admin_dashboard_busy_workers": "Busy Workers",
Expand All @@ -515,7 +515,7 @@
"admin_dashboard_worker_details": "Worker Details",
"admin_dashboard_worker_status_error": "Failed to load worker status",
"admin_dashboard_loading": "Loading...",
"admin_dashboard_system_monitoring": "System Monitoring",
"admin_dashboard_system_monitoring": "Worker Status",
"admin_dashboard_total_workers_description": "All registered workers",
"admin_dashboard_busy_workers_description": "Currently processing",
"admin_dashboard_available_workers_description": "Ready for tasks",
Expand Down Expand Up @@ -895,5 +895,17 @@
"admin_languages_status_disabled": "Disabled",
"admin_languages_action_toggle": "Toggle",
"admin_languages_toggle_error": "Failed to update language",
"admin_languages_toggle_success": "Language updated successfully"
"admin_languages_toggle_success": "Language updated successfully",
"app_name": "Mini-Maxit",
"title_type_task": "Task",
"title_type_contest": "Contest",
"title_type_profile": "Profile",
"title_type_submission": "Submission",
"title_type_results": "Results",
"title_type_group": "Group",
"title_type_groups": "Groups",
"title_type_language": "Languages",
"title_type_user": "Users",
"title_type_dashboard": "Dashboard",
"title_type_admin": "Admin"
}
20 changes: 16 additions & 4 deletions messages/pl.json
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,7 @@
"relative_time_years_ago_plural": "{count} lat temu",
"sidebar_admin": "Admin",
"sidebar_teacher": "Nauczyciel",
"sidebar_admin_monitoring": "Monitoring Systemu",
"sidebar_admin_monitoring": "Monitoring Pracowników",
"sidebar_admin_contests": "Konkursy",
"sidebar_admin_tasks": "Zadania",
"sidebar_admin_users": "Użytkownicy",
Expand Down Expand Up @@ -506,7 +506,7 @@
"admin_contest_submissions_placeholder_user": "Szukaj według nazwy użytkownika, imienia...",
"admin_contest_submissions_placeholder_task": "Szukaj według nazwy zadania lub ID...",
"admin_contests_card_view_submissions": "Zobacz Zgłoszenia",
"admin_dashboard_title": "Panel Administracyjny",
"admin_dashboard_title": "Monitoring Pracowników",
"admin_dashboard_worker_status": "Status Workerów",
"admin_dashboard_total_workers": "Wszystkie Workery",
"admin_dashboard_busy_workers": "Zajęte Workery",
Expand All @@ -515,7 +515,7 @@
"admin_dashboard_worker_details": "Szczegóły Workerów",
"admin_dashboard_worker_status_error": "Nie udało się załadować statusu workerów",
"admin_dashboard_loading": "Ładowanie...",
"admin_dashboard_system_monitoring": "Monitoring Systemu",
"admin_dashboard_system_monitoring": "Status Pracowników",
"admin_dashboard_total_workers_description": "Wszystkie zarejestrowane workery",
"admin_dashboard_busy_workers_description": "Obecnie przetwarzane",
"admin_dashboard_available_workers_description": "Gotowe do zadań",
Expand Down Expand Up @@ -895,5 +895,17 @@
"admin_languages_status_disabled": "Wyłączony",
"admin_languages_action_toggle": "Przełącz",
"admin_languages_toggle_error": "Nie udało się zaktualizować języka",
"admin_languages_toggle_success": "Język zaktualizowany pomyślnie"
"admin_languages_toggle_success": "Język zaktualizowany pomyślnie",
"app_name": "Mini-Maxit",
"title_type_task": "Zadanie",
"title_type_contest": "Konkurs",
"title_type_profile": "Profil",
"title_type_submission": "Zgłoszenie",
"title_type_results": "Wyniki",
"title_type_group": "Grupa",
"title_type_groups": "Grupy",
"title_type_language": "Języki",
"title_type_user": "Użytkownicy",
"title_type_dashboard": "Panel",
"title_type_admin": "Administracja"
}
2 changes: 1 addition & 1 deletion src/lib/components/dashboard/DashboardSidebar.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@
const adminMenuItems = [
{
title: () => m.sidebar_admin_monitoring(),
href: localizeHref(AppRoutes.Admin),
href: localizeHref(AppRoutes.AdminWorkerMonitoring),
icon: Activity
},
{
Expand Down
4 changes: 3 additions & 1 deletion src/lib/components/dashboard/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ export function getDashboardTitleTranslationFromPathname(pathname: string): stri
[AppRoutes.TeacherContests]: () => m.sidebar_admin_contests(),
[AppRoutes.TeacherGroups]: () => m.groups_management_title(),
[AppRoutes.TeacherTasks]: () => m.sidebar_admin_tasks(),
[AppRoutes.AdminUsers]: () => m.admin_users_title()
[AppRoutes.AdminUsers]: () => m.admin_users_title(),
[AppRoutes.AdminLanguages]: () => m.admin_languages_title(),
[AppRoutes.AdminWorkerMonitoring]: () => m.sidebar_admin_monitoring()
};

// Check for dynamic routes (e.g., /dashboard/tasks/[taskId])
Expand Down
1 change: 1 addition & 0 deletions src/lib/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export enum AppRoutes {
Admin = `${AppRoutes.Dashboard}/admin`,
AdminUsers = `${AppRoutes.Admin}/users`,
AdminLanguages = `${AppRoutes.Admin}/languages`,
AdminWorkerMonitoring = `${AppRoutes.Admin}/worker-monitoring`,

Error = '/error'
}
Expand Down
18 changes: 18 additions & 0 deletions src/lib/title.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import * as m from '$lib/paraglide/messages';

export const APP_NAME = 'Mini-Maxit';

/**
* Builds a browser tab title with a consistent identification pattern.
* e.g. `Test Contest - Contest · Mini-Maxit`
*
* @param name - the resource name (task title, contest name, user name...)
* @param type - the resource type label (translated, e.g. "Contest")
*/
export function buildDocumentTitle(name?: string, type?: string): string {
const parts: string[] = [];
if (name) parts.push(name);
if (type) parts.push(type);
if (parts.length === 0) return m.app_name();
return `${parts.join(' - ')} · ${APP_NAME}`;
}
5 changes: 5 additions & 0 deletions src/routes/+layout.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import { page } from '$app/state';
import { getApiInstance } from '$lib/services';
import { isProtectedRoute } from '$lib/routes';
import { buildDocumentTitle } from '$lib/title';

let { children } = $props();

Expand All @@ -26,6 +27,10 @@
});
</script>

<svelte:head>
<title>{buildDocumentTitle()}</title>
</svelte:head>

<Toaster />

{@render children?.()}
6 changes: 5 additions & 1 deletion src/routes/dashboard/+layout.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@
import { getUserInstance, getApiInstance } from '$lib/services';
import { LoadingSpinner } from '$lib/components/common';
import Footer from '$lib/components/Footer.svelte';
import { buildDocumentTitle } from '$lib/title';

let { children } = $props();

const pageTitle = $derived(getDashboardTitleTranslationFromPathname(page.url.pathname));
const isLoading = $derived(userStore.isLoading());

// Client-side authentication guard and user fetch
onMount(async () => {
if (!browser) return;
Expand Down Expand Up @@ -58,6 +58,10 @@
});
</script>

<svelte:head>
<title>{buildDocumentTitle(pageTitle)}</title>
</svelte:head>

{#if isLoading || !userStore.tryGetUser()}
<div class="flex h-screen items-center justify-center">
<LoadingSpinner />
Expand Down
4 changes: 0 additions & 4 deletions src/routes/dashboard/tasks/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,6 @@
});
</script>

<svelte:head>
<title>{m.tasks_page_title()}</title>
</svelte:head>

<div class="container mx-auto p-6">
<div class="mb-8">
<h1 class="mb-2 text-4xl font-bold text-foreground">{m.tasks_title()}</h1>
Expand Down
11 changes: 11 additions & 0 deletions src/routes/dashboard/tasks/[taskId]/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import FilePreview from '$lib/components/dashboard/tasks/task-page/tasks/FilePreview.svelte';
import { LoadingSpinner, ErrorCard } from '$lib/components/common';
import * as m from '$lib/paraglide/messages';
import { buildDocumentTitle } from '$lib/title';

const taskService = getTaskInstance();
const submissionService = getSubmissionInstance();
Expand All @@ -32,8 +33,18 @@
});

let fileContent = $state<string>('');

const documentTitle = $derived(
buildDocumentTitle(taskQuery.current?.title, m.title_type_task())
);
</script>

<svelte:head>
{#if taskQuery.current?.title}
<title>{documentTitle}</title>
{/if}
</svelte:head>

<div class="space-y-6">
{#if taskQuery.error}
<ErrorCard
Expand Down
20 changes: 20 additions & 0 deletions src/routes/dashboard/user/contests/[contestId]/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,20 @@
import { createParameterizedQuery } from '$lib/utils/query.svelte';
import { getContestInstance } from '$lib/services';
import { AppRoutes } from '$lib/routes';
import { buildDocumentTitle } from '$lib/title';

const contestService = getContestInstance();
const contestId = $derived(Number(page.params.contestId));

const contestQuery = createParameterizedQuery(
() => contestId,
async (id) => {
if (!contestService) throw new Error('Service unavailable');
if (!Number.isFinite(id)) throw new Error('Invalid contest ID');
return await contestService.getContest(id);
}
);

const tasksQuery = createParameterizedQuery(
() => contestId,
async (id) => {
Expand All @@ -25,8 +35,18 @@
return result.data!;
}
);

const documentTitle = $derived(
buildDocumentTitle(contestQuery.current?.name, m.title_type_contest())
);
</script>

<svelte:head>
{#if contestQuery.current?.name}
<title>{documentTitle}</title>
{/if}
</svelte:head>

<div class="space-y-6 p-4 sm:p-6 lg:p-8">
<div class="space-y-2">
<h1 class="text-4xl font-bold tracking-tight text-foreground">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import { format } from 'date-fns';
import * as m from '$lib/paraglide/messages';
import type { TaskResult } from '$lib/dto/contest';
import { buildDocumentTitle } from '$lib/title';

const contestService = getContestInstance();
const contestId = $derived(Number(page.params.contestId));
Expand Down Expand Up @@ -44,8 +45,18 @@
const date = new Date(dateString);
return format(date, 'MMM dd, yyyy');
}

const documentTitle = $derived(
buildDocumentTitle(resultsQuery.current?.contest?.name, m.title_type_results())
);
</script>

<svelte:head>
{#if resultsQuery.current?.contest?.name}
<title>{documentTitle}</title>
{/if}
</svelte:head>

<div class="space-y-6 p-4 sm:p-6 lg:p-8">
<div class="space-y-2">
<h1 class="text-4xl font-bold tracking-tight text-foreground">{m.contest_results_title()}</h1>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import { LoadingSpinner, ErrorCard } from '$lib/components/common';
import * as m from '$lib/paraglide/messages';
import TaskHeader from '$lib/components/dashboard/tasks/task-page/tasks/TaskHeader.svelte';
import { buildDocumentTitle } from '$lib/title';

const taskService = getTaskInstance();
const submissionService = getSubmissionInstance();
Expand All @@ -34,8 +35,18 @@
});

let fileContent = $state<string>('');

const documentTitle = $derived(
buildDocumentTitle(taskQuery.current?.title, m.title_type_task())
);
</script>

<svelte:head>
{#if taskQuery.current?.title}
<title>{documentTitle}</title>
{/if}
</svelte:head>

<div class="space-y-6">
{#if taskQuery.error}
<ErrorCard
Expand Down
10 changes: 10 additions & 0 deletions src/routes/dashboard/user/profile/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,20 @@
import QuickActions from '$lib/components/dashboard/profile/QuickActions.svelte';
import { userStore } from '$lib/stores/user-store.svelte';
import * as m from '$lib/paraglide/messages';
import { buildDocumentTitle } from '$lib/title';

const user = $derived(userStore.getUserUnsafe());
const documentTitle = $derived(
user ? buildDocumentTitle(`${user.name} ${user.surname}`, m.title_type_profile()) : ''
);
</script>

<svelte:head>
{#if user}
<title>{documentTitle}</title>
{/if}
</svelte:head>

<div class="space-y-8 p-4 sm:p-6 lg:p-8">
<!-- Page Header -->
<div class="space-y-2">
Expand Down
11 changes: 11 additions & 0 deletions src/routes/dashboard/user/submissions/[submissionId]/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import * as Card from '$lib/components/ui/card';
import * as m from '$lib/paraglide/messages';
import { formatDate } from '$lib/utils';
import { buildDocumentTitle } from '$lib/title';
import TestCaseResult from '$lib/components/dashboard/submissions/TestCaseResult.svelte';
import { SubmissionStatus, SubmissionResultCode, type TestResult } from '$lib/dto/submission';
import CheckCircle from '@lucide/svelte/icons/check-circle';
Expand Down Expand Up @@ -143,8 +144,18 @@
const total = submissionQuery.current.result.testResults.length;
return `${passed}/${total}`;
};

const documentTitle = $derived(
buildDocumentTitle(submissionQuery.current?.task?.title, m.title_type_submission())
);
</script>

<svelte:head>
{#if submissionQuery.current?.task?.title}
<title>{documentTitle}</title>
{/if}
</svelte:head>

<div class="space-y-8 p-4 sm:p-6 lg:p-8">
<!-- Page Header -->
<div class="space-y-2">
Expand Down
Loading