feat: Milestones 3+4 - Frontend widgets and role-based workspaces

- Execution Card JS: live timer with Start/Pause/Stop buttons
  polling get_active_time every second, HH:MM:SS display
- Engineer Workspace: My Active Deliverables cards,
  Execution Card links with employee-filtered shortcuts
- Manager Workspace: Engagement Order/Resource Center/Recipe
  navigation, Active/Stalled/Capacity filtered shortcuts
- Director Workspace: Financial Margin Ledger, Delivery
  Performance cards, KPI shortcuts for Revenue/Cost/Margin
- hooks.py: doctype_js for Execution Card, fixtures for
  workspace auto-sync during migrate
This commit is contained in:
Claus Lohmar 2026-07-15 20:15:17 +00:00
parent 1fbbbb6e7d
commit 7c20c56433
5 changed files with 669 additions and 1 deletions

View file

@ -43,7 +43,7 @@ app_license = "mit"
# page_js = {"page" : "public/js/file.js"} # page_js = {"page" : "public/js/file.js"}
# include js in doctype views # include js in doctype views
# doctype_js = {"doctype" : "public/js/doctype.js"} doctype_js = {"Execution Card" : "doctype/execution_card/execution_card.js"}
# doctype_list_js = {"doctype" : "public/js/doctype_list.js"} # doctype_list_js = {"doctype" : "public/js/doctype_list.js"}
# doctype_tree_js = {"doctype" : "public/js/doctype_tree.js"} # doctype_tree_js = {"doctype" : "public/js/doctype_tree.js"}
# doctype_calendar_js = {"doctype" : "public/js/doctype_calendar.js"} # doctype_calendar_js = {"doctype" : "public/js/doctype_calendar.js"}
@ -167,6 +167,14 @@ app_license = "mit"
# ], # ],
# } # }
# Fixtures - sync Workspaces during migrate
# -----------------------------------------
fixtures = [
{"dt": "Workspace", "filters": [
["module", "=", "Service Factory"]
]}
]
# Testing # Testing
# ------- # -------

View file

@ -0,0 +1,123 @@
// Copyright (c) 2026, cclohmar and contributors
// For license information, please see license.txt
frappe.ui.form.on("Execution Card", {
refresh: function (frm) {
setup_timer_widget(frm);
},
status: function (frm) {
setup_timer_widget(frm);
},
});
function setup_timer_widget(frm) {
// Clear existing timer buttons
frm.remove_custom_button(__("Start Work"), "Actions");
frm.remove_custom_button(__("Pause Work"), "Actions");
frm.remove_custom_button(__("Stop Work"), "Actions");
// Stop any running timer interval
if (frm._timer_interval) {
clearInterval(frm._timer_interval);
frm._timer_interval = null;
}
// Remove existing timer display
frm.$wrapper.find(".execution-card-timer").remove();
const status = frm.doc.status;
if (status === "Open" || status === "Paused") {
frm.add_custom_button(
__("Start Work"),
function () {
frm.call("start_work").then((r) => {
frm.refresh();
});
},
__("Actions")
);
}
if (status === "Running") {
frm.add_custom_button(
__("Pause Work"),
function () {
frm.call("pause_work").then((r) => {
frm.refresh();
});
},
__("Actions")
);
frm.add_custom_button(
__("Stop Work"),
function () {
frm.call("stop_work").then((r) => {
frm.refresh();
});
},
__("Actions")
);
// Start live timer display
start_live_timer(frm);
}
if (status === "Submitted") {
frm.add_custom_button(
__("View Time Logs"),
function () {
frappe.set_route("List", "Execution Card Log", {
parent: frm.doc.name,
});
},
__("Actions")
);
}
}
function start_live_timer(frm) {
// Add a timer display element
const $timer = $(`
<div class="execution-card-timer">
<div class="row">
<div class="col-md-12 text-center" style="padding: 15px 0;">
<div style="font-size: 14px; color: #666;"> ${__("Elapsed Time")}</div>
<div class="timer-display" style="font-size: 48px; font-weight: bold; font-family: monospace; color: #2490ef;">
00:00:00
</div>
</div>
</div>
</div>
`);
// Insert timer after the form title
frm.$wrapper.find(".form-page").prepend($timer);
// Poll for elapsed time every second
frm._timer_interval = setInterval(function () {
frm.call("get_active_time").then((r) => {
if (r.message) {
const seconds = r.message.elapsed_seconds || 0;
const display = format_seconds(seconds);
$timer.find(".timer-display").text(display);
}
});
}, 1000);
}
function format_seconds(total_seconds) {
const hours = Math.floor(total_seconds / 3600);
const minutes = Math.floor((total_seconds % 3600) / 60);
const seconds = Math.floor(total_seconds % 60);
return (
String(hours).padStart(2, "0") +
":" +
String(minutes).padStart(2, "0") +
":" +
String(seconds).padStart(2, "0")
);
}

View file

@ -0,0 +1,170 @@
{
"charts": [],
"content": "[{\"id\":\"director_header_1\",\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Financial Margin Ledger</b></span>\",\"col\":12}},{\"id\":\"director_card_margin\",\"type\":\"card\",\"data\":{\"card_name\":\"Margin Overview\",\"col\":6}},{\"id\":\"director_card_pnl\",\"type\":\"card\",\"data\":{\"card_name\":\"P&amp;L Summary\",\"col\":6}},{\"id\":\"director_spacer_1\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"director_header_2\",\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Delivery Performance</b></span>\",\"col\":12}},{\"id\":\"director_card_efficiency\",\"type\":\"card\",\"data\":{\"card_name\":\"Delivery Efficiency\",\"col\":6}},{\"id\":\"director_card_burn\",\"type\":\"card\",\"data\":{\"card_name\":\"Resource Burn-Down\",\"col\":6}},{\"id\":\"director_spacer_2\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"director_header_3\",\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Executive KPIs</b></span>\",\"col\":12}},{\"id\":\"director_shortcut_revenue\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Revenue Pipeline\",\"col\":3}},{\"id\":\"director_shortcut_costs\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Delivery Costs\",\"col\":3}},{\"id\":\"director_shortcut_margin\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Gross Margin %\",\"col\":3}},{\"id\":\"director_shortcut_utilization\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Utilization Rate\",\"col\":3}}]",
"creation": "2026-07-15 19:00:00.000000",
"custom_blocks": [],
"docstatus": 0,
"doctype": "Workspace",
"for_user": "",
"hide_custom": 0,
"icon": "chart",
"idx": 1,
"is_hidden": 0,
"label": "Director Workspace",
"links": [
{
"hidden": 0,
"is_query_report": 0,
"label": "Financial Overview",
"link_count": 3,
"link_type": "DocType",
"onboard": 0,
"type": "Card Break"
},
{
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
"label": "Engagement Order",
"link_count": 0,
"link_to": "Engagement Order",
"link_type": "DocType",
"onboard": 1,
"type": "Link"
},
{
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
"label": "Execution Card",
"link_count": 0,
"link_to": "Execution Card",
"link_type": "DocType",
"onboard": 1,
"type": "Link"
},
{
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
"label": "Resource Center",
"link_count": 0,
"link_to": "Resource Center",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{
"hidden": 0,
"is_query_report": 0,
"label": "Analytics",
"link_count": 4,
"link_type": "DocType",
"onboard": 0,
"type": "Card Break"
},
{
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
"label": "All Orders",
"link_count": 0,
"link_to": "Engagement Order",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
"label": "Completed Orders",
"link_count": 0,
"link_to": "Engagement Order",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
"label": "Engagement Recipes",
"link_count": 0,
"link_to": "Engagement Recipe",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
"label": "Submitted Cards",
"link_count": 0,
"link_to": "Execution Card",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
}
],
"modified": "2026-07-15 19:00:00.000000",
"modified_by": "Administrator",
"module": "Service Factory",
"name": "Director Workspace",
"number_cards": [],
"owner": "Administrator",
"parent_page": "",
"public": 1,
"quick_lists": [],
"roles": [],
"sequence_id": 3.0,
"shortcuts": [
{
"action": "List",
"color": "Green",
"doc_type": "Engagement Order",
"format": "{}",
"icon": "currency",
"label": "Revenue Pipeline",
"link_to": "Engagement Order",
"stats_filter": "[[\"Engagement Order\",\"docstatus\",\"=\",1],[\"Engagement Order\",\"status\",\"!=\",\"Cancelled\"]]",
"type": "DocType"
},
{
"action": "List",
"color": "Red",
"doc_type": "Execution Card",
"format": "{}",
"icon": "cost",
"label": "Delivery Costs",
"link_to": "Execution Card",
"stats_filter": "[[\"Execution Card\",\"status\",\"=\",\"Submitted\"]]",
"type": "DocType"
},
{
"action": "Report",
"color": "Blue",
"doc_type": "Engagement Order",
"format": "{}",
"icon": "percent",
"label": "Gross Margin %",
"link_to": "Engagement Order",
"stats_filter": "[]",
"type": "DocType"
},
{
"action": "List",
"color": "Orange",
"doc_type": "Resource Center",
"format": "{}",
"icon": "chart",
"label": "Utilization Rate",
"link_to": "Resource Center",
"stats_filter": "[]",
"type": "DocType"
}
],
"title": "Director Workspace",
"type": "Workspace"
}

View file

@ -0,0 +1,146 @@
{
"charts": [],
"content": "[{\"id\":\"engineer_header_1\",\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>My Active Deliverables</b></span>\",\"col\":12}},{\"id\":\"engineer_card_my_cards\",\"type\":\"card\",\"data\":{\"card_name\":\"My Execution Cards\",\"col\":4}},{\"id\":\"engineer_card_orders\",\"type\":\"card\",\"data\":{\"card_name\":\"My Engagement Orders\",\"col\":4}},{\"id\":\"engineer_card_reports\",\"type\":\"card\",\"data\":{\"card_name\":\"Reports\",\"col\":4}},{\"id\":\"engineer_spacer_1\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"engineer_header_2\",\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Time Tracking</b></span>\",\"col\":12}},{\"id\":\"engineer_card_timer\",\"type\":\"card\",\"data\":{\"card_name\":\"Execution Card Timer\",\"col\":6}},{\"id\":\"engineer_card_hours\",\"type\":\"card\",\"data\":{\"card_name\":\"My Hours This Week\",\"col\":6}},{\"id\":\"engineer_spacer_2\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"engineer_header_3\",\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Quick Actions</b></span>\",\"col\":12}},{\"id\":\"engineer_shortcut_new_card\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"New Execution Card\",\"col\":3}},{\"id\":\"engineer_shortcut_active\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"My Active Cards\",\"col\":3}}]",
"creation": "2026-07-15 19:00:00.000000",
"custom_blocks": [],
"docstatus": 0,
"doctype": "Workspace",
"for_user": "",
"hide_custom": 0,
"icon": "workflow",
"idx": 1,
"is_hidden": 0,
"label": "Engineer Workspace",
"links": [
{
"hidden": 0,
"is_query_report": 0,
"label": "My Execution Cards",
"link_count": 3,
"link_type": "DocType",
"onboard": 0,
"type": "Card Break"
},
{
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
"label": "Execution Card",
"link_count": 0,
"link_to": "Execution Card",
"link_type": "DocType",
"onboard": 1,
"type": "Link"
},
{
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
"label": "My Open Cards",
"link_count": 0,
"link_to": "Execution Card",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
"label": "My Submitted Cards",
"link_count": 0,
"link_to": "Execution Card",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{
"hidden": 0,
"is_query_report": 0,
"label": "My Engagement Orders",
"link_count": 2,
"link_type": "DocType",
"onboard": 0,
"type": "Card Break"
},
{
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
"label": "Engagement Order",
"link_count": 0,
"link_to": "Engagement Order",
"link_type": "DocType",
"onboard": 1,
"type": "Link"
},
{
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
"label": "My Active Orders",
"link_count": 0,
"link_to": "Engagement Order",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{
"hidden": 0,
"is_query_report": 0,
"label": "Reports",
"link_count": 1,
"link_type": "DocType",
"onboard": 0,
"type": "Card Break"
},
{
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
"label": "My Hours",
"link_count": 0,
"link_to": "Execution Card",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
}
],
"modified": "2026-07-15 19:00:00.000000",
"modified_by": "Administrator",
"module": "Service Factory",
"name": "Engineer Workspace",
"number_cards": [],
"owner": "Administrator",
"parent_page": "",
"public": 1,
"quick_lists": [],
"roles": [],
"sequence_id": 1.0,
"shortcuts": [
{
"action": "New",
"color": "Blue",
"doc_type": "Execution Card",
"format": "{}",
"icon": "add",
"label": "New Execution Card",
"link_to": "Execution Card",
"stats_filter": "[]",
"type": "DocType"
},
{
"action": "List",
"color": "Grey",
"doc_type": "Execution Card",
"format": "{}",
"icon": "list",
"label": "My Active Cards",
"link_to": "Execution Card",
"stats_filter": "[[\"Execution Card\",\"assigned_employee\",\"=\",\"__user_employee__\"],[\"Execution Card\",\"status\",\"in\",[\"Open\",\"Running\",\"Paused\"]]]",
"type": "DocType"
}
],
"title": "Engineer Workspace",
"type": "Workspace"
}

View file

@ -0,0 +1,221 @@
{
"charts": [],
"content": "[{\"id\":\"manager_header_1\",\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Operational Overview</b></span>\",\"col\":12}},{\"id\":\"manager_card_orders\",\"type\":\"card\",\"data\":{\"card_name\":\"Engagement Orders\",\"col\":4}},{\"id\":\"manager_card_centers\",\"type\":\"card\",\"data\":{\"card_name\":\"Resource Centers\",\"col\":4}},{\"id\":\"manager_card_recipes\",\"type\":\"card\",\"data\":{\"card_name\":\"Engagement Recipes\",\"col\":4}},{\"id\":\"manager_spacer_1\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"manager_header_2\",\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Resource Capacity</b></span>\",\"col\":12}},{\"id\":\"manager_card_gantt\",\"type\":\"card\",\"data\":{\"card_name\":\"Schedule Gantt\",\"col\":6}},{\"id\":\"manager_card_alerts\",\"type\":\"card\",\"data\":{\"card_name\":\"Capacity Alerts\",\"col\":6}},{\"id\":\"manager_spacer_2\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"manager_header_3\",\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Key Metrics</b></span>\",\"col\":12}},{\"id\":\"manager_shortcut_active\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Active Engagements\",\"col\":3}},{\"id\":\"manager_shortcut_stalled\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Stalled / Blocked\",\"col\":3}},{\"id\":\"manager_shortcut_capacity\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Capacity Overview\",\"col\":3}},{\"id\":\"manager_shortcut_executions\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"All Execution Cards\",\"col\":3}}]",
"creation": "2026-07-15 19:00:00.000000",
"custom_blocks": [],
"docstatus": 0,
"doctype": "Workspace",
"for_user": "",
"hide_custom": 0,
"icon": "share",
"idx": 1,
"is_hidden": 0,
"label": "Manager Workspace",
"links": [
{
"hidden": 0,
"is_query_report": 0,
"label": "Engagement Orders",
"link_count": 4,
"link_type": "DocType",
"onboard": 0,
"type": "Card Break"
},
{
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
"label": "Engagement Order",
"link_count": 0,
"link_to": "Engagement Order",
"link_type": "DocType",
"onboard": 1,
"type": "Link"
},
{
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
"label": "Scheduled Orders",
"link_count": 0,
"link_to": "Engagement Order",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
"label": "In Progress Orders",
"link_count": 0,
"link_to": "Engagement Order",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
"label": "Completed Orders",
"link_count": 0,
"link_to": "Engagement Order",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{
"hidden": 0,
"is_query_report": 0,
"label": "Resource Centers",
"link_count": 2,
"link_type": "DocType",
"onboard": 0,
"type": "Card Break"
},
{
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
"label": "Resource Center",
"link_count": 0,
"link_to": "Resource Center",
"link_type": "DocType",
"onboard": 1,
"type": "Link"
},
{
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
"label": "Resource Center Members",
"link_count": 0,
"link_to": "Resource Center Member",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{
"hidden": 0,
"is_query_report": 0,
"label": "Engagement Recipes",
"link_count": 2,
"link_type": "DocType",
"onboard": 0,
"type": "Card Break"
},
{
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
"label": "Engagement Recipe",
"link_count": 0,
"link_to": "Engagement Recipe",
"link_type": "DocType",
"onboard": 1,
"type": "Link"
},
{
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
"label": "Engagement Recipe Steps",
"link_count": 0,
"link_to": "Engagement Recipe Step",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{
"hidden": 0,
"is_query_report": 0,
"label": "Execution Tracking",
"link_count": 2,
"link_type": "DocType",
"onboard": 0,
"type": "Card Break"
},
{
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
"label": "Execution Card",
"link_count": 0,
"link_to": "Execution Card",
"link_type": "DocType",
"onboard": 1,
"type": "Link"
},
{
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
"label": "Execution Card Logs",
"link_count": 0,
"link_to": "Execution Card Log",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
}
],
"modified": "2026-07-15 19:00:00.000000",
"modified_by": "Administrator",
"module": "Service Factory",
"name": "Manager Workspace",
"number_cards": [],
"owner": "Administrator",
"parent_page": "",
"public": 1,
"quick_lists": [],
"roles": [],
"sequence_id": 2.0,
"shortcuts": [
{
"action": "List",
"color": "Blue",
"doc_type": "Engagement Order",
"format": "{}",
"icon": "list",
"label": "Active Engagements",
"link_to": "Engagement Order",
"stats_filter": "[[\"Engagement Order\",\"status\",\"in\",[\"Scheduled\",\"In Progress\"]]]",
"type": "DocType"
},
{
"action": "List",
"color": "Red",
"doc_type": "Engagement Order",
"format": "{}",
"icon": "info",
"label": "Stalled / Blocked",
"link_to": "Engagement Order",
"stats_filter": "[[\"Engagement Order\",\"status\",\"=\",\"On Hold\"]]",
"type": "DocType"
},
{
"action": "List",
"color": "Green",
"doc_type": "Resource Center",
"format": "{}",
"icon": "organization",
"label": "Capacity Overview",
"link_to": "Resource Center",
"stats_filter": "[]",
"type": "DocType"
},
{
"action": "List",
"color": "Grey",
"doc_type": "Execution Card",
"format": "{}",
"icon": "check",
"label": "All Execution Cards",
"link_to": "Execution Card",
"stats_filter": "[]",
"type": "DocType"
}
],
"title": "Manager Workspace",
"type": "Workspace"
}