Skip to content

Commit

Permalink
Pages Editor: UI tweaks, improved Workflow Settings (zooniverse#7093)
Browse files Browse the repository at this point in the history
* pages-editor-pt22: add optional ?tab=settings URL override

* WorkflowSettings: add Image Display Options

* WorkflowSettings: add option to flip image colo(u)r

* WorkflowSettingsPage: more options for mutli-image options. 'Reset' experimental tool now resets workflow.configuration as well.

* WorkflowSettingsPage: add playIterations option

* Style: add rounded borders to every select element

* WorkflowSettingsPage: add ?showRemovedOptions=true to show removed options

* WorkflowSettingsPage: add option for separate frames view

* WorkflowSettingsPage: restyle smaller info text

* WorkflowSettingsPage: move Workflow ID

* Add WorkflowVersion

* DataManager: UPDATE MEMO WHEN STATUS CHANGES

* WorkflowVersion: add loading icon

* TasksPage: add 'no tasks' notice

* WorkflowSettingsPage: show Classifications Count. Restyle disabled elements.

* TasksPage: introduce UI for linear workflows

* TasksPage: adding/moving/deleting pages triggers linear workflow rules

* TasksPage, WorkflowSettingsPage: introduce Advanced Mode

* Feedback: increase fontsize of span.small_info

* Feedback: if showing separate viewers, 'col' layout is default
  • Loading branch information
shaunanoordin authored Jun 4, 2024
1 parent 614ec1b commit 1ca8532
Show file tree
Hide file tree
Showing 11 changed files with 405 additions and 101 deletions.
3 changes: 2 additions & 1 deletion app/pages/lab-pages-editor/DataManager.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -110,10 +110,11 @@ function DataManager({

return {
project: apiData.project,
status: apiData.status,
workflow: apiData.workflow,
update
};
}, [apiData.project, apiData.workflow, updateCounter]);
}, [apiData.project, apiData.workflow, apiData.status, updateCounter]);

if (!workflowId) return (<div>ERROR: no Workflow ID specified</div>);
// if (!workflow) return null
Expand Down
10 changes: 9 additions & 1 deletion app/pages/lab-pages-editor/PagesEditor.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,17 @@ import TasksPage from './components/TasksPage';
import WorkflowSettingsPage from './components/WorkflowSettingsPage';
import strings from './strings.json';

function getDefaultTab() { // Use ?tab=1 or tab='settings' to link directly to Workflow Settings
const params = new URLSearchParams(window?.location?.search);
const tab = params.get('tab');

if ([1, '1', 'settings', 'workflowsettings'].includes(tab)) return 1;
return 0;
}

function PagesEditor({ params }) {
const { workflowID: workflowId, projectID: projectId } = params;
const [currentTab, setCurrentTab] = useState(0);
const [currentTab, setCurrentTab] = useState(getDefaultTab()); // Default tab is 0
const tabs = [
{
id: 'pages-editor_workflow-header-tab-button_task',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export default function ExperimentalPanel({
}) {
function experimentalReset() {
update({
configuration: {},
first_task: '',
tasks: {},
steps: []
Expand Down
54 changes: 47 additions & 7 deletions app/pages/lab-pages-editor/components/TasksPage/TasksPage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import createStep from '../../helpers/createStep.js';
import createTask from '../../helpers/createTask.js';
import getNewStepKey from '../../helpers/getNewStepKey.js';
import getNewTaskKey from '../../helpers/getNewTaskKey.js';
import linkStepsInWorkflow from '../../helpers/linkStepsInWorkflow.js';
import moveItemInArray from '../../helpers/moveItemInArray.js';
import cleanupTasksAndSteps from '../../helpers/cleanupTasksAndSteps.js';
// import strings from '../../strings.json'; // TODO: move all text into strings
Expand All @@ -13,6 +14,17 @@ import ExperimentalPanel from './ExperimentalPanel.jsx';
import EditStepDialog from './components/EditStepDialog';
import NewTaskDialog from './components/NewTaskDialog.jsx';
import StepItem from './components/StepItem';
import WorkflowVersion from '../WorkflowVersion.jsx';
import WrenchIcon from '../../icons/WrenchIcon.jsx';

// Use ?advanced=true to enable advanced mode.
// - switches from simpler "linear workflow" to "manual workflow".
// - enables Experimental Panel.
// - shows hidden options in workflow settings.
function getAdvancedMode() {
const params = new URLSearchParams(window?.location?.search);
return !!params.get('advanced');
}

export default function TasksPage() {
const { workflow, update } = useWorkflowContext();
Expand All @@ -22,6 +34,13 @@ export default function TasksPage() {
const [ activeDragItem, setActiveDragItem ] = useState(-1); // Keeps track of active item being dragged (StepItem). This is because "dragOver" CAN'T read the data from dragEnter.dataTransfer.getData().
const firstStepKey = workflow?.steps?.[0]?.[0] || '';
const isActive = true; // TODO
const advancedMode = getAdvancedMode();

// A linear workflow means every step (except branching steps) will move into
// the next step in the workflow.steps array. e.g. step0.next = step1
// A manual (i.e. non-linear) workflow asks the user to explicity spell out
// the next step of each step.
const isLinearWorkflow = !advancedMode;

/*
Adds a new Task of a specified type (with default settings) to a Step.
Expand All @@ -32,7 +51,7 @@ export default function TasksPage() {
if (!workflow) return;
const newTaskKey = getNewTaskKey(workflow.tasks);
const newTask = createTask(taskType);
const steps = workflow.steps?.slice() || [];
let steps = workflow.steps?.slice() || [];

let step
if (stepIndex < 0) {
Expand Down Expand Up @@ -62,6 +81,10 @@ export default function TasksPage() {
[newTaskKey]: newTask
};

if (linkStepsInWorkflow) {
steps = linkStepsInWorkflow(steps, tasks);
}

await update({ tasks, steps });
return (stepIndex < 0) ? steps.length - 1 : stepIndex;
}
Expand Down Expand Up @@ -117,7 +140,10 @@ export default function TasksPage() {
const oldSteps = workflow.steps || [];
if (from < 0 || to < 0 || from >= oldSteps.length || to >= oldSteps.length) return;

const steps = moveItemInArray(oldSteps, from, to);
let steps = moveItemInArray(oldSteps, from, to);
if (linkStepsInWorkflow) {
steps = linkStepsInWorkflow(steps, workflow.tasks);
}
update({ steps });
}

Expand All @@ -134,6 +160,9 @@ export default function TasksPage() {

// cleanedupTasksAndSteps() will also remove tasks not associated with any step.
const cleanedTasksAndSteps = cleanupTasksAndSteps(newTasks, newSteps);
if (linkStepsInWorkflow) {
cleanedTasksAndSteps.steps = linkStepsInWorkflow(cleanedTasksAndSteps.steps, cleanedTasksAndSteps.tasks);
}
update(cleanedTasksAndSteps);
}

Expand Down Expand Up @@ -214,11 +243,13 @@ export default function TasksPage() {
<div className="tasks-page">
<div className="workflow-title flex-row">
<h2 className="flex-item">{workflow.display_name}</h2>
<span className="workflow-id">{`#${workflow.id}`}</span>
{(isActive) ? <span className="status-active">Active</span> : <span className="status-inactive">Inactive</span>}
</div>
<section aria-labelledby="workflow-tasks-heading">
<h3 id="workflow-tasks-heading">Tasks</h3>
<div className="flex-row">
<h3 id="workflow-tasks-heading" className="flex-item">Tasks</h3>
<WorkflowVersion />
</div>
<div className="flex-row">
<button
className="flex-item big primary decoration-plus"
Expand Down Expand Up @@ -246,6 +277,12 @@ export default function TasksPage() {
))}
</select>
</div>
{!(workflow.steps?.length > 0) && (
<div className="no-tasks-notice">
<WrenchIcon />
<p>Start by adding tasks to build your Task Funnel here.</p>
</div>
)}
<ul className="steps-list" aria-label="Pages/Steps">
{workflow.steps?.map((step, index) => (
<StepItem
Expand All @@ -255,6 +292,7 @@ export default function TasksPage() {
allTasks={workflow.tasks}
deleteStep={deleteStep}
moveStep={moveStep}
isLinearWorkflow={isLinearWorkflow}
openEditStepDialog={openEditStepDialog}
setActiveDragItem={setActiveDragItem}
step={step}
Expand Down Expand Up @@ -285,9 +323,11 @@ export default function TasksPage() {
/>

{/* EXPERIMENTAL */}
<ExperimentalPanel
update={update}
/>
{advancedMode && (
<ExperimentalPanel
update={update}
/>
)}
</section>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,48 +5,63 @@ const DEFAULT_HANDLER = () => {};

export default function SimpleNextControls({
allSteps = [],
isLastItem = false,
isLinearWorkflow = false,
step,
updateNextStepForStep = DEFAULT_HANDLER
}) {
if (!step) return null;
const [ stepKey, stepBody ] = step;

const isLinearItem = isLinearWorkflow && !isLastItem;
const showFakeSubmit = isLinearWorkflow && isLastItem;
const showNextPageDropdown = !isLinearWorkflow && !showFakeSubmit;

function onChange(e) {
const next = e.target?.value;
updateNextStepForStep(stepKey, next);
}

return (
<div className="next-controls vertical-layout">
<NextStepArrow className="next-arrow" height="10" />
<select
className={(!stepBody?.next) ? 'next-is-submit' : ''}
onChange={onChange}
value={stepBody?.next || ''}
>
<option
value={''}
<NextStepArrow
arrowhead={isLinearItem}
className="next-arrow"
height={isLinearItem ? 24 : 10}
/>
{showNextPageDropdown && (<select
className={(!stepBody?.next) ? 'next-is-submit' : ''}
onChange={onChange}
value={stepBody?.next || ''}
>
Submit
</option>
{allSteps.map(([otherStepKey, otherStepBody]) => {
const taskKeys = otherStepBody?.taskKeys?.toString() || '(none)';
return (
<option
key={`simple-next-controls-option-${otherStepKey}`}
value={otherStepKey}
>
{taskKeys}
</option>
);
})}
</select>
<option
value={''}
>
Submit
</option>
{allSteps.map(([otherStepKey, otherStepBody]) => {
const taskKeys = otherStepBody?.taskKeys?.toString() || '(none)';
return (
<option
key={`simple-next-controls-option-${otherStepKey}`}
value={otherStepKey}
>
{taskKeys}
</option>
);
})}
</select>
)}
{showFakeSubmit && (
<div className="fake-submit">Submit</div>
)}
</div>
);
}

SimpleNextControls.propTypes = {
allSteps: PropTypes.arrayOf(PropTypes.array),
isLastItem: PropTypes.bool,
isLinearWorkflow: PropTypes.bool,
step: PropTypes.array,
updateNextStepForStep: PropTypes.func
};
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ function StepItem({
deleteStep = DEFAULT_HANDLER,
moveStep = DEFAULT_HANDLER,
openEditStepDialog = DEFAULT_HANDLER,
isLinearWorkflow = false,
setActiveDragItem = DEFAULT_HANDLER,
step,
stepIndex,
Expand All @@ -33,6 +34,7 @@ function StepItem({
const [stepKey, stepBody] = step || [];
if (!stepKey || !stepBody || !allSteps || !allTasks) return <li className="step-item">ERROR: could not render Step</li>;

const isLastItem = stepIndex === allSteps.length - 1;
const taskKeys = stepBody.taskKeys || [];

function doDelete() {
Expand Down Expand Up @@ -142,6 +144,8 @@ function StepItem({
{!branchingTaskKey && (
<SimpleNextControls
allSteps={allSteps}
isLastItem={isLastItem}
isLinearWorkflow={isLinearWorkflow}
step={step}
updateNextStepForStep={updateNextStepForStep}
/>
Expand All @@ -161,6 +165,7 @@ StepItem.propTypes = {
allSteps: PropTypes.array,
allTasks: PropTypes.object,
deleteStep: PropTypes.func,
isLinearWorkflow: PropTypes.bool,
moveStep: PropTypes.func,
openEditStepDialog: PropTypes.func,
setActiveDragItem: PropTypes.func,
Expand Down
Loading

0 comments on commit 1ca8532

Please sign in to comment.