Web: show a "get the addon" call to action if there are no jobs
If there are no jobs in the database yet, show a "get the addon" call to action. This includes the current API URL, which can be copied by clicking on it. There is no feedback yet that the copy took place, though.
This commit is contained in:
parent
2185d8b5ee
commit
72b994db7d
@ -644,3 +644,7 @@ span.state-transition-arrow.lazy {
|
||||
max-height: 100%;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.click-to-copy {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
21
web/app/src/clipboard.js
Normal file
21
web/app/src/clipboard.js
Normal file
@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Copy the inner text of an element to the clipboard.
|
||||
*
|
||||
* @param {Event } clickEvent the click event that triggered this function call.
|
||||
*/
|
||||
export function copyElementText(clickEvent) {
|
||||
const sourceElement = clickEvent.target;
|
||||
const inputElement = document.createElement("input");
|
||||
document.body.appendChild(inputElement);
|
||||
inputElement.setAttribute("value", sourceElement.innerText);
|
||||
inputElement.select();
|
||||
|
||||
// Note that the `navigator.clipboard` interface is only available when using
|
||||
// a secure (HTTPS) connection, which Flamenco Manager will likely not have.
|
||||
// This is why this code falls back to the deprecated `document.execCommand()`
|
||||
// call.
|
||||
// Source: https://developer.mozilla.org/en-US/docs/Web/API/Clipboard
|
||||
document.execCommand("copy");
|
||||
|
||||
document.body.removeChild(inputElement);
|
||||
}
|
28
web/app/src/components/GetTheAddon.vue
Normal file
28
web/app/src/components/GetTheAddon.vue
Normal file
@ -0,0 +1,28 @@
|
||||
<template>
|
||||
<div class="details-no-item-selected">
|
||||
<div class="get-the-addon">
|
||||
<p>Get the Blender add-on and submit a job.</p>
|
||||
<p><button class="btn btn-primary">Get the add-on!</button></p>
|
||||
<p>Use the URL below in the add-on preferences. Click on it to copy.</p>
|
||||
<p class="click-to-copy" title="Click to copy this URL" @click="copyElementText">{{ api() }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { api } from '@/urls';
|
||||
import { copyElementText } from '@/clipboard.js';
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.get-the-addon {
|
||||
color: var(--color-text-muted);
|
||||
display: block;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
button {
|
||||
margin: var(--spacer);
|
||||
font-size: var(--font-size-lg);
|
||||
}
|
||||
</style>
|
@ -18,6 +18,7 @@ import * as datetime from "@/datetime";
|
||||
import * as API from '@/manager-api'
|
||||
import { indicator } from '@/statusindicator';
|
||||
import { apiClient } from '@/stores/api-query-count';
|
||||
import { useJobs } from '@/stores/jobs';
|
||||
|
||||
import JobActionsBar from '@/components/jobs/JobActionsBar.vue'
|
||||
import StatusFilterBar from '@/components/StatusFilterBar.vue'
|
||||
@ -33,6 +34,8 @@ export default {
|
||||
return {
|
||||
shownStatuses: [],
|
||||
availableStatuses: [], // Will be filled after data is loaded from the backend.
|
||||
|
||||
jobs: useJobs(),
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
@ -127,6 +130,7 @@ export default {
|
||||
fetchAllJobs() {
|
||||
const jobsApi = new API.JobsApi(apiClient);
|
||||
const jobsQuery = {};
|
||||
this.jobs.isJobless = false;
|
||||
jobsApi.queryJobs(jobsQuery).then(this.onJobsFetched, function (error) {
|
||||
// TODO: error handling.
|
||||
console.error(error);
|
||||
@ -135,6 +139,8 @@ export default {
|
||||
onJobsFetched(data) {
|
||||
// "Down-cast" to JobUpdate to only get those fields, just for debugging things:
|
||||
// data.jobs = data.jobs.map((j) => API.JobUpdate.constructFromObject(j));
|
||||
const hasJobs = data && data.jobs && data.jobs.length > 0;
|
||||
this.jobs.isJobless = !hasJobs;
|
||||
this.tabulator.setData(data.jobs);
|
||||
this._refreshAvailableStatuses();
|
||||
|
||||
|
@ -17,6 +17,12 @@ export const useJobs = defineStore('jobs', {
|
||||
* @type {string}
|
||||
*/
|
||||
activeJobID: "",
|
||||
|
||||
/**
|
||||
* Set to true when it is known that there are no jobs at all in the system.
|
||||
* This is written by the JobsTable.vue component.
|
||||
*/
|
||||
isJobless: false,
|
||||
}),
|
||||
getters: {
|
||||
canDelete() {
|
||||
|
@ -3,8 +3,12 @@
|
||||
<jobs-table ref="jobsTable" :activeJobID="jobID" @tableRowClicked="onTableJobClicked" />
|
||||
</div>
|
||||
<div class="col col-2 job-details-column" id="col-job-details">
|
||||
<get-the-addon v-if="jobs.isJobless" />
|
||||
<template v-else>
|
||||
<job-details ref="jobDetails" :jobData="jobs.activeJob" @reshuffled="_recalcTasksTableHeight" />
|
||||
<tasks-table v-if="hasJobData" ref="tasksTable" :jobID="jobID" :taskID="taskID" @tableRowClicked="onTableTaskClicked" />
|
||||
<tasks-table v-if="hasJobData" ref="tasksTable" :jobID="jobID" :taskID="taskID"
|
||||
@tableRowClicked="onTableTaskClicked" />
|
||||
</template>
|
||||
</div>
|
||||
<div class="col col-3">
|
||||
<task-details v-if="hasJobData" :taskData="tasks.activeTask" @showTaskLogTail="showTaskLogTail" />
|
||||
@ -37,6 +41,7 @@ import { useTaskLog } from '@/stores/tasklog'
|
||||
import { apiClient } from '@/stores/api-query-count';
|
||||
|
||||
import FooterPopup from '@/components/footer/FooterPopup.vue'
|
||||
import GetTheAddon from '@/components/GetTheAddon.vue'
|
||||
import JobDetails from '@/components/jobs/JobDetails.vue'
|
||||
import JobsTable from '@/components/jobs/JobsTable.vue'
|
||||
import NotificationBar from '@/components/footer/NotificationBar.vue'
|
||||
@ -49,6 +54,7 @@ export default {
|
||||
props: ["jobID", "taskID"], // provided by Vue Router.
|
||||
components: {
|
||||
FooterPopup,
|
||||
GetTheAddon,
|
||||
JobDetails,
|
||||
JobsTable,
|
||||
NotificationBar,
|
||||
|
Loading…
x
Reference in New Issue
Block a user