mirror of
https://github.com/KohakuBlueleaf/KohakuHub.git
synced 2026-03-11 17:34:08 -05:00
Re org kohakuboard ui routes
This commit is contained in:
@@ -2,11 +2,13 @@
|
||||
import { ref, onMounted } from "vue";
|
||||
import { initializeSliderSync } from "@/composables/useSliderSync";
|
||||
import { useAnimationPreference } from "@/composables/useAnimationPreference";
|
||||
import { getSystemInfo } from "@/utils/api";
|
||||
|
||||
const darkMode = ref(false);
|
||||
const { animationsEnabled, toggleAnimations } = useAnimationPreference();
|
||||
const systemInfo = ref(null);
|
||||
|
||||
onMounted(() => {
|
||||
onMounted(async () => {
|
||||
const savedTheme = localStorage.getItem("theme");
|
||||
darkMode.value =
|
||||
savedTheme === "dark" ||
|
||||
@@ -15,6 +17,13 @@ onMounted(() => {
|
||||
|
||||
// Initialize global slider synchronization
|
||||
initializeSliderSync();
|
||||
|
||||
// Fetch system info for mode detection
|
||||
try {
|
||||
systemInfo.value = await getSystemInfo();
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch system info:", error);
|
||||
}
|
||||
});
|
||||
|
||||
function toggleDarkMode() {
|
||||
@@ -59,13 +68,13 @@ function updateTheme() {
|
||||
to="/"
|
||||
class="px-3 py-1.5 rounded-md text-sm font-medium transition-colors text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800 hover:text-blue-600 dark:hover:text-blue-400"
|
||||
>
|
||||
Dashboard
|
||||
Projects
|
||||
</router-link>
|
||||
<router-link
|
||||
to="/experiments"
|
||||
class="px-3 py-1.5 rounded-md text-sm font-medium transition-colors text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800 hover:text-blue-600 dark:hover:text-blue-400"
|
||||
>
|
||||
Experiments
|
||||
Experiments (Legacy)
|
||||
</router-link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,24 +1,25 @@
|
||||
<script setup>
|
||||
import { ref, onMounted } from "vue";
|
||||
import { fetchExperiments } from "@/utils/api";
|
||||
import { fetchProjects } from "@/utils/api";
|
||||
import { useRouter } from "vue-router";
|
||||
|
||||
const router = useRouter();
|
||||
const experiments = ref([]);
|
||||
const projects = ref([]);
|
||||
const loading = ref(true);
|
||||
|
||||
onMounted(async () => {
|
||||
try {
|
||||
experiments.value = await fetchExperiments();
|
||||
const result = await fetchProjects();
|
||||
projects.value = result.projects;
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch experiments:", error);
|
||||
console.error("Failed to fetch projects:", error);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
});
|
||||
|
||||
function viewExperiment(id) {
|
||||
router.push(`/experiments/${id}`);
|
||||
function viewProject(projectName) {
|
||||
router.push(`/projects/${projectName}`);
|
||||
}
|
||||
|
||||
function formatDate(timestamp) {
|
||||
@@ -47,76 +48,37 @@ function formatSteps(steps) {
|
||||
</div>
|
||||
|
||||
<div v-if="loading" class="text-center py-12">
|
||||
<div class="text-gray-500 dark:text-gray-400">Loading experiments...</div>
|
||||
<div class="text-gray-500 dark:text-gray-400">Loading projects...</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-else-if="experiments.length === 0"
|
||||
v-else-if="projects.length === 0"
|
||||
class="bg-white dark:bg-gray-900 rounded-lg shadow-md p-8 text-center border border-gray-200 dark:border-gray-800"
|
||||
>
|
||||
<div class="text-gray-500 dark:text-gray-400 mb-4">No boards found</div>
|
||||
<div class="text-gray-500 dark:text-gray-400 mb-4">No projects found</div>
|
||||
<p class="text-sm text-gray-400 dark:text-gray-500">
|
||||
Start tracking your ML experiments with KohakuBoard client library.
|
||||
<br />
|
||||
Boards are automatically discovered from:
|
||||
<code class="bg-gray-100 dark:bg-gray-800 px-2 py-1 rounded"
|
||||
>./kohakuboard</code
|
||||
>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div v-else class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
<div
|
||||
v-for="experiment in experiments"
|
||||
:key="experiment.id"
|
||||
@click="viewExperiment(experiment.id)"
|
||||
class="bg-white dark:bg-gray-900 rounded-lg shadow-md p-4 cursor-pointer hover:shadow-lg transition-all border border-gray-200 dark:border-gray-800 hover:border-blue-400 dark:hover:border-blue-600"
|
||||
v-for="project in projects"
|
||||
:key="project.name"
|
||||
@click="viewProject(project.name)"
|
||||
class="bg-white dark:bg-gray-900 rounded-lg shadow-md p-6 cursor-pointer hover:shadow-lg transition-all border border-gray-200 dark:border-gray-800 hover:border-blue-400 dark:hover:border-blue-600"
|
||||
>
|
||||
<div class="flex items-start justify-between mb-3">
|
||||
<h3 class="font-semibold text-lg text-gray-900 dark:text-gray-100">
|
||||
{{ experiment.name }}
|
||||
</h3>
|
||||
<span
|
||||
class="px-2 py-1 text-xs rounded font-medium"
|
||||
:class="{
|
||||
'bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400':
|
||||
experiment.status === 'completed',
|
||||
'bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-400':
|
||||
experiment.status === 'running',
|
||||
'bg-gray-100 text-gray-700 dark:bg-gray-800 dark:text-gray-400':
|
||||
experiment.status === 'stopped',
|
||||
}"
|
||||
>
|
||||
{{ experiment.status }}
|
||||
</span>
|
||||
<h3 class="font-semibold text-xl text-gray-900 dark:text-gray-100 mb-2">
|
||||
{{ project.display_name }}
|
||||
</h3>
|
||||
<div class="text-gray-600 dark:text-gray-400 mb-4">
|
||||
{{ project.run_count }} {{ project.run_count === 1 ? "run" : "runs" }}
|
||||
</div>
|
||||
|
||||
<p class="text-sm text-gray-600 dark:text-gray-400 mb-3">
|
||||
{{ experiment.description }}
|
||||
</p>
|
||||
|
||||
<div class="grid grid-cols-2 gap-2 text-sm">
|
||||
<div>
|
||||
<div class="text-gray-500 dark:text-gray-500">Board ID</div>
|
||||
<div
|
||||
class="font-mono text-xs text-gray-900 dark:text-gray-100 truncate"
|
||||
:title="experiment.id"
|
||||
>
|
||||
{{ experiment.id }}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="text-gray-500 dark:text-gray-500">Status</div>
|
||||
<div class="font-medium text-gray-900 dark:text-gray-100">
|
||||
{{ experiment.status }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="mt-3 pt-3 border-t border-gray-200 dark:border-gray-800 text-xs text-gray-500 dark:text-gray-500"
|
||||
v-if="project.updated_at"
|
||||
class="text-xs text-gray-500 dark:text-gray-500"
|
||||
>
|
||||
Created: {{ formatDate(experiment.created_at) }}
|
||||
Updated: {{ formatDate(project.updated_at) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
1087
src/kohaku-board-ui/src/pages/projects/[project]/[id].vue
Normal file
1087
src/kohaku-board-ui/src/pages/projects/[project]/[id].vue
Normal file
File diff suppressed because it is too large
Load Diff
140
src/kohaku-board-ui/src/pages/projects/[project]/index.vue
Normal file
140
src/kohaku-board-ui/src/pages/projects/[project]/index.vue
Normal file
@@ -0,0 +1,140 @@
|
||||
<script setup>
|
||||
import { ref, onMounted } from "vue";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
import { fetchProjectRuns } from "@/utils/api";
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const project = ref(route.params.project);
|
||||
const runs = ref([]);
|
||||
const loading = ref(true);
|
||||
const projectOwner = ref(null);
|
||||
|
||||
onMounted(async () => {
|
||||
try {
|
||||
const result = await fetchProjectRuns(project.value);
|
||||
runs.value = result.runs;
|
||||
projectOwner.value = result.owner;
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch runs:", error);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
});
|
||||
|
||||
function viewRun(runId) {
|
||||
// Use the main experiments viewer (the most important page!)
|
||||
router.push(`/experiments/${runId}`);
|
||||
}
|
||||
|
||||
function formatDate(timestamp) {
|
||||
if (!timestamp) return "N/A";
|
||||
return new Date(timestamp).toLocaleString();
|
||||
}
|
||||
|
||||
function formatSize(bytes) {
|
||||
if (!bytes) return "N/A";
|
||||
const mb = bytes / 1024 / 1024;
|
||||
if (mb < 1) return `${(bytes / 1024).toFixed(1)} KB`;
|
||||
if (mb < 1024) return `${mb.toFixed(1)} MB`;
|
||||
return `${(mb / 1024).toFixed(2)} GB`;
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div class="flex items-center justify-between mb-6">
|
||||
<div>
|
||||
<h1 class="text-3xl font-bold text-gray-900 dark:text-gray-100">
|
||||
{{ project }}
|
||||
</h1>
|
||||
<p
|
||||
v-if="projectOwner"
|
||||
class="text-sm text-gray-500 dark:text-gray-400 mt-1"
|
||||
>
|
||||
Owner: {{ projectOwner }}
|
||||
</p>
|
||||
</div>
|
||||
<router-link
|
||||
to="/projects"
|
||||
class="px-4 py-2 bg-gray-600 hover:bg-gray-700 dark:bg-gray-500 dark:hover:bg-gray-600 text-white rounded-md font-medium transition-colors shadow-sm"
|
||||
>
|
||||
← Back to Projects
|
||||
</router-link>
|
||||
</div>
|
||||
|
||||
<div v-if="loading" class="text-center py-12">
|
||||
<div class="text-gray-500 dark:text-gray-400">Loading runs...</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-else-if="runs.length === 0"
|
||||
class="bg-white dark:bg-gray-900 rounded-lg shadow-md p-8 text-center border border-gray-200 dark:border-gray-800"
|
||||
>
|
||||
<div class="text-gray-500 dark:text-gray-400 mb-4">No runs found</div>
|
||||
<p class="text-sm text-gray-400 dark:text-gray-500">
|
||||
This project doesn't have any training runs yet.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div v-else class="grid grid-cols-1 gap-4">
|
||||
<div
|
||||
v-for="run in runs"
|
||||
:key="run.run_id"
|
||||
@click="viewRun(run.run_id)"
|
||||
class="bg-white dark:bg-gray-900 rounded-lg shadow-md p-4 cursor-pointer hover:shadow-lg transition-all border border-gray-200 dark:border-gray-800 hover:border-blue-400 dark:hover:border-blue-600"
|
||||
>
|
||||
<div class="flex items-start justify-between">
|
||||
<div class="flex-1">
|
||||
<h3 class="font-semibold text-lg text-gray-900 dark:text-gray-100">
|
||||
{{ run.name }}
|
||||
</h3>
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400 font-mono mt-1">
|
||||
{{ run.run_id }}
|
||||
</p>
|
||||
</div>
|
||||
<div v-if="run.private !== undefined" class="ml-4">
|
||||
<span
|
||||
class="px-2 py-1 text-xs rounded font-medium"
|
||||
:class="{
|
||||
'bg-gray-100 text-gray-700 dark:bg-gray-800 dark:text-gray-400':
|
||||
run.private,
|
||||
'bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400':
|
||||
!run.private,
|
||||
}"
|
||||
>
|
||||
{{ run.private ? "Private" : "Public" }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 md:grid-cols-4 gap-4 mt-4 text-sm">
|
||||
<div>
|
||||
<div class="text-gray-500 dark:text-gray-500">Created</div>
|
||||
<div class="text-gray-900 dark:text-gray-100">
|
||||
{{ formatDate(run.created_at) }}
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="run.last_synced_at">
|
||||
<div class="text-gray-500 dark:text-gray-500">Last Synced</div>
|
||||
<div class="text-gray-900 dark:text-gray-100">
|
||||
{{ formatDate(run.last_synced_at) }}
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="run.total_size">
|
||||
<div class="text-gray-500 dark:text-gray-500">Size</div>
|
||||
<div class="text-gray-900 dark:text-gray-100">
|
||||
{{ formatSize(run.total_size) }}
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="run.config && Object.keys(run.config).length > 0">
|
||||
<div class="text-gray-500 dark:text-gray-500">Config</div>
|
||||
<div class="text-gray-900 dark:text-gray-100 text-xs">
|
||||
{{ Object.keys(run.config).length }} params
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
75
src/kohaku-board-ui/src/pages/projects/index.vue
Normal file
75
src/kohaku-board-ui/src/pages/projects/index.vue
Normal file
@@ -0,0 +1,75 @@
|
||||
<script setup>
|
||||
import { ref, onMounted } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import { fetchProjects } from "@/utils/api";
|
||||
|
||||
const router = useRouter();
|
||||
const projects = ref([]);
|
||||
const loading = ref(true);
|
||||
|
||||
onMounted(async () => {
|
||||
try {
|
||||
const result = await fetchProjects();
|
||||
projects.value = result.projects;
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch projects:", error);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
});
|
||||
|
||||
function viewProject(projectName) {
|
||||
router.push(`/projects/${projectName}`);
|
||||
}
|
||||
|
||||
function formatDate(timestamp) {
|
||||
if (!timestamp) return "N/A";
|
||||
return new Date(timestamp).toLocaleString();
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div class="flex items-center justify-between mb-6">
|
||||
<h1 class="text-3xl font-bold text-gray-900 dark:text-gray-100">
|
||||
Projects
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
<div v-if="loading" class="text-center py-12">
|
||||
<div class="text-gray-500 dark:text-gray-400">Loading projects...</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-else-if="projects.length === 0"
|
||||
class="bg-white dark:bg-gray-900 rounded-lg shadow-md p-8 text-center border border-gray-200 dark:border-gray-800"
|
||||
>
|
||||
<div class="text-gray-500 dark:text-gray-400 mb-4">No projects found</div>
|
||||
<p class="text-sm text-gray-400 dark:text-gray-500">
|
||||
Start tracking your ML experiments with KohakuBoard client library.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div v-else class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
<div
|
||||
v-for="project in projects"
|
||||
:key="project.name"
|
||||
@click="viewProject(project.name)"
|
||||
class="bg-white dark:bg-gray-900 rounded-lg shadow-md p-6 cursor-pointer hover:shadow-lg transition-all border border-gray-200 dark:border-gray-800 hover:border-blue-400 dark:hover:border-blue-600"
|
||||
>
|
||||
<h3 class="font-semibold text-xl text-gray-900 dark:text-gray-100 mb-2">
|
||||
{{ project.display_name }}
|
||||
</h3>
|
||||
<div class="text-gray-600 dark:text-gray-400 mb-4">
|
||||
{{ project.run_count }} {{ project.run_count === 1 ? "run" : "runs" }}
|
||||
</div>
|
||||
<div
|
||||
v-if="project.updated_at"
|
||||
class="text-xs text-gray-500 dark:text-gray-500"
|
||||
>
|
||||
Updated: {{ formatDate(project.updated_at) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
15
src/kohaku-board-ui/src/typed-router.d.ts
vendored
15
src/kohaku-board-ui/src/typed-router.d.ts
vendored
@@ -22,6 +22,9 @@ declare module 'vue-router/auto-routes' {
|
||||
'/dashboard/[id]': RouteRecordInfo<'/dashboard/[id]', '/dashboard/:id', { id: ParamValue<true> }, { id: ParamValue<false> }>,
|
||||
'/experiments/': RouteRecordInfo<'/experiments/', '/experiments', Record<never, never>, Record<never, never>>,
|
||||
'/experiments/[id]': RouteRecordInfo<'/experiments/[id]', '/experiments/:id', { id: ParamValue<true> }, { id: ParamValue<false> }>,
|
||||
'/projects/': RouteRecordInfo<'/projects/', '/projects', Record<never, never>, Record<never, never>>,
|
||||
'/projects/[project]/': RouteRecordInfo<'/projects/[project]/', '/projects/:project', { project: ParamValue<true> }, { project: ParamValue<false> }>,
|
||||
'/projects/[project]/[id]': RouteRecordInfo<'/projects/[project]/[id]', '/projects/:project/:id', { project: ParamValue<true>, id: ParamValue<true> }, { project: ParamValue<false>, id: ParamValue<false> }>,
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -51,6 +54,18 @@ declare module 'vue-router/auto-routes' {
|
||||
routes: '/experiments/[id]'
|
||||
views: never
|
||||
}
|
||||
'src/pages/projects/index.vue': {
|
||||
routes: '/projects/'
|
||||
views: never
|
||||
}
|
||||
'src/pages/projects/[project]/index.vue': {
|
||||
routes: '/projects/[project]/'
|
||||
views: never
|
||||
}
|
||||
'src/pages/projects/[project]/[id].vue': {
|
||||
routes: '/projects/[project]/[id]'
|
||||
views: never
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -74,7 +74,43 @@ export async function generateMockData(config) {
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// BOARDS API - Real data from file system
|
||||
// SYSTEM API - Mode detection
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Get system information (mode, auth requirements, user info)
|
||||
* @returns {Promise<Object>} System info
|
||||
*/
|
||||
export async function getSystemInfo() {
|
||||
const response = await api.get("/system/info");
|
||||
return response.data;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// PROJECTS API - Project and run management
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Fetch all accessible projects
|
||||
* @returns {Promise<Object>} Projects list
|
||||
*/
|
||||
export async function fetchProjects() {
|
||||
const response = await api.get("/projects");
|
||||
return response.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch runs in a project
|
||||
* @param {string} projectName - Project name
|
||||
* @returns {Promise<Object>} Runs list with project info
|
||||
*/
|
||||
export async function fetchProjectRuns(projectName) {
|
||||
const response = await api.get(`/projects/${projectName}/runs`);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// BOARDS API - Real data from file system (LEGACY - kept for compatibility)
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
@@ -106,6 +142,119 @@ export async function fetchBoardSummary(boardId) {
|
||||
return response.data;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// RUN DATA API - Project-based run access
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Fetch run summary
|
||||
* @param {string} project - Project name
|
||||
* @param {string} runId - Run ID
|
||||
* @returns {Promise<Object>} Run summary
|
||||
*/
|
||||
export async function fetchRunSummary(project, runId) {
|
||||
const response = await api.get(`/projects/${project}/runs/${runId}/summary`);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch available scalar metrics for a run
|
||||
* @param {string} project - Project name
|
||||
* @param {string} runId - Run ID
|
||||
* @returns {Promise<Object>} Object with metrics array
|
||||
*/
|
||||
export async function fetchRunScalars(project, runId) {
|
||||
const response = await api.get(`/projects/${project}/runs/${runId}/scalars`);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch scalar data for a specific metric
|
||||
* @param {string} project - Project name
|
||||
* @param {string} runId - Run ID
|
||||
* @param {string} metric - Metric name
|
||||
* @param {Object} params - Query parameters (limit, etc.)
|
||||
* @returns {Promise<Object>} Object with metric name and data array
|
||||
*/
|
||||
export async function fetchRunScalarData(project, runId, metric, params = {}) {
|
||||
const response = await api.get(
|
||||
`/projects/${project}/runs/${runId}/scalars/${metric}`,
|
||||
{
|
||||
params,
|
||||
},
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch available media log names
|
||||
* @param {string} project - Project name
|
||||
* @param {string} runId - Run ID
|
||||
* @returns {Promise<Object>} Object with media array
|
||||
*/
|
||||
export async function fetchRunMedia(project, runId) {
|
||||
const response = await api.get(`/projects/${project}/runs/${runId}/media`);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch media data for a specific log name
|
||||
* @param {string} project - Project name
|
||||
* @param {string} runId - Run ID
|
||||
* @param {string} name - Media log name
|
||||
* @param {Object} params - Query parameters (limit, etc.)
|
||||
* @returns {Promise<Object>} Object with name and data array
|
||||
*/
|
||||
export async function fetchRunMediaData(project, runId, name, params = {}) {
|
||||
const response = await api.get(
|
||||
`/projects/${project}/runs/${runId}/media/${name}`,
|
||||
{
|
||||
params,
|
||||
},
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get media file URL
|
||||
* @param {string} project - Project name
|
||||
* @param {string} runId - Run ID
|
||||
* @param {string} filename - Media filename
|
||||
* @returns {string} Media file URL
|
||||
*/
|
||||
export function getRunMediaFileUrl(project, runId, filename) {
|
||||
return `/api/projects/${project}/runs/${runId}/media/files/${filename}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch available table log names
|
||||
* @param {string} project - Project name
|
||||
* @param {string} runId - Run ID
|
||||
* @returns {Promise<Object>} Object with tables array
|
||||
*/
|
||||
export async function fetchRunTables(project, runId) {
|
||||
const response = await api.get(`/projects/${project}/runs/${runId}/tables`);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch table data for a specific log name
|
||||
* @param {string} project - Project name
|
||||
* @param {string} runId - Run ID
|
||||
* @param {string} name - Table log name
|
||||
* @param {Object} params - Query parameters (limit, etc.)
|
||||
* @returns {Promise<Object>} Object with name and data array
|
||||
*/
|
||||
export async function fetchRunTableData(project, runId, name, params = {}) {
|
||||
const response = await api.get(
|
||||
`/projects/${project}/runs/${runId}/tables/${name}`,
|
||||
{
|
||||
params,
|
||||
},
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch available scalar metrics for a board
|
||||
* @param {string} boardId - Board identifier
|
||||
|
||||
Reference in New Issue
Block a user