Webapp: avoid browser JS errors about forbidden 'User-Agent' header
Brave (and maybe other browseres) refuse to set the 'User-Agent' header in XMLHTTPRequests, and are vocal about this in the debug log. Since the OpenAPI code generator always outputs a custom 'User-Agent' header, I've added some JS code to strip that off when constructing an API client.
This commit is contained in:
parent
a5cfa9959b
commit
1add6bfc8a
@ -26,7 +26,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import * as API from '@/manager-api';
|
import * as API from '@/manager-api';
|
||||||
import { apiClient } from '@/stores/api-query-count';
|
import { getAPIClient } from "@/api-client";
|
||||||
import { backendURL } from '@/urls';
|
import { backendURL } from '@/urls';
|
||||||
|
|
||||||
import ApiSpinner from '@/components/ApiSpinner.vue'
|
import ApiSpinner from '@/components/ApiSpinner.vue'
|
||||||
@ -51,7 +51,7 @@ export default {
|
|||||||
methods: {
|
methods: {
|
||||||
// TODO: also call this when SocketIO reconnects.
|
// TODO: also call this when SocketIO reconnects.
|
||||||
fetchManagerInfo() {
|
fetchManagerInfo() {
|
||||||
const metaAPI = new API.MetaApi(apiClient);
|
const metaAPI = new API.MetaApi(getAPIClient());
|
||||||
metaAPI.getVersion().then((version) => {
|
metaAPI.getVersion().then((version) => {
|
||||||
this.flamencoName = version.name;
|
this.flamencoName = version.name;
|
||||||
this.flamencoVersion = version.version;
|
this.flamencoVersion = version.version;
|
||||||
|
@ -17,7 +17,7 @@ const DEFAULT_FLAMENCO_NAME = "Flamenco";
|
|||||||
const DEFAULT_FLAMENCO_VERSION = "unknown";
|
const DEFAULT_FLAMENCO_VERSION = "unknown";
|
||||||
import ApiSpinner from '@/components/ApiSpinner.vue'
|
import ApiSpinner from '@/components/ApiSpinner.vue'
|
||||||
import { MetaApi } from "@/manager-api";
|
import { MetaApi } from "@/manager-api";
|
||||||
import { apiClient } from '@/stores/api-query-count';
|
import { getAPIClient } from "@/api-client";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'SetupAssistant',
|
name: 'SetupAssistant',
|
||||||
@ -35,7 +35,7 @@ export default {
|
|||||||
methods: {
|
methods: {
|
||||||
// TODO: also call this when SocketIO reconnects.
|
// TODO: also call this when SocketIO reconnects.
|
||||||
fetchManagerInfo() {
|
fetchManagerInfo() {
|
||||||
const metaAPI = new MetaApi(apiClient);
|
const metaAPI = new MetaApi(getAPIClient());
|
||||||
metaAPI.getVersion().then((version) => {
|
metaAPI.getVersion().then((version) => {
|
||||||
this.flamencoName = version.name;
|
this.flamencoName = version.name;
|
||||||
this.flamencoVersion = version.version;
|
this.flamencoVersion = version.version;
|
||||||
|
39
web/app/src/api-client.js
Normal file
39
web/app/src/api-client.js
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import { ApiClient } from "@/manager-api";
|
||||||
|
import { CountingApiClient } from "@/stores/api-query-count";
|
||||||
|
import { api as apiURL } from '@/urls'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scrub the custom User-Agent header from the API client, for those webbrowsers
|
||||||
|
* who do not want to have it set.
|
||||||
|
*
|
||||||
|
* It's actually scrubbed for all webbrowsers, as those privacy-first
|
||||||
|
* webbrowsers also make it hard to fingerprint which browser you're using (for
|
||||||
|
* good reason).
|
||||||
|
*
|
||||||
|
* @param {ApiClient} apiClient
|
||||||
|
*/
|
||||||
|
export function scrubAPIClient(apiClient) {
|
||||||
|
delete apiClient.defaultHeaders['User-Agent'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {ApiClient} Bare API client that is not connected to the UI in any way.
|
||||||
|
*/
|
||||||
|
export function newBareAPIClient() {
|
||||||
|
const apiClient = new ApiClient(apiURL());
|
||||||
|
scrubAPIClient(apiClient);
|
||||||
|
return apiClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
let apiClient = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {ApiClient} API client that updates the UI to show long-running queries.
|
||||||
|
*/
|
||||||
|
export function getAPIClient() {
|
||||||
|
if (apiClient == null) {
|
||||||
|
apiClient = new CountingApiClient(apiURL());
|
||||||
|
scrubAPIClient(apiClient);
|
||||||
|
}
|
||||||
|
return apiClient;
|
||||||
|
}
|
@ -26,7 +26,7 @@
|
|||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
|
|
||||||
import { useNotifs } from '@/stores/notifications';
|
import { useNotifs } from '@/stores/notifications';
|
||||||
import { apiClient } from '@/stores/api-query-count';
|
import { getAPIClient } from "@/api-client";
|
||||||
import { JobsApi, JobPriorityChange } from '@/manager-api';
|
import { JobsApi, JobPriorityChange } from '@/manager-api';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
@ -45,7 +45,7 @@ const errorMessage = ref('');
|
|||||||
// Methods
|
// Methods
|
||||||
function updateJobPriority() {
|
function updateJobPriority() {
|
||||||
const jobPriorityChange = new JobPriorityChange(priorityState.value);
|
const jobPriorityChange = new JobPriorityChange(priorityState.value);
|
||||||
const jobsAPI = new JobsApi(apiClient);
|
const jobsAPI = new JobsApi(getAPIClient());
|
||||||
return jobsAPI.setJobPriority(props.jobId, jobPriorityChange)
|
return jobsAPI.setJobPriority(props.jobId, jobPriorityChange)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
notifs.add(`Updated job priority to ${priorityState.value}`)
|
notifs.add(`Updated job priority to ${priorityState.value}`)
|
||||||
|
@ -3,7 +3,7 @@ import { onMounted, onUnmounted } from 'vue'
|
|||||||
import { TabulatorFull as Tabulator } from 'tabulator-tables';
|
import { TabulatorFull as Tabulator } from 'tabulator-tables';
|
||||||
import { useTaskLog } from '@/stores/tasklog'
|
import { useTaskLog } from '@/stores/tasklog'
|
||||||
import { useTasks } from '@/stores/tasks'
|
import { useTasks } from '@/stores/tasks'
|
||||||
import { apiClient } from '@/stores/api-query-count';
|
import { getAPIClient } from "@/api-client";
|
||||||
import { JobsApi } from '@/manager-api';
|
import { JobsApi } from '@/manager-api';
|
||||||
|
|
||||||
const taskLog = useTaskLog();
|
const taskLog = useTaskLog();
|
||||||
@ -59,7 +59,7 @@ function _fetchLogTail(taskID) {
|
|||||||
|
|
||||||
if (!taskID) return;
|
if (!taskID) return;
|
||||||
|
|
||||||
const jobsAPI = new JobsApi(apiClient);
|
const jobsAPI = new JobsApi(getAPIClient());
|
||||||
return jobsAPI.fetchTaskLogTail(taskID)
|
return jobsAPI.fetchTaskLogTail(taskID)
|
||||||
.then((logTail) => {
|
.then((logTail) => {
|
||||||
taskLog.addChunk(logTail);
|
taskLog.addChunk(logTail);
|
||||||
|
@ -26,7 +26,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { apiClient } from '@/stores/api-query-count';
|
import { getAPIClient } from "@/api-client";
|
||||||
import { JobsApi } from '@/manager-api';
|
import { JobsApi } from '@/manager-api';
|
||||||
import LinkWorker from '@/components/LinkWorker.vue';
|
import LinkWorker from '@/components/LinkWorker.vue';
|
||||||
import { watch, onMounted, inject, ref, nextTick } from 'vue'
|
import { watch, onMounted, inject, ref, nextTick } from 'vue'
|
||||||
@ -35,7 +35,7 @@ import { watch, onMounted, inject, ref, nextTick } from 'vue'
|
|||||||
const props = defineProps(['jobID']);
|
const props = defineProps(['jobID']);
|
||||||
const emit = defineEmits(['reshuffled'])
|
const emit = defineEmits(['reshuffled'])
|
||||||
|
|
||||||
const jobsApi = new JobsApi(apiClient);
|
const jobsApi = new JobsApi(getAPIClient());
|
||||||
const isVisible = inject("isVisible");
|
const isVisible = inject("isVisible");
|
||||||
const isFetching = ref(false);
|
const isFetching = ref(false);
|
||||||
const errorMsg = ref("");
|
const errorMsg = ref("");
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
<script>
|
<script>
|
||||||
import { useJobs } from '@/stores/jobs';
|
import { useJobs } from '@/stores/jobs';
|
||||||
import { useNotifs } from '@/stores/notifications';
|
import { useNotifs } from '@/stores/notifications';
|
||||||
import { apiClient } from '@/stores/api-query-count';
|
import { getAPIClient } from "@/api-client";
|
||||||
import { JobsApi } from '@/manager-api';
|
import { JobsApi } from '@/manager-api';
|
||||||
import { JobDeletionInfo } from '@/manager-api';
|
import { JobDeletionInfo } from '@/manager-api';
|
||||||
|
|
||||||
@ -31,7 +31,7 @@ export default {
|
|||||||
data: () => ({
|
data: () => ({
|
||||||
jobs: useJobs(),
|
jobs: useJobs(),
|
||||||
notifs: useNotifs(),
|
notifs: useNotifs(),
|
||||||
jobsAPI: new JobsApi(apiClient),
|
jobsAPI: new JobsApi(getAPIClient()),
|
||||||
|
|
||||||
deleteInfo: null,
|
deleteInfo: null,
|
||||||
}),
|
}),
|
||||||
|
@ -39,7 +39,7 @@
|
|||||||
<dd class="field-status-label" :class="'status-' + jobData.status">{{ jobData.status }}</dd>
|
<dd class="field-status-label" :class="'status-' + jobData.status">{{ jobData.status }}</dd>
|
||||||
|
|
||||||
<dt class="field-type" title="Type">Type</dt>
|
<dt class="field-type" title="Type">Type</dt>
|
||||||
<dd>{{ jobType? jobType.label : jobData.type }}</dd>
|
<dd>{{ jobType ? jobType.label : jobData.type }}</dd>
|
||||||
|
|
||||||
<dt class="field-priority" title="Priority">Priority</dt>
|
<dt class="field-priority" title="Priority">Priority</dt>
|
||||||
<dd>
|
<dd>
|
||||||
@ -76,7 +76,7 @@
|
|||||||
<script>
|
<script>
|
||||||
import * as datetime from "@/datetime";
|
import * as datetime from "@/datetime";
|
||||||
import * as API from '@/manager-api';
|
import * as API from '@/manager-api';
|
||||||
import { apiClient } from '@/stores/api-query-count';
|
import { getAPIClient } from "@/api-client";
|
||||||
import LastRenderedImage from '@/components/jobs/LastRenderedImage.vue'
|
import LastRenderedImage from '@/components/jobs/LastRenderedImage.vue'
|
||||||
import Blocklist from './Blocklist.vue'
|
import Blocklist from './Blocklist.vue'
|
||||||
import TabItem from '@/components/TabItem.vue'
|
import TabItem from '@/components/TabItem.vue'
|
||||||
@ -103,7 +103,7 @@ export default {
|
|||||||
datetime: datetime, // So that the template can access it.
|
datetime: datetime, // So that the template can access it.
|
||||||
copyElementText: copyElementText,
|
copyElementText: copyElementText,
|
||||||
simpleSettings: null, // Object with filtered job settings, or null if there is no job.
|
simpleSettings: null, // Object with filtered job settings, or null if there is no job.
|
||||||
jobsApi: new API.JobsApi(apiClient),
|
jobsApi: new API.JobsApi(getAPIClient()),
|
||||||
jobType: null, // API.AvailableJobType object for the current job type.
|
jobType: null, // API.AvailableJobType object for the current job type.
|
||||||
jobTypeSettings: null, // Mapping from setting key to its definition in the job type.
|
jobTypeSettings: null, // Mapping from setting key to its definition in the job type.
|
||||||
showAllSettings: false,
|
showAllSettings: false,
|
||||||
|
@ -17,7 +17,7 @@ import { TabulatorFull as Tabulator } from 'tabulator-tables';
|
|||||||
import * as datetime from "@/datetime";
|
import * as datetime from "@/datetime";
|
||||||
import * as API from '@/manager-api'
|
import * as API from '@/manager-api'
|
||||||
import { indicator } from '@/statusindicator';
|
import { indicator } from '@/statusindicator';
|
||||||
import { apiClient } from '@/stores/api-query-count';
|
import { getAPIClient } from "@/api-client";
|
||||||
import { useJobs } from '@/stores/jobs';
|
import { useJobs } from '@/stores/jobs';
|
||||||
|
|
||||||
import JobActionsBar from '@/components/jobs/JobActionsBar.vue'
|
import JobActionsBar from '@/components/jobs/JobActionsBar.vue'
|
||||||
@ -130,7 +130,7 @@ export default {
|
|||||||
this.fetchAllJobs();
|
this.fetchAllJobs();
|
||||||
},
|
},
|
||||||
fetchAllJobs() {
|
fetchAllJobs() {
|
||||||
const jobsApi = new API.JobsApi(apiClient);
|
const jobsApi = new API.JobsApi(getAPIClient());
|
||||||
const jobsQuery = {};
|
const jobsQuery = {};
|
||||||
this.jobs.isJobless = false;
|
this.jobs.isJobless = false;
|
||||||
jobsApi.queryJobs(jobsQuery).then(this.onJobsFetched, function (error) {
|
jobsApi.queryJobs(jobsQuery).then(this.onJobsFetched, function (error) {
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
import { reactive, ref, watch } from 'vue'
|
import { reactive, ref, watch } from 'vue'
|
||||||
import { api } from '@/urls';
|
import { api } from '@/urls';
|
||||||
import { JobsApi, JobLastRenderedImageInfo, SocketIOLastRenderedUpdate } from '@/manager-api';
|
import { JobsApi, JobLastRenderedImageInfo, SocketIOLastRenderedUpdate } from '@/manager-api';
|
||||||
import { apiClient } from '@/stores/api-query-count';
|
import { getAPIClient } from "@/api-client";
|
||||||
|
|
||||||
const props = defineProps([
|
const props = defineProps([
|
||||||
/* The job UUID to show renders for, or some false-y value if renders from all
|
/* The job UUID to show renders for, or some false-y value if renders from all
|
||||||
@ -27,7 +27,7 @@ const cssClasses = reactive({
|
|||||||
'nothing-rendered-yet': true,
|
'nothing-rendered-yet': true,
|
||||||
})
|
})
|
||||||
|
|
||||||
const jobsApi = new JobsApi(apiClient);
|
const jobsApi = new JobsApi(getAPIClient());
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetches the last-rendered info for the given job, then updates the <img> tag for it.
|
* Fetches the last-rendered info for the given job, then updates the <img> tag for it.
|
||||||
|
@ -73,7 +73,7 @@
|
|||||||
import * as datetime from "@/datetime";
|
import * as datetime from "@/datetime";
|
||||||
import { JobsApi } from '@/manager-api';
|
import { JobsApi } from '@/manager-api';
|
||||||
import { backendURL } from '@/urls';
|
import { backendURL } from '@/urls';
|
||||||
import { apiClient } from '@/stores/api-query-count';
|
import { getAPIClient } from "@/api-client";
|
||||||
import { useNotifs } from "@/stores/notifications";
|
import { useNotifs } from "@/stores/notifications";
|
||||||
import LinkWorker from '@/components/LinkWorker.vue';
|
import LinkWorker from '@/components/LinkWorker.vue';
|
||||||
import { copyElementText } from '@/clipboard';
|
import { copyElementText } from '@/clipboard';
|
||||||
@ -90,7 +90,7 @@ export default {
|
|||||||
return {
|
return {
|
||||||
datetime: datetime, // So that the template can access it.
|
datetime: datetime, // So that the template can access it.
|
||||||
copyElementText: copyElementText,
|
copyElementText: copyElementText,
|
||||||
jobsApi: new JobsApi(apiClient),
|
jobsApi: new JobsApi(getAPIClient()),
|
||||||
notifs: useNotifs(),
|
notifs: useNotifs(),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
@ -17,7 +17,7 @@ import { TabulatorFull as Tabulator } from 'tabulator-tables';
|
|||||||
import * as datetime from "@/datetime";
|
import * as datetime from "@/datetime";
|
||||||
import * as API from '@/manager-api'
|
import * as API from '@/manager-api'
|
||||||
import { indicator } from '@/statusindicator';
|
import { indicator } from '@/statusindicator';
|
||||||
import { apiClient } from '@/stores/api-query-count';
|
import { getAPIClient } from "@/api-client";
|
||||||
import { useTasks } from '@/stores/tasks';
|
import { useTasks } from '@/stores/tasks';
|
||||||
|
|
||||||
import TaskActionsBar from '@/components/jobs/TaskActionsBar.vue'
|
import TaskActionsBar from '@/components/jobs/TaskActionsBar.vue'
|
||||||
@ -132,7 +132,7 @@ export default {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const jobsApi = new API.JobsApi(apiClient);
|
const jobsApi = new API.JobsApi(getAPIClient());
|
||||||
jobsApi.fetchJobTasks(this.jobID)
|
jobsApi.fetchJobTasks(this.jobID)
|
||||||
.then(this.onTasksFetched, function (error) {
|
.then(this.onTasksFetched, function (error) {
|
||||||
// TODO: error handling.
|
// TODO: error handling.
|
||||||
|
@ -6,11 +6,7 @@
|
|||||||
</option>
|
</option>
|
||||||
<option v-for="(action, key) in WORKER_ACTIONS" :value="key">{{ action.label }}</option>
|
<option v-for="(action, key) in WORKER_ACTIONS" :value="key">{{ action.label }}</option>
|
||||||
</select>
|
</select>
|
||||||
<button
|
<button :disabled="!canPerformAction" class="btn" @click.prevent="performWorkerAction">Apply</button>
|
||||||
:disabled="!canPerformAction"
|
|
||||||
class="btn"
|
|
||||||
@click.prevent="performWorkerAction"
|
|
||||||
>Apply</button>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
@ -18,7 +14,7 @@ import { computed, ref } from 'vue'
|
|||||||
import { useWorkers } from '@/stores/workers';
|
import { useWorkers } from '@/stores/workers';
|
||||||
import { useNotifs } from '@/stores/notifications';
|
import { useNotifs } from '@/stores/notifications';
|
||||||
import { WorkerMgtApi, WorkerStatusChangeRequest } from '@/manager-api';
|
import { WorkerMgtApi, WorkerStatusChangeRequest } from '@/manager-api';
|
||||||
import { apiClient } from '@/stores/api-query-count';
|
import { getAPIClient } from "@/api-client";
|
||||||
|
|
||||||
/* Freeze to prevent Vue.js from creating getters & setters all over this object.
|
/* Freeze to prevent Vue.js from creating getters & setters all over this object.
|
||||||
* We don't need it to be tracked, as it won't be changed anyway. */
|
* We don't need it to be tracked, as it won't be changed anyway. */
|
||||||
@ -73,7 +69,7 @@ function performWorkerAction() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const api = new WorkerMgtApi(apiClient);
|
const api = new WorkerMgtApi(getAPIClient());
|
||||||
const action = WORKER_ACTIONS[selectedAction.value];
|
const action = WORKER_ACTIONS[selectedAction.value];
|
||||||
const statuschange = new WorkerStatusChangeRequest(action.target_status, action.lazy);
|
const statuschange = new WorkerStatusChangeRequest(action.target_status, action.lazy);
|
||||||
console.log("Requesting worker status change", statuschange);
|
console.log("Requesting worker status change", statuschange);
|
||||||
|
@ -123,7 +123,7 @@ import { useNotifs } from '@/stores/notifications'
|
|||||||
|
|
||||||
import * as datetime from "@/datetime";
|
import * as datetime from "@/datetime";
|
||||||
import { WorkerMgtApi, WorkerSleepSchedule } from '@/manager-api';
|
import { WorkerMgtApi, WorkerSleepSchedule } from '@/manager-api';
|
||||||
import { apiClient } from '@/stores/api-query-count';
|
import { getAPIClient } from "@/api-client";
|
||||||
import { workerStatus } from "../../statusindicator";
|
import { workerStatus } from "../../statusindicator";
|
||||||
import LinkWorkerTask from '@/components/LinkWorkerTask.vue';
|
import LinkWorkerTask from '@/components/LinkWorkerTask.vue';
|
||||||
import SwitchCheckbox from '@/components/SwitchCheckbox.vue';
|
import SwitchCheckbox from '@/components/SwitchCheckbox.vue';
|
||||||
@ -140,7 +140,7 @@ export default {
|
|||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
datetime: datetime, // So that the template can access it.
|
datetime: datetime, // So that the template can access it.
|
||||||
api: new WorkerMgtApi(apiClient),
|
api: new WorkerMgtApi(getAPIClient()),
|
||||||
workerStatusHTML: "",
|
workerStatusHTML: "",
|
||||||
workerSleepSchedule: this.defaultWorkerSleepSchedule(),
|
workerSleepSchedule: this.defaultWorkerSleepSchedule(),
|
||||||
isScheduleEditing: false,
|
isScheduleEditing: false,
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
import { TabulatorFull as Tabulator } from 'tabulator-tables';
|
import { TabulatorFull as Tabulator } from 'tabulator-tables';
|
||||||
import { WorkerMgtApi } from '@/manager-api'
|
import { WorkerMgtApi } from '@/manager-api'
|
||||||
import { indicator, workerStatus } from '@/statusindicator';
|
import { indicator, workerStatus } from '@/statusindicator';
|
||||||
import { apiClient } from '@/stores/api-query-count';
|
import { getAPIClient } from "@/api-client";
|
||||||
import { useWorkers } from '@/stores/workers';
|
import { useWorkers } from '@/stores/workers';
|
||||||
|
|
||||||
import StatusFilterBar from '@/components/StatusFilterBar.vue'
|
import StatusFilterBar from '@/components/StatusFilterBar.vue'
|
||||||
@ -117,7 +117,7 @@ export default {
|
|||||||
this.fetchAllWorkers();
|
this.fetchAllWorkers();
|
||||||
},
|
},
|
||||||
fetchAllWorkers() {
|
fetchAllWorkers() {
|
||||||
const api = new WorkerMgtApi(apiClient);
|
const api = new WorkerMgtApi(getAPIClient());
|
||||||
api.fetchWorkers().then(this.onWorkersFetched, function (error) {
|
api.fetchWorkers().then(this.onWorkersFetched, function (error) {
|
||||||
// TODO: error handling.
|
// TODO: error handling.
|
||||||
console.error(error);
|
console.error(error);
|
||||||
|
@ -7,7 +7,8 @@ import SetupAssistant from '@/SetupAssistant.vue'
|
|||||||
import autoreload from '@/autoreloader'
|
import autoreload from '@/autoreloader'
|
||||||
import router from '@/router/index'
|
import router from '@/router/index'
|
||||||
import setupAssistantRouter from '@/router/setup-assistant'
|
import setupAssistantRouter from '@/router/setup-assistant'
|
||||||
import { ApiClient, MetaApi } from "@/manager-api";
|
import { MetaApi } from "@/manager-api";
|
||||||
|
import { newBareAPIClient } from "@/api-client";
|
||||||
import * as urls from '@/urls'
|
import * as urls from '@/urls'
|
||||||
|
|
||||||
// Ensure Tabulator can find `luxon`, which it needs for sorting by
|
// Ensure Tabulator can find `luxon`, which it needs for sorting by
|
||||||
@ -42,7 +43,7 @@ function setupAssistantMode() {
|
|||||||
/* This cannot use the client from '@/stores/api-query-count', as that would
|
/* This cannot use the client from '@/stores/api-query-count', as that would
|
||||||
* require Pinia, which is unavailable until the app is actually started. And to
|
* require Pinia, which is unavailable until the app is actually started. And to
|
||||||
* know which app to start, this API call needs to return data. */
|
* know which app to start, this API call needs to return data. */
|
||||||
const apiClient = new ApiClient(urls.api());;
|
const apiClient = newBareAPIClient();
|
||||||
const metaAPI = new MetaApi(apiClient);
|
const metaAPI = new MetaApi(apiClient);
|
||||||
metaAPI.getConfiguration()
|
metaAPI.getConfiguration()
|
||||||
.then((config) => {
|
.then((config) => {
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import { defineStore } from "pinia";
|
import { defineStore } from "pinia";
|
||||||
import { ApiClient } from "@/manager-api";
|
import { ApiClient } from "@/manager-api";
|
||||||
import * as urls from '@/urls'
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Keep track of running API queries.
|
* Keep track of running API queries.
|
||||||
@ -42,5 +41,3 @@ export class CountingApiClient extends ApiClient {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const apiClient = new CountingApiClient(urls.api());;
|
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
|
|
||||||
import * as API from '@/manager-api';
|
import * as API from '@/manager-api';
|
||||||
import { apiClient } from '@/stores/api-query-count';
|
import { getAPIClient } from "@/api-client";
|
||||||
|
|
||||||
|
|
||||||
const jobsAPI = new API.JobsApi(apiClient);
|
const jobsAPI = new API.JobsApi(getAPIClient());
|
||||||
|
|
||||||
// 'use' prefix is idiomatic for Pinia stores.
|
// 'use' prefix is idiomatic for Pinia stores.
|
||||||
// See https://pinia.vuejs.org/core-concepts/
|
// See https://pinia.vuejs.org/core-concepts/
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
|
|
||||||
import * as API from '@/manager-api';
|
import * as API from '@/manager-api';
|
||||||
import { apiClient } from '@/stores/api-query-count';
|
import { getAPIClient } from "@/api-client";
|
||||||
|
|
||||||
|
|
||||||
const jobsAPI = new API.JobsApi(apiClient);
|
const jobsAPI = new API.JobsApi(getAPIClient());
|
||||||
|
|
||||||
// 'use' prefix is idiomatic for Pinia stores.
|
// 'use' prefix is idiomatic for Pinia stores.
|
||||||
// See https://pinia.vuejs.org/core-concepts/
|
// See https://pinia.vuejs.org/core-concepts/
|
||||||
|
@ -39,7 +39,7 @@ import { useJobs } from '@/stores/jobs';
|
|||||||
import { useTasks } from '@/stores/tasks';
|
import { useTasks } from '@/stores/tasks';
|
||||||
import { useNotifs } from '@/stores/notifications'
|
import { useNotifs } from '@/stores/notifications'
|
||||||
import { useTaskLog } from '@/stores/tasklog'
|
import { useTaskLog } from '@/stores/tasklog'
|
||||||
import { apiClient } from '@/stores/api-query-count';
|
import { getAPIClient } from "@/api-client";
|
||||||
|
|
||||||
import FooterPopup from '@/components/footer/FooterPopup.vue'
|
import FooterPopup from '@/components/footer/FooterPopup.vue'
|
||||||
import GetTheAddon from '@/components/GetTheAddon.vue'
|
import GetTheAddon from '@/components/GetTheAddon.vue'
|
||||||
@ -127,7 +127,7 @@ export default {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const jobsAPI = new API.JobsApi(apiClient);
|
const jobsAPI = new API.JobsApi(getAPIClient());
|
||||||
jobsAPI.fetchTask(taskSummary.id)
|
jobsAPI.fetchTask(taskSummary.id)
|
||||||
.then((task) => {
|
.then((task) => {
|
||||||
this.tasks.setActiveTask(task);
|
this.tasks.setActiveTask(task);
|
||||||
@ -225,7 +225,7 @@ export default {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const jobsAPI = new API.JobsApi(apiClient);
|
const jobsAPI = new API.JobsApi(getAPIClient());
|
||||||
return jobsAPI.fetchJob(jobID)
|
return jobsAPI.fetchJob(jobID)
|
||||||
.then((job) => {
|
.then((job) => {
|
||||||
this.jobs.setActiveJob(job);
|
this.jobs.setActiveJob(job);
|
||||||
@ -254,7 +254,7 @@ export default {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const jobsAPI = new API.JobsApi(apiClient);
|
const jobsAPI = new API.JobsApi(getAPIClient());
|
||||||
return jobsAPI.fetchTask(taskID)
|
return jobsAPI.fetchTask(taskID)
|
||||||
.then((task) => {
|
.then((task) => {
|
||||||
this.tasks.setActiveTask(task);
|
this.tasks.setActiveTask(task);
|
||||||
|
@ -2,38 +2,28 @@
|
|||||||
<div class="setup-container">
|
<div class="setup-container">
|
||||||
<h1>Flamenco Setup Assistant</h1>
|
<h1>Flamenco Setup Assistant</h1>
|
||||||
<ul class="progress">
|
<ul class="progress">
|
||||||
<li
|
<li v-for="step in totalSetupSteps" :key="step" @click="jumpToStep(step)" :class="{
|
||||||
v-for="step in totalSetupSteps" :key="step"
|
|
||||||
@click="jumpToStep(step)"
|
|
||||||
:class="{
|
|
||||||
current: step == currentSetupStep,
|
current: step == currentSetupStep,
|
||||||
done: step < overallSetupStep,
|
done: step < overallSetupStep,
|
||||||
done_previously: (step < overallSetupStep && currentSetupStep > step),
|
done_previously: (step < overallSetupStep && currentSetupStep > step),
|
||||||
done_and_current: step == currentSetupStep && (step < overallSetupStep || step == 1),
|
done_and_current: step == currentSetupStep && (step < overallSetupStep || step == 1),
|
||||||
disabled: step > overallSetupStep,
|
disabled: step > overallSetupStep,
|
||||||
}"
|
}">
|
||||||
>
|
|
||||||
<span></span>
|
<span></span>
|
||||||
</li>
|
</li>
|
||||||
<div class="progress-bar"></div>
|
<div class="progress-bar"></div>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="setup-step step-welcome">
|
<div class="setup-step step-welcome">
|
||||||
|
|
||||||
<step-item
|
<step-item v-show="currentSetupStep == 1" @next-clicked="nextStep" :is-next-clickable="true"
|
||||||
v-show="currentSetupStep == 1"
|
:is-back-visible="false" title="Welcome!" next-label="Let's go">
|
||||||
@next-clicked="nextStep"
|
|
||||||
:is-next-clickable="true"
|
|
||||||
:is-back-visible="false"
|
|
||||||
title="Welcome!"
|
|
||||||
next-label="Let's go"
|
|
||||||
>
|
|
||||||
<p>
|
<p>
|
||||||
This setup assistant will guide you through the initial configuration of Flamenco. You will be up
|
This setup assistant will guide you through the initial configuration of Flamenco. You will be up
|
||||||
and running in a few minutes!
|
and running in a few minutes!
|
||||||
</p>
|
</p>
|
||||||
<p>Before we start, here is a quick overview of the Flamenco architecture.</p>
|
<p>Before we start, here is a quick overview of the Flamenco architecture.</p>
|
||||||
|
|
||||||
<img src="@/assets/architecture.png" alt="Flamenco architecture"/>
|
<img src="@/assets/architecture.png" alt="Flamenco architecture" />
|
||||||
|
|
||||||
<p>The illustration shows the key components and how they interact together:</p>
|
<p>The illustration shows the key components and how they interact together:</p>
|
||||||
<ul>
|
<ul>
|
||||||
@ -41,26 +31,24 @@
|
|||||||
<strong>Manager</strong>: This application. It coordinates all the activity.
|
<strong>Manager</strong>: This application. It coordinates all the activity.
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<strong>Worker</strong>: A workstation or dedicated rendering machine. It executes the tasks assigned by the Manager.
|
<strong>Worker</strong>: A workstation or dedicated rendering machine. It executes the tasks assigned by the
|
||||||
|
Manager.
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<strong>Shared Storage</strong>: A location accessible by the Manager and the Workers, where the files to be rendered should be saved.
|
<strong>Shared Storage</strong>: A location accessible by the Manager and the Workers, where the files to be
|
||||||
|
rendered should be saved.
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<strong>Blender Add-on</strong>: This is needed to connect to the Manager and submit a job from Blender.
|
<strong>Blender Add-on</strong>: This is needed to connect to the Manager and submit a job from Blender.
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>More information is available on the online documentation at
|
<p>More information is available on the online documentation at
|
||||||
<a href="https://flamenco.blender.org">flamenco.blender.org</a>.</p>
|
<a href="https://flamenco.blender.org">flamenco.blender.org</a>.
|
||||||
|
</p>
|
||||||
</step-item>
|
</step-item>
|
||||||
|
|
||||||
<step-item
|
<step-item v-show="currentSetupStep == 2" @next-clicked="nextStepAfterCheckSharedStoragePath"
|
||||||
v-show="currentSetupStep == 2"
|
@back-clicked="prevStep" :is-next-clickable="sharedStoragePath.length > 0" title="Shared Storage">
|
||||||
@next-clicked="nextStepAfterCheckSharedStoragePath"
|
|
||||||
@back-clicked="prevStep"
|
|
||||||
:is-next-clickable="sharedStoragePath.length > 0"
|
|
||||||
title="Shared Storage"
|
|
||||||
>
|
|
||||||
<p>Specify a path (or drive) where you want to store your Flamenco data.</p>
|
<p>Specify a path (or drive) where you want to store your Flamenco data.</p>
|
||||||
<p>
|
<p>
|
||||||
The location of the shared storage should be accessible by Flamenco Manager and by the Workers.
|
The location of the shared storage should be accessible by Flamenco Manager and by the Workers.
|
||||||
@ -79,17 +67,11 @@
|
|||||||
<a href="https://flamenco.blender.org/usage/shared-storage/">Learn more</a>.
|
<a href="https://flamenco.blender.org/usage/shared-storage/">Learn more</a>.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<input
|
<input v-model="sharedStoragePath" @keyup.enter="nextStepAfterCheckSharedStoragePath" type="text"
|
||||||
v-model="sharedStoragePath"
|
placeholder="Path to shared storage" :class="{
|
||||||
@keyup.enter="nextStepAfterCheckSharedStoragePath"
|
|
||||||
type="text"
|
|
||||||
placeholder="Path to shared storage"
|
|
||||||
:class="{
|
|
||||||
'is-invalid': (sharedStorageCheckResult != null) && !sharedStorageCheckResult.is_usable
|
'is-invalid': (sharedStorageCheckResult != null) && !sharedStorageCheckResult.is_usable
|
||||||
}"
|
}">
|
||||||
>
|
<p v-if="sharedStorageCheckResult != null" :class="{
|
||||||
<p v-if="sharedStorageCheckResult != null"
|
|
||||||
:class="{
|
|
||||||
'check-ok': sharedStorageCheckResult.is_usable,
|
'check-ok': sharedStorageCheckResult.is_usable,
|
||||||
'check-failed': !sharedStorageCheckResult.is_usable
|
'check-failed': !sharedStorageCheckResult.is_usable
|
||||||
}">
|
}">
|
||||||
@ -97,19 +79,14 @@
|
|||||||
</p>
|
</p>
|
||||||
</step-item>
|
</step-item>
|
||||||
|
|
||||||
<step-item
|
<step-item v-show="currentSetupStep == 3" @next-clicked="nextStepAfterCheckBlenderExePath" @back-clicked="prevStep"
|
||||||
v-show="currentSetupStep == 3"
|
:is-next-clickable="selectedBlender != null || customBlenderExe != (null || '')" title="Blender">
|
||||||
@next-clicked="nextStepAfterCheckBlenderExePath"
|
|
||||||
@back-clicked="prevStep"
|
|
||||||
:is-next-clickable="selectedBlender != null || customBlenderExe != (null || '')"
|
|
||||||
title="Blender"
|
|
||||||
>
|
|
||||||
|
|
||||||
<div v-if="isBlenderExeFinding" class="is-in-progress">Looking for Blender installs...</div>
|
<div v-if="isBlenderExeFinding" class="is-in-progress">Looking for Blender installs...</div>
|
||||||
|
|
||||||
<p v-if="autoFoundBlenders.length === 0">
|
<p v-if="autoFoundBlenders.length === 0">
|
||||||
Provide a path to a Blender executable accessible by all Workers.
|
Provide a path to a Blender executable accessible by all Workers.
|
||||||
<br/><br/>
|
<br /><br />
|
||||||
If your rendering setup features operating systems different from the one you are currently using,
|
If your rendering setup features operating systems different from the one you are currently using,
|
||||||
you can manually set up the other paths later.
|
you can manually set up the other paths later.
|
||||||
</p>
|
</p>
|
||||||
@ -119,23 +96,16 @@
|
|||||||
<fieldset v-if="autoFoundBlenders.length >= 1">
|
<fieldset v-if="autoFoundBlenders.length >= 1">
|
||||||
<label v-if="autoFoundBlenderPathEnvvar" for="blender-path_envvar">
|
<label v-if="autoFoundBlenderPathEnvvar" for="blender-path_envvar">
|
||||||
<div>
|
<div>
|
||||||
<input
|
<input v-model="selectedBlender" :value="autoFoundBlenderPathEnvvar" id="blender-path_envvar" name="blender"
|
||||||
v-model="selectedBlender"
|
|
||||||
:value="autoFoundBlenderPathEnvvar"
|
|
||||||
id="blender-path_envvar"
|
|
||||||
name="blender"
|
|
||||||
type="radio">
|
type="radio">
|
||||||
{{ sourceLabels[autoFoundBlenderPathEnvvar.source] }}
|
{{ sourceLabels[autoFoundBlenderPathEnvvar.source] }}
|
||||||
</div>
|
</div>
|
||||||
<div class="setup-path-command">
|
<div class="setup-path-command">
|
||||||
<span class="path">
|
<span class="path">
|
||||||
{{autoFoundBlenderPathEnvvar.path}}
|
{{ autoFoundBlenderPathEnvvar.path }}
|
||||||
</span>
|
</span>
|
||||||
<span
|
<span aria-label="Console output when running with --version" class="command-preview"
|
||||||
aria-label="Console output when running with --version"
|
data-microtip-position="top" role="tooltip">
|
||||||
class="command-preview"
|
|
||||||
data-microtip-position="top"
|
|
||||||
role="tooltip">
|
|
||||||
{{ autoFoundBlenderPathEnvvar.cause }}
|
{{ autoFoundBlenderPathEnvvar.cause }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@ -143,23 +113,16 @@
|
|||||||
|
|
||||||
<label v-if="autoFoundBlenderFileAssociation" for="blender-file_association">
|
<label v-if="autoFoundBlenderFileAssociation" for="blender-file_association">
|
||||||
<div>
|
<div>
|
||||||
<input
|
<input v-model="selectedBlender" :value="autoFoundBlenderFileAssociation" id="blender-file_association"
|
||||||
v-model="selectedBlender"
|
name="blender" type="radio">
|
||||||
:value="autoFoundBlenderFileAssociation"
|
|
||||||
id="blender-file_association"
|
|
||||||
name="blender"
|
|
||||||
type="radio">
|
|
||||||
{{ sourceLabels[autoFoundBlenderFileAssociation.source] }}
|
{{ sourceLabels[autoFoundBlenderFileAssociation.source] }}
|
||||||
</div>
|
</div>
|
||||||
<div class="setup-path-command">
|
<div class="setup-path-command">
|
||||||
<span class="path">
|
<span class="path">
|
||||||
{{autoFoundBlenderFileAssociation.path}}
|
{{ autoFoundBlenderFileAssociation.path }}
|
||||||
</span>
|
</span>
|
||||||
<span
|
<span aria-label="Console output when running with --version" class="command-preview"
|
||||||
aria-label="Console output when running with --version"
|
data-microtip-position="top" role="tooltip">
|
||||||
class="command-preview"
|
|
||||||
data-microtip-position="top"
|
|
||||||
role="tooltip">
|
|
||||||
{{ autoFoundBlenderFileAssociation.cause }}
|
{{ autoFoundBlenderFileAssociation.cause }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@ -167,24 +130,15 @@
|
|||||||
|
|
||||||
<label for="blender-input_path">
|
<label for="blender-input_path">
|
||||||
<div>
|
<div>
|
||||||
<input
|
<input type="radio" v-model="selectedBlender" name="blender" :value="blenderFromInputPath"
|
||||||
type="radio"
|
id="blender-input_path">
|
||||||
v-model="selectedBlender"
|
|
||||||
name="blender"
|
|
||||||
:value="blenderFromInputPath"
|
|
||||||
id="blender-input_path"
|
|
||||||
>
|
|
||||||
{{ sourceLabels['input_path'] }}
|
{{ sourceLabels['input_path'] }}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<input
|
<input v-model="customBlenderExe" @keyup.enter="nextStepAfterCheckBlenderExePath"
|
||||||
v-model="customBlenderExe"
|
|
||||||
@keyup.enter="nextStepAfterCheckBlenderExePath"
|
|
||||||
@focus="selectedBlender = null"
|
@focus="selectedBlender = null"
|
||||||
:class="{'is-invalid': blenderExeCheckResult != null && !blenderExeCheckResult.is_usable}"
|
:class="{ 'is-invalid': blenderExeCheckResult != null && !blenderExeCheckResult.is_usable }" type="text"
|
||||||
type="text"
|
placeholder="Path to Blender">
|
||||||
placeholder="Path to Blender"
|
|
||||||
>
|
|
||||||
<p v-if="isBlenderExeChecking" class="is-in-progress">Checking...</p>
|
<p v-if="isBlenderExeChecking" class="is-in-progress">Checking...</p>
|
||||||
<p v-if="blenderExeCheckResult != null && !blenderExeCheckResult.is_usable" class="check-failed">
|
<p v-if="blenderExeCheckResult != null && !blenderExeCheckResult.is_usable" class="check-failed">
|
||||||
{{ blenderExeCheckResult.cause }}</p>
|
{{ blenderExeCheckResult.cause }}</p>
|
||||||
@ -193,13 +147,9 @@
|
|||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
<div v-if="autoFoundBlenders.length === 0">
|
<div v-if="autoFoundBlenders.length === 0">
|
||||||
<input
|
<input v-model="customBlenderExe" @keyup.enter="nextStepAfterCheckBlenderExePath"
|
||||||
v-model="customBlenderExe"
|
:class="{ 'is-invalid': blenderExeCheckResult != null && !blenderExeCheckResult.is_usable }" type="text"
|
||||||
@keyup.enter="nextStepAfterCheckBlenderExePath"
|
placeholder="Path to Blender executable">
|
||||||
:class="{'is-invalid': blenderExeCheckResult != null && !blenderExeCheckResult.is_usable}"
|
|
||||||
type="text"
|
|
||||||
placeholder="Path to Blender executable"
|
|
||||||
>
|
|
||||||
|
|
||||||
<p v-if="isBlenderExeChecking" class="is-in-progress">Checking...</p>
|
<p v-if="isBlenderExeChecking" class="is-in-progress">Checking...</p>
|
||||||
<p v-if="blenderExeCheckResult != null && !blenderExeCheckResult.is_usable" class="check-failed">
|
<p v-if="blenderExeCheckResult != null && !blenderExeCheckResult.is_usable" class="check-failed">
|
||||||
@ -207,14 +157,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</step-item>
|
</step-item>
|
||||||
|
|
||||||
<step-item
|
<step-item v-show="currentSetupStep == 4" @next-clicked="confirmSetupAssistant" @back-clicked="prevStep"
|
||||||
v-show="currentSetupStep == 4"
|
next-label="Confirm" title="Review" :is-next-clickable="setupConfirmIsClickable">
|
||||||
@next-clicked="confirmSetupAssistant"
|
|
||||||
@back-clicked="prevStep"
|
|
||||||
next-label="Confirm"
|
|
||||||
title="Review"
|
|
||||||
:is-next-clickable="setupConfirmIsClickable"
|
|
||||||
>
|
|
||||||
<div v-if="isConfigComplete">
|
<div v-if="isConfigComplete">
|
||||||
<p>This is the configuration that will be used by Flamenco:</p>
|
<p>This is the configuration that will be used by Flamenco:</p>
|
||||||
<dl>
|
<dl>
|
||||||
@ -253,7 +197,7 @@ import NotificationBar from '@/components/footer/NotificationBar.vue'
|
|||||||
import UpdateListener from '@/components/UpdateListener.vue'
|
import UpdateListener from '@/components/UpdateListener.vue'
|
||||||
import StepItem from '@/components/steps/StepItem.vue';
|
import StepItem from '@/components/steps/StepItem.vue';
|
||||||
import { MetaApi, PathCheckInput, SetupAssistantConfig } from "@/manager-api";
|
import { MetaApi, PathCheckInput, SetupAssistantConfig } from "@/manager-api";
|
||||||
import { apiClient } from '@/stores/api-query-count';
|
import { getAPIClient } from "@/api-client";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'SetupAssistantView',
|
name: 'SetupAssistantView',
|
||||||
@ -265,7 +209,7 @@ export default {
|
|||||||
data: () => ({
|
data: () => ({
|
||||||
sharedStoragePath: "",
|
sharedStoragePath: "",
|
||||||
sharedStorageCheckResult: null, // api.PathCheckResult
|
sharedStorageCheckResult: null, // api.PathCheckResult
|
||||||
metaAPI: new MetaApi(apiClient),
|
metaAPI: new MetaApi(getAPIClient()),
|
||||||
|
|
||||||
allBlenders: [], // combination of autoFoundBlenders and blenderExeCheckResult.
|
allBlenders: [], // combination of autoFoundBlenders and blenderExeCheckResult.
|
||||||
|
|
||||||
@ -458,7 +402,6 @@ export default {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<style>
|
<style>
|
||||||
|
|
||||||
.step-welcome ul {
|
.step-welcome ul {
|
||||||
padding-left: var(--spacer-xl);
|
padding-left: var(--spacer-xl);
|
||||||
margin-bottom: var(--spacer-xl);
|
margin-bottom: var(--spacer-xl);
|
||||||
@ -517,7 +460,8 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.progress-bar {
|
.progress-bar {
|
||||||
--width-each-segment: calc(100% / calc(v-bind('totalSetupSteps') - 1)); /* Substract 1 because the first step has no progress. */
|
--width-each-segment: calc(100% / calc(v-bind('totalSetupSteps') - 1));
|
||||||
|
/* Substract 1 because the first step has no progress. */
|
||||||
--progress-bar-width-at-current-step: calc(var(--width-each-segment) * calc(v-bind('currentSetupStep') - 1));
|
--progress-bar-width-at-current-step: calc(var(--width-each-segment) * calc(v-bind('currentSetupStep') - 1));
|
||||||
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
@ -7,8 +7,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<footer class="app-footer">
|
<footer class="app-footer">
|
||||||
<notification-bar />
|
<notification-bar />
|
||||||
<update-listener ref="updateListener" mainSubscription="allWorkers"
|
<update-listener ref="updateListener" mainSubscription="allWorkers" @workerUpdate="onSIOWorkerUpdate"
|
||||||
@workerUpdate="onSIOWorkerUpdate"
|
|
||||||
@sioReconnected="onSIOReconnected" @sioDisconnected="onSIODisconnected" />
|
@sioReconnected="onSIOReconnected" @sioDisconnected="onSIODisconnected" />
|
||||||
</footer>
|
</footer>
|
||||||
</template>
|
</template>
|
||||||
@ -17,6 +16,7 @@
|
|||||||
.col-workers-list {
|
.col-workers-list {
|
||||||
grid-area: col-1;
|
grid-area: col-1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.col-workers-2 {
|
.col-workers-2 {
|
||||||
grid-area: col-2;
|
grid-area: col-2;
|
||||||
}
|
}
|
||||||
@ -26,7 +26,7 @@
|
|||||||
import { WorkerMgtApi } from '@/manager-api';
|
import { WorkerMgtApi } from '@/manager-api';
|
||||||
import { useNotifs } from '@/stores/notifications'
|
import { useNotifs } from '@/stores/notifications'
|
||||||
import { useWorkers } from '@/stores/workers';
|
import { useWorkers } from '@/stores/workers';
|
||||||
import { apiClient } from '@/stores/api-query-count';
|
import { getAPIClient } from "@/api-client";
|
||||||
|
|
||||||
import NotificationBar from '@/components/footer/NotificationBar.vue'
|
import NotificationBar from '@/components/footer/NotificationBar.vue'
|
||||||
import UpdateListener from '@/components/UpdateListener.vue'
|
import UpdateListener from '@/components/UpdateListener.vue'
|
||||||
@ -45,7 +45,7 @@ export default {
|
|||||||
data: () => ({
|
data: () => ({
|
||||||
workers: useWorkers(),
|
workers: useWorkers(),
|
||||||
notifs: useNotifs(),
|
notifs: useNotifs(),
|
||||||
api: new WorkerMgtApi(apiClient),
|
api: new WorkerMgtApi(getAPIClient()),
|
||||||
}),
|
}),
|
||||||
mounted() {
|
mounted() {
|
||||||
window.workersView = this;
|
window.workersView = this;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user