Add Process Journey Timeline Component
- Introduced a new component, ProcessJourneyTimeline.vue, to visualize the journey of processes with interactive nodes and metrics. - Implemented props for journey data, display options for metrics, issues, and branches, and support for interactive node selection. - Enhanced user experience with dynamic styling based on node types and dropoff rates, providing visual feedback on process performance. - Added helper functions for formatting durations, calculating completion rates, and managing node interactions, ensuring a comprehensive overview of process journeys. - Updated styles for improved layout and responsiveness, aligning with existing design principles in the application.
This commit is contained in:
parent
35a0bd412e
commit
ba08d2e466
@ -1224,7 +1224,6 @@ function fromObject(flowObject) {
|
||||
/* Node styles from ProcessFlowNodes.js are imported globally in a plugin */
|
||||
.process-flow-container {
|
||||
width: 100%;
|
||||
height: calc(100vh - 190px); /* Adjust based on new header/footer height */
|
||||
min-height: 500px;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 0;
|
||||
|
@ -121,11 +121,8 @@ const checkScreenSize = () => {
|
||||
showRightPanel.value = true;
|
||||
}
|
||||
} else {
|
||||
// Desktop: show both panels by default
|
||||
if (!showLeftPanel.value && !showRightPanel.value) {
|
||||
showLeftPanel.value = true;
|
||||
showRightPanel.value = true;
|
||||
}
|
||||
// Desktop: show both panels by default only on first load
|
||||
// Don't override user preferences on screen size changes
|
||||
}
|
||||
};
|
||||
|
||||
@ -148,6 +145,13 @@ const toggleRightPanel = () => {
|
||||
}
|
||||
};
|
||||
|
||||
// Toggle both panels at once
|
||||
const toggleBothPanels = () => {
|
||||
const bothVisible = showLeftPanel.value && showRightPanel.value;
|
||||
showLeftPanel.value = !bothVisible;
|
||||
showRightPanel.value = !bothVisible;
|
||||
};
|
||||
|
||||
// Close panels when clicking on canvas (mobile only)
|
||||
const onPaneClickMobile = () => {
|
||||
selectedNode.value = null;
|
||||
@ -162,6 +166,32 @@ const onPaneClickMobile = () => {
|
||||
}
|
||||
};
|
||||
|
||||
// Keyboard shortcuts for panel toggles
|
||||
const handleKeyboardShortcuts = (event) => {
|
||||
// Only handle shortcuts when no input is focused
|
||||
if (event.target.tagName === 'INPUT' || event.target.tagName === 'TEXTAREA') {
|
||||
return;
|
||||
}
|
||||
|
||||
// Ctrl/Cmd + 1: Toggle left panel
|
||||
if ((event.ctrlKey || event.metaKey) && event.key === '1') {
|
||||
event.preventDefault();
|
||||
toggleLeftPanel();
|
||||
}
|
||||
|
||||
// Ctrl/Cmd + 2: Toggle right panel
|
||||
if ((event.ctrlKey || event.metaKey) && event.key === '2') {
|
||||
event.preventDefault();
|
||||
toggleRightPanel();
|
||||
}
|
||||
|
||||
// Ctrl/Cmd + 3: Toggle both panels
|
||||
if ((event.ctrlKey || event.metaKey) && event.key === '3') {
|
||||
event.preventDefault();
|
||||
toggleBothPanels();
|
||||
}
|
||||
};
|
||||
|
||||
// Handle node highlighting from variable usage
|
||||
const handleNodeHighlight = (event) => {
|
||||
|
||||
@ -1111,6 +1141,9 @@ onMounted(() => {
|
||||
// Add node highlight listener for variable navigation
|
||||
window.addEventListener('highlightNode', handleNodeHighlight);
|
||||
|
||||
// Add keyboard shortcuts listener
|
||||
window.addEventListener('keydown', handleKeyboardShortcuts);
|
||||
|
||||
// Initial screen size check
|
||||
checkScreenSize();
|
||||
});
|
||||
@ -1128,6 +1161,7 @@ onUnmounted(() => {
|
||||
document.removeEventListener('click', handleClickOutside);
|
||||
window.removeEventListener('resize', checkScreenSize);
|
||||
window.removeEventListener('highlightNode', handleNodeHighlight);
|
||||
window.removeEventListener('keydown', handleKeyboardShortcuts);
|
||||
|
||||
// Clear highlight timeout if it exists
|
||||
if (highlightTimeout.value) {
|
||||
@ -2107,31 +2141,57 @@ const sendToBack = () => {
|
||||
<p class="text-sm text-gray-500">Create business processes with drag and drop</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Mobile panel toggles -->
|
||||
<div v-if="hasCurrentProcess && (isMobile || isTablet)" class="flex items-center gap-1 ml-2">
|
||||
</div>
|
||||
|
||||
<!-- Middle section - Process name and Panel Controls -->
|
||||
<div class="flex-1 flex justify-center items-center mx-2 md:mx-4 gap-4">
|
||||
<!-- Panel toggle controls - Always visible when process is loaded -->
|
||||
<div v-if="hasCurrentProcess" class="flex items-center gap-1">
|
||||
<RsButton
|
||||
@click="toggleLeftPanel"
|
||||
variant="tertiary"
|
||||
size="sm"
|
||||
:class="{ 'bg-gray-100': showLeftPanel }"
|
||||
class="p-1"
|
||||
:class="{
|
||||
'bg-blue-100 text-blue-600 border-blue-200': showLeftPanel,
|
||||
'bg-gray-100 text-gray-600 border-gray-200': !showLeftPanel
|
||||
}"
|
||||
class="p-2 border transition-all duration-200 hover:scale-105 relative"
|
||||
title="Toggle Components Panel (Ctrl+1)"
|
||||
>
|
||||
<Icon name="material-symbols:widgets" class="w-4 h-4" />
|
||||
<span v-if="!showLeftPanel" class="absolute -top-1 -right-1 w-2 h-2 bg-orange-400 rounded-full"></span>
|
||||
</RsButton>
|
||||
<RsButton
|
||||
@click="toggleRightPanel"
|
||||
variant="tertiary"
|
||||
size="sm"
|
||||
:class="{ 'bg-gray-100': showRightPanel }"
|
||||
class="p-1"
|
||||
:class="{
|
||||
'bg-blue-100 text-blue-600 border-blue-200': showRightPanel,
|
||||
'bg-gray-100 text-gray-600 border-gray-200': !showRightPanel
|
||||
}"
|
||||
class="p-2 border transition-all duration-200 hover:scale-105 relative"
|
||||
title="Toggle Properties Panel (Ctrl+2)"
|
||||
>
|
||||
<Icon name="material-symbols:tune" class="w-4 h-4" />
|
||||
<span v-if="!showRightPanel" class="absolute -top-1 -right-1 w-2 h-2 bg-orange-400 rounded-full"></span>
|
||||
</RsButton>
|
||||
<RsButton
|
||||
@click="toggleBothPanels"
|
||||
variant="tertiary"
|
||||
size="sm"
|
||||
:class="{
|
||||
'bg-blue-100 text-blue-600 border-blue-200': showLeftPanel && showRightPanel,
|
||||
'bg-gray-100 text-gray-600 border-gray-200': !(showLeftPanel && showRightPanel)
|
||||
}"
|
||||
class="p-2 hidden md:block border transition-all duration-200 hover:scale-105 relative"
|
||||
title="Toggle Both Panels (Ctrl+3)"
|
||||
>
|
||||
<Icon name="material-symbols:view-sidebar" class="w-4 h-4" />
|
||||
<span v-if="!showLeftPanel || !showRightPanel" class="absolute -top-1 -right-1 w-2 h-2 bg-orange-400 rounded-full"></span>
|
||||
</RsButton>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Middle section - Process name -->
|
||||
<div class="flex-1 flex justify-center items-center mx-2 md:mx-4">
|
||||
<!-- Process name input -->
|
||||
<FormKit
|
||||
v-if="hasCurrentProcess"
|
||||
v-model="processStore.currentProcess.name"
|
||||
@ -2209,14 +2269,14 @@ const sendToBack = () => {
|
||||
</header>
|
||||
|
||||
<!-- Main Content Area -->
|
||||
<div class="flex-1 flex overflow-hidden relative" v-if="hasCurrentProcess">
|
||||
<div class="flex-1 flex overflow-hidden relative h-full" v-if="hasCurrentProcess">
|
||||
<!-- Left Panel - Components -->
|
||||
<div
|
||||
v-show="showLeftPanel"
|
||||
:class="{
|
||||
'absolute inset-y-0 left-0 z-20 bg-white shadow-lg': isMobile,
|
||||
'absolute inset-y-0 left-0 z-10 bg-white shadow-md': isTablet,
|
||||
'relative w-64': !isMobile && !isTablet,
|
||||
'relative w-60': !isMobile && !isTablet,
|
||||
'w-72': isMobile,
|
||||
'w-80': isTablet
|
||||
}"
|
||||
@ -2224,11 +2284,11 @@ const sendToBack = () => {
|
||||
>
|
||||
<div class="bg-gray-100 p-3 flex items-center justify-between border-b border-gray-200">
|
||||
<h2 class="text-sm font-medium text-gray-700">Process Components</h2>
|
||||
<!-- Close button for mobile/tablet -->
|
||||
<!-- Close button for all devices -->
|
||||
<button
|
||||
v-if="isMobile || isTablet"
|
||||
@click="showLeftPanel = false"
|
||||
class="p-1 hover:bg-gray-200 rounded"
|
||||
class="p-1 hover:bg-gray-200 rounded transition-colors"
|
||||
title="Close Panel (Ctrl+1)"
|
||||
>
|
||||
<Icon name="material-symbols:close" class="w-4 h-4 text-gray-500" />
|
||||
</button>
|
||||
@ -2239,7 +2299,16 @@ const sendToBack = () => {
|
||||
</div>
|
||||
|
||||
<!-- Center Panel - Process Canvas -->
|
||||
<div class="flex-1 relative">
|
||||
<div class="flex-1 relative h-full" :class="{ 'bg-gray-50': !showLeftPanel && !showRightPanel }">
|
||||
<!-- Canvas State Indicator -->
|
||||
<div v-if="!showLeftPanel && !showRightPanel" class="absolute top-4 left-1/2 transform -translate-x-1/2 z-10 bg-white px-4 py-2 rounded-lg shadow-md border border-gray-200">
|
||||
<div class="flex items-center gap-2 text-sm text-gray-600">
|
||||
<Icon name="material-symbols:fullscreen" class="w-4 h-4 text-green-600" />
|
||||
<span>Full Canvas Mode</span>
|
||||
<span class="text-xs text-gray-400">Press Ctrl+3 to restore panels</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ProcessFlowCanvas
|
||||
ref="processFlowCanvas"
|
||||
:initial-nodes="canvasNodes"
|
||||
@ -2250,6 +2319,7 @@ const sendToBack = () => {
|
||||
@pane-click="onPaneClick"
|
||||
@nodes-change="onNodesChange"
|
||||
@edges-change="onEdgesChange"
|
||||
class="w-full h-full"
|
||||
/>
|
||||
|
||||
<!-- Mobile floating action buttons -->
|
||||
@ -2279,7 +2349,7 @@ const sendToBack = () => {
|
||||
:class="{
|
||||
'absolute inset-y-0 right-0 z-20 bg-white shadow-lg': isMobile,
|
||||
'absolute inset-y-0 right-0 z-10 bg-white shadow-md': isTablet,
|
||||
'relative w-72': !isMobile && !isTablet,
|
||||
'relative w-64': !isMobile && !isTablet,
|
||||
'w-72': isMobile,
|
||||
'w-80': isTablet
|
||||
}"
|
||||
@ -2287,11 +2357,11 @@ const sendToBack = () => {
|
||||
>
|
||||
<div class="bg-gray-100 p-3 flex items-center justify-between border-b border-gray-200">
|
||||
<h2 class="text-sm font-medium text-gray-700">Properties</h2>
|
||||
<!-- Close button for mobile/tablet -->
|
||||
<!-- Close button for all devices -->
|
||||
<button
|
||||
v-if="isMobile || isTablet"
|
||||
@click="showRightPanel = false"
|
||||
class="p-1 hover:bg-gray-200 rounded"
|
||||
class="p-1 hover:bg-gray-200 rounded transition-colors"
|
||||
title="Close Panel (Ctrl+2)"
|
||||
>
|
||||
<Icon name="material-symbols:close" class="w-4 h-4 text-gray-500" />
|
||||
</button>
|
||||
@ -3825,4 +3895,27 @@ const sendToBack = () => {
|
||||
.modern-icon-btn[variant~='danger-text']:hover {
|
||||
background: #fee2e2;
|
||||
}
|
||||
|
||||
/* Ensure full canvas coverage */
|
||||
.process-builder {
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Override Vue Flow container to fill full height */
|
||||
:deep(.vue-flow) {
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
min-height: 100% !important;
|
||||
}
|
||||
|
||||
:deep(.vue-flow__pane) {
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
}
|
||||
|
||||
:deep(.vue-flow__renderer) {
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
}
|
||||
</style>
|
Loading…
x
Reference in New Issue
Block a user