extends VBoxContainer const Brand := preload("res://scripts/ui/medmind_style.gd") const ICON_STETHOSCOPE := preload("res://assets/icons/lucide/stethoscope.svg") const ICON_CLIPBOARD := preload("res://assets/icons/lucide/clipboard-plus.svg") const ICON_CLOCK := preload("res://assets/icons/lucide/clock-3.svg") const ICON_LOCATION := preload("res://assets/icons/lucide/house.svg") const ICON_SEARCH := preload("res://assets/icons/lucide/search.svg") const RESPONSIVE_BREAKPOINT := 1050.0 const CHART_TABS := [ ["Order Sheet", "orders"], ["Progress Notes", "progress"], ["Vital Signs", "vitals"], ["Lab Reports", "lab"], ["Imaging", "imaging"], ["Other Tests", "other_test"], ["Treatment Record", "treatment"] ] var main: Control var header_panel: PanelContainer var header_layout: BoxContainer var heading: Label var allotted_label: Label var toolbar: HFlowContainer var theme_button: Button var exit_button: Button var abandon_button: Button var action_grid: GridContainer var action_buttons: Array[Button] = [] var workspace: PanelContainer var chart_panel: PanelContainer var clerk_panel: PanelContainer var tabs: TabContainer var chart_views := {} var order_sheet: VBoxContainer var active_orders: VBoxContainer var search: LineEdit var search_button: Button var search_results: VBoxContainer var staged_orders: VBoxContainer var commit_orders_button: Button var clear_orders_button: Button var final_orders_notice: Label var simulated_clock: Label var elapsed_clock: Label var remaining_clock: Label var status: Label var timer: Timer var report_dialog: Control var intro_dialog: AcceptDialog var intro_title: Label var intro_body: RichTextLabel var opening_step := 0 var opening_active := false var exam_dialog: ConfirmationDialog var interval_history_check: CheckBox var exam_checks := {} var exam_summary_dialog: ConfirmationDialog var exam_summary: RichTextLabel var staged_exam_codes: Array[String] = [] var staged_interval_history := false var awaiting_exam_result := false var findings_dialog: AcceptDialog var findings_body: RichTextLabel var time_dialog: ConfirmationDialog var time_preset: OptionButton var custom_target: LineEdit var time_preview: Label var location_dialog: ConfirmationDialog var location_option: OptionButton var end_dialog: ConfirmationDialog var final_instructions: AcceptDialog var abandon_dialog: ConfirmationDialog var staged_order_codes: Array[String] = [] var staged_order_names := {} var pending_findings_before := 0 var leaving := false var theme_service var toolbar_buttons: Array[Button] = [] var clinical_labels: Array[RichTextLabel] = [] func _ready() -> void: theme_service = get_node("/root/UIThemeService") set_anchors_preset(Control.PRESET_FULL_RECT) add_theme_constant_override("separation", 8) _build_header() _build_actions() _build_workspace() _build_footer() _build_dialogs() _build_runtime_services() resized.connect(_update_responsive_layout) if not theme_service.theme_changed.is_connected(_on_theme_changed): theme_service.theme_changed.connect(_on_theme_changed) if not CCSService.text_scale_changed.is_connected(_apply_text_scale): CCSService.text_scale_changed.connect(_apply_text_scale) _apply_theme() call_deferred("_update_responsive_layout") func _build_header() -> void: header_panel = PanelContainer.new() add_child(header_panel) var margin := MarginContainer.new() for side in ["left", "right"]: margin.add_theme_constant_override("margin_%s" % side, 14) margin.add_theme_constant_override("margin_top", 8) margin.add_theme_constant_override("margin_bottom", 8) header_panel.add_child(margin) header_layout = BoxContainer.new() header_layout.add_theme_constant_override("separation", 12) margin.add_child(header_layout) heading = Label.new() heading.text = "CCS Case" heading.size_flags_horizontal = Control.SIZE_EXPAND_FILL heading.add_theme_font_size_override("font_size", 18) Brand.apply_font(heading) header_layout.add_child(heading) allotted_label = Label.new() allotted_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER allotted_label.size_flags_horizontal = Control.SIZE_EXPAND_FILL allotted_label.add_theme_font_size_override("font_size", 17) Brand.apply_font(allotted_label) header_layout.add_child(allotted_label) toolbar = HFlowContainer.new() toolbar.size_flags_horizontal = Control.SIZE_SHRINK_END toolbar.add_theme_constant_override("h_separation", 6) header_layout.add_child(toolbar) var smaller := _toolbar_button("A−", "Decrease clinical text size") smaller.pressed.connect(_zoom.bind(-CCSService.TEXT_SCALE_STEP)) var larger := _toolbar_button("A+", "Increase clinical text size") larger.pressed.connect(_zoom.bind(CCSService.TEXT_SCALE_STEP)) var report := _toolbar_button("Report", "Report this CCS case") report.pressed.connect(_report) theme_button = _toolbar_button("", "Switch color theme") theme_button.custom_minimum_size.x = 42 theme_button.pressed.connect(theme_service.toggle_theme) exit_button = _toolbar_button("Exit Case", "Enter final orders") exit_button.pressed.connect(_request_end_case) abandon_button = _toolbar_button("Abandon", "Abandon this attempt without a score") abandon_button.pressed.connect(func(): abandon_dialog.popup_centered(Vector2i(520, 220))) func _toolbar_button(text_value: String, tooltip: String) -> Button: var button := Button.new() button.text = text_value button.tooltip_text = tooltip button.custom_minimum_size = Vector2(58, 38) toolbar.add_child(button) toolbar_buttons.append(button) return button func _build_actions() -> void: action_grid = GridContainer.new() action_grid.columns = 4 action_grid.add_theme_constant_override("h_separation", 8) action_grid.add_theme_constant_override("v_separation", 8) add_child(action_grid) _add_action("History & Physical", "Interval history and physical exam", ICON_STETHOSCOPE, _open_exam) _add_action("Write Orders / Review Chart", "Place orders and review the patient chart", ICON_CLIPBOARD, _open_order_sheet) _add_action("Obtain Results / See Patient Later", "Advance simulated case time", ICON_CLOCK, _open_time_dialog) _add_action("Change Location", "Move the patient to another care setting", ICON_LOCATION, _open_location_dialog) func _add_action(title: String, subtitle: String, icon: Texture2D, callback: Callable) -> void: var button := Button.new() button.text = "%s\n%s" % [title, subtitle] button.icon = icon button.expand_icon = true button.add_theme_constant_override("icon_max_width", 38) button.alignment = HORIZONTAL_ALIGNMENT_LEFT button.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART button.custom_minimum_size = Vector2(220, 86) button.size_flags_horizontal = Control.SIZE_EXPAND_FILL button.pressed.connect(callback) action_grid.add_child(button) action_buttons.append(button) func _build_workspace() -> void: workspace = PanelContainer.new() workspace.size_flags_vertical = Control.SIZE_EXPAND_FILL add_child(workspace) chart_panel = workspace clerk_panel = workspace var margin := MarginContainer.new() margin.add_theme_constant_override("margin_left", 14) margin.add_theme_constant_override("margin_right", 14) margin.add_theme_constant_override("margin_top", 10) margin.add_theme_constant_override("margin_bottom", 12) workspace.add_child(margin) tabs = TabContainer.new() tabs.size_flags_vertical = Control.SIZE_EXPAND_FILL tabs.tab_changed.connect(func(_index: int): _render_chart()) margin.add_child(tabs) for definition in CHART_TABS: var page := VBoxContainer.new() page.name = str(definition[0]) page.add_theme_constant_override("separation", 8) tabs.add_child(page) if str(definition[1]) == "orders": _build_order_sheet(page) else: var scroll := ScrollContainer.new() scroll.size_flags_vertical = Control.SIZE_EXPAND_FILL page.add_child(scroll) var label := _clinical_text("chart_%s" % definition[1]) label.size_flags_horizontal = Control.SIZE_EXPAND_FILL scroll.add_child(label) chart_views[definition[1]] = label func _build_order_sheet(page: VBoxContainer) -> void: order_sheet = page final_orders_notice = Label.new() final_orders_notice.visible = false final_orders_notice.text = "FINAL ORDERS — Add or cancel orders for the patient’s current condition, then submit." final_orders_notice.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART page.add_child(final_orders_notice) var search_row := HBoxContainer.new() search_row.add_theme_constant_override("separation", 6) page.add_child(search_row) search = LineEdit.new() search.placeholder_text = "Search tests, treatments, medications, and procedures" search.size_flags_horizontal = Control.SIZE_EXPAND_FILL search.text_submitted.connect(func(_value: String): _submit_search()) search_row.add_child(search) search_button = Button.new() search_button.icon = ICON_SEARCH search_button.tooltip_text = "Search the order catalog" search_button.custom_minimum_size = Vector2(44, 42) search_button.pressed.connect(_submit_search) search_row.add_child(search_button) search_results = VBoxContainer.new() search_results.add_theme_constant_override("separation", 4) page.add_child(search_results) var staged_title := Label.new() staged_title.text = "Orders awaiting confirmation" Brand.apply_font(staged_title) page.add_child(staged_title) staged_orders = VBoxContainer.new() page.add_child(staged_orders) var staged_actions := HBoxContainer.new() page.add_child(staged_actions) commit_orders_button = Button.new() commit_orders_button.text = "Confirm Orders" commit_orders_button.pressed.connect(_commit_orders) staged_actions.add_child(commit_orders_button) clear_orders_button = Button.new() clear_orders_button.text = "Clear" clear_orders_button.pressed.connect(_clear_staged_orders) staged_actions.add_child(clear_orders_button) page.add_child(HSeparator.new()) var active_title := Label.new() active_title.text = "Order sheet" active_title.add_theme_font_size_override("font_size", 18) Brand.apply_font(active_title) page.add_child(active_title) var header := HBoxContainer.new() for item in [["Order", 2.4], ["Route", 0.8], ["Frequency", 1.0], ["Order Time", 1.0], ["Report Time", 1.0], ["", 0.8]]: var label := Label.new() label.text = item[0] label.size_flags_horizontal = Control.SIZE_EXPAND_FILL label.size_flags_stretch_ratio = item[1] Brand.apply_font(label) header.add_child(label) page.add_child(header) var active_scroll := ScrollContainer.new() active_scroll.size_flags_vertical = Control.SIZE_EXPAND_FILL page.add_child(active_scroll) active_orders = VBoxContainer.new() active_orders.size_flags_horizontal = Control.SIZE_EXPAND_FILL active_scroll.add_child(active_orders) func _build_footer() -> void: var footer := HBoxContainer.new() footer.add_theme_constant_override("separation", 12) add_child(footer) simulated_clock = _footer_label(HORIZONTAL_ALIGNMENT_LEFT) footer.add_child(simulated_clock) elapsed_clock = _footer_label(HORIZONTAL_ALIGNMENT_CENTER) footer.add_child(elapsed_clock) remaining_clock = _footer_label(HORIZONTAL_ALIGNMENT_RIGHT) footer.add_child(remaining_clock) status = Label.new() status.visible = false status.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART add_child(status) func _footer_label(h_align: HorizontalAlignment) -> Label: var label := Label.new() label.horizontal_alignment = h_align label.size_flags_horizontal = Control.SIZE_EXPAND_FILL label.add_theme_font_size_override("font_size", 14) Brand.apply_font(label) return label func _build_dialogs() -> void: intro_dialog = AcceptDialog.new() intro_dialog.close_requested.connect(_accept_opening_step) intro_dialog.confirmed.connect(_accept_opening_step) add_child(intro_dialog) var intro_box := VBoxContainer.new() intro_box.custom_minimum_size = Vector2(760, 480) intro_dialog.add_child(intro_box) intro_title = Label.new() intro_title.add_theme_font_size_override("font_size", 22) Brand.apply_font(intro_title) intro_box.add_child(intro_title) intro_body = _clinical_text("opening") intro_body.size_flags_vertical = Control.SIZE_EXPAND_FILL intro_body.fit_content = false intro_body.scroll_active = true intro_box.add_child(intro_body) exam_dialog = ConfirmationDialog.new() exam_dialog.title = "History & Physical" exam_dialog.ok_button_text = "Review selection" exam_dialog.confirmed.connect(_review_exam_selection) add_child(exam_dialog) var exam_scroll := ScrollContainer.new() exam_scroll.custom_minimum_size = Vector2(680, 480) exam_dialog.add_child(exam_scroll) var exam_box := VBoxContainer.new() exam_box.size_flags_horizontal = Control.SIZE_EXPAND_FILL exam_scroll.add_child(exam_box) var history_help := Label.new() history_help.text = "Select physical examination components. Findings become available after the confirmed time advance." history_help.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART exam_box.add_child(history_help) exam_box.set_meta("exam_container", true) exam_summary_dialog = ConfirmationDialog.new() exam_summary_dialog.title = "Confirm History & Physical" exam_summary_dialog.confirmed.connect(_commit_exams) add_child(exam_summary_dialog) exam_summary = _clinical_text("exam_confirmation") exam_summary.custom_minimum_size = Vector2(620, 300) exam_summary_dialog.add_child(exam_summary) findings_dialog = AcceptDialog.new() findings_dialog.title = "History and Physical" add_child(findings_dialog) findings_body = _clinical_text("exam_findings") findings_body.custom_minimum_size = Vector2(720, 440) findings_body.fit_content = false findings_body.scroll_active = true findings_dialog.add_child(findings_body) time_dialog = ConfirmationDialog.new() time_dialog.title = "Obtain Results / See Patient Later" time_dialog.confirmed.connect(_commit_time) add_child(time_dialog) var time_box := VBoxContainer.new() time_box.custom_minimum_size = Vector2(540, 260) time_dialog.add_child(time_box) time_preset = OptionButton.new() for item in [["10 minutes", 10], ["1 hour", 60], ["4 hours", 240], ["12 hours", 720], ["1 day", 1440], ["Custom target", -1]]: time_preset.add_item(item[0]) time_preset.set_item_metadata(time_preset.item_count - 1, item[1]) time_preset.item_selected.connect(_update_time_preview) time_box.add_child(time_preset) custom_target = LineEdit.new() custom_target.placeholder_text = "YYYY-MM-DD HH:MM (case time)" custom_target.visible = false custom_target.text_changed.connect(func(_value: String): _update_time_preview(time_preset.selected)) time_box.add_child(custom_target) time_preview = Label.new() time_preview.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART time_box.add_child(time_preview) location_dialog = ConfirmationDialog.new() location_dialog.title = "Change Location" location_dialog.confirmed.connect(_commit_location) add_child(location_dialog) var location_box := VBoxContainer.new() location_box.custom_minimum_size = Vector2(480, 180) location_dialog.add_child(location_box) var location_help := Label.new() location_help.text = "Select the patient’s destination:" location_box.add_child(location_help) location_option = OptionButton.new() location_box.add_child(location_option) end_dialog = ConfirmationDialog.new() end_dialog.title = "Enter Final Orders?" end_dialog.dialog_text = "This ends active case management. You will have up to two minutes to finalize or cancel patient-care orders." end_dialog.ok_button_text = "Enter Final Orders" end_dialog.confirmed.connect(func(): CCSService.send_command("end_case")) add_child(end_dialog) final_instructions = AcceptDialog.new() final_instructions.title = "Case-end Instructions" final_instructions.dialog_text = "Finalize care of the patient’s current condition.\n\n• Cancel orders that should stop now.\n• Add orders that should be done now.\n• Add relevant future-care orders.\n\nThen submit final orders before time expires." final_instructions.confirmed.connect(_open_order_sheet) add_child(final_instructions) abandon_dialog = ConfirmationDialog.new() abandon_dialog.title = "Abandon CCS case?" abandon_dialog.dialog_text = "This attempt will end without a score and cannot be resumed." abandon_dialog.ok_button_text = "Abandon Case" abandon_dialog.confirmed.connect(_confirm_abandon) add_child(abandon_dialog) func _build_runtime_services() -> void: timer = Timer.new() timer.wait_time = 1 timer.timeout.connect(_tick) add_child(timer) CCSService.attempt_changed.connect(_render) CCSService.attempt_abandoned.connect(_on_abandoned) CCSService.orders_changed.connect(_render_search) CCSService.request_failed.connect(_request_failed) CCSService.report_submitted.connect(_report_submitted) report_dialog = preload("res://scripts/ui/ccs_report_dialog.gd").new() add_child(report_dialog) func on_show(payload: Variant = null) -> void: _set_status("") leaving = false if typeof(payload) == TYPE_STRING and not str(payload).is_empty(): CCSService.load_attempt(str(payload)) _render() timer.start() func on_hide() -> void: timer.stop() _dismiss_dialogs() func _dismiss_dialogs() -> void: # Dialogs are Window nodes, so hiding their parent Control does not dismiss # them. Always close every CCS overlay when navigation or logout hides the # player to prevent case content from appearing over another screen. for dialog in _ccs_dialogs(): if dialog != null: dialog.hide() if report_dialog != null and report_dialog.has_method("hide_dialog"): report_dialog.hide_dialog() func _ccs_dialogs() -> Array[Window]: var dialogs: Array[Window] = [] for dialog in [intro_dialog, exam_dialog, exam_summary_dialog, findings_dialog, time_dialog, location_dialog, end_dialog, final_instructions, abandon_dialog]: if dialog is Window: dialogs.append(dialog) return dialogs func _render() -> void: var attempt: Dictionary = CCSService.attempt if attempt.is_empty(): return if str(attempt.get("state", "")) == "completed": main.show_screen("ccs_review", attempt) return var was_final := exit_button.text == "Submit Final Orders" var final_orders := str(attempt.get("state", "")) == "final_orders" for code in staged_order_codes.duplicate(): if code in attempt.get("activeOrderCodes", []): staged_order_codes.erase(code) staged_order_names.erase(code) heading.text = "CCS Case | %s" % _humanize_code(str(attempt.get("location", ""))) var total := int(attempt.get("realTimeMinutes", 20)) allotted_label.text = ( "Legacy untimed CCS case" if str(attempt.get("mode", "timed")) == "practice" else "Maximum allotted real time: %d minutes + 2 minutes for final orders" % maxi(1, total - 2) ) exit_button.text = "Submit Final Orders" if final_orders else "Exit Case" exit_button.tooltip_text = "Submit final orders and complete the case" if final_orders else "Enter final orders" final_orders_notice.visible = final_orders for button in action_buttons: button.disabled = final_orders action_buttons[1].disabled = false _render_active_orders() _render_staged_orders() _render_chart() _tick() _apply_text_scale() if not bool(attempt.get("openingCompleted", true)) and not opening_active: _start_opening() if final_orders and not was_final: final_instructions.popup_centered(Vector2i(820, 500)) _open_order_sheet() if awaiting_exam_result and not CCSService.is_loading: awaiting_exam_result = false _show_new_findings() func _start_opening() -> void: opening_active = true opening_step = 0 _show_opening_step() func _show_opening_step() -> void: var attempt: Dictionary = CCSService.attempt match opening_step: 0: intro_dialog.title = "Case Introduction" intro_title.text = _simulated_time_text(0) intro_body.text = "[center][b]%s[/b]\n\n%s[/center]" % [ _escape_bbcode(_humanize_code(str(attempt.get("location", "")))), _escape_bbcode(str(attempt.get("reasonForEncounter", ""))) ] 1: intro_dialog.title = "Initial Vital Signs" intro_title.text = _simulated_time_text(0) intro_body.text = _vitals_bbcode(attempt.get("vitals", {})) 2: intro_dialog.title = "Initial History" intro_title.text = str(attempt.get("patientSummary", "")) intro_body.text = _history_bbcode() intro_dialog.popup_centered(Vector2i(820, 560)) func _accept_opening_step() -> void: if not opening_active: return opening_step += 1 if opening_step < 3: call_deferred("_show_opening_step") else: opening_active = false CCSService.send_command("acknowledge_opening") func _open_exam() -> void: if _is_final_orders(): return var container: VBoxContainer for node in exam_dialog.find_children("*", "VBoxContainer", true, false): if node.has_meta("exam_container"): container = node break if container == null: return for child in container.get_children(): if child is CheckBox: child.queue_free() exam_checks.clear() interval_history_check = CheckBox.new() interval_history_check.text = "Interval / follow-up history (1 min)" container.add_child(interval_history_check) for raw_exam in CCSService.attempt.get("availableExams", []): var exam: Dictionary = raw_exam var check := CheckBox.new() var minutes := maxi(1, int(exam.get("durationMinutes", 1))) check.text = "%s (%d min)" % [str(exam.get("name", _humanize_code(str(exam.get("code", ""))))), minutes] check.set_meta("exam_code", str(exam.get("code", ""))) check.set_meta("exam_minutes", minutes) container.add_child(check) exam_checks[str(exam.get("code", ""))] = check exam_dialog.popup_centered(Vector2i(740, 600)) func _review_exam_selection() -> void: staged_exam_codes.clear() staged_interval_history = interval_history_check != null and interval_history_check.button_pressed var total := 0 var lines: Array[String] = [] if staged_interval_history: total += 1 lines.append("• Interval / follow-up history — 1 min") for code in exam_checks: var check: CheckBox = exam_checks[code] if check.button_pressed: staged_exam_codes.append(code) var minutes := int(check.get_meta("exam_minutes", 1)) total += minutes lines.append("• %s — %d min" % [check.text.get_slice(" (", 0), minutes]) if staged_exam_codes.is_empty() and not staged_interval_history: _set_status("Select interval history or at least one examination component.") return exam_summary.text = "%s\n\n[b]Total simulated time: %d minutes[/b]\nThis will advance the clock to [b]%s[/b]." % [ "\n".join(lines), total, _simulated_time_text(int(CCSService.attempt.get("simulatedMinute", 0)) + total) ] exam_summary_dialog.popup_centered(Vector2i(680, 420)) func _commit_exams() -> void: var commands: Array = [] if staged_interval_history: commands.append({"type": "interval_history", "code": "", "minutes": 0}) for code in staged_exam_codes: commands.append({"type": "exam", "code": code, "minutes": 0}) pending_findings_before = _chart_entries("progress").size() awaiting_exam_result = true CCSService.send_batch(commands) func _show_new_findings() -> void: var entries := _chart_entries("progress") var start := mini(pending_findings_before, entries.size()) var text := "[center][b]%s[/b][/center]\n\n" % _simulated_time_text(int(CCSService.attempt.get("simulatedMinute", 0))) for index in range(start, entries.size()): var entry: Dictionary = entries[index] if str(entry.get("code", "")).is_empty(): continue text += "[b]%s[/b]\n%s\n\n" % [_escape_bbcode(str(entry.get("title", ""))), _escape_bbcode(str(entry.get("text", "")))] findings_body.text = text findings_dialog.popup_centered(Vector2i(780, 540)) func _open_order_sheet() -> void: tabs.current_tab = 0 search.grab_focus() func _submit_search() -> void: if search.text.strip_edges().length() < 2: _set_status("Enter at least two characters to search.") return _set_status("") CCSService.search_orders(search.text) func _render_search() -> void: _clear(search_results) if CCSService.orders.is_empty(): _set_status( "No matching orders found. Try a clinical name or category." if search.text.strip_edges().length() >= 2 else "") return _set_status("") for raw_order in CCSService.orders: var order: Dictionary = raw_order var code := str(order.get("code", "")) var button := Button.new() button.text = "%s · %s" % [str(order.get("canonicalName", code)), _humanize_code(str(order.get("category", "")))] button.alignment = HORIZONTAL_ALIGNMENT_LEFT button.disabled = code in staged_order_codes or code in CCSService.attempt.get("activeOrderCodes", []) button.pressed.connect(_stage_order.bind(code)) search_results.add_child(button) func _stage_order(code: String) -> void: if code.is_empty() or code in staged_order_codes: return staged_order_names[code] = _catalog_name(code) staged_order_codes.append(code) search.text = "" CCSService.orders = [] _render_search() _render_staged_orders() func _render_staged_orders() -> void: _clear(staged_orders) for code in staged_order_codes: var row := HBoxContainer.new() staged_orders.add_child(row) var label := Label.new() label.text = _catalog_name(code) label.size_flags_horizontal = Control.SIZE_EXPAND_FILL row.add_child(label) var remove := Button.new() remove.text = "Remove" remove.pressed.connect(_unstage_order.bind(code)) row.add_child(remove) commit_orders_button.disabled = staged_order_codes.is_empty() clear_orders_button.disabled = staged_order_codes.is_empty() func _unstage_order(code: String) -> void: staged_order_codes.erase(code) staged_order_names.erase(code) _render_staged_orders() func _clear_staged_orders() -> void: staged_order_codes.clear() staged_order_names.clear() _render_staged_orders() func _commit_orders() -> void: if staged_order_codes.is_empty(): return var commands: Array = [] for code in staged_order_codes: commands.append({"type": "order", "code": code, "minutes": 0}) CCSService.orders = [] _render_search() _render_staged_orders() CCSService.send_batch(commands) func _render_active_orders() -> void: _clear(active_orders) var rows: Array = CCSService.attempt.get("activeOrders", []) if rows.is_empty(): var empty := Label.new() empty.text = "No active orders." active_orders.add_child(empty) return for raw_order in rows: var order: Dictionary = raw_order var row := HBoxContainer.new() row.custom_minimum_size.y = 44 active_orders.add_child(row) _add_order_cell(row, str(order.get("name", _humanize_code(str(order.get("code", ""))))), 2.4) _add_order_cell(row, str(order.get("route", "—")) if not str(order.get("route", "")).is_empty() else "—", 0.8) _add_order_cell(row, str(order.get("frequency", "—")) if not str(order.get("frequency", "")).is_empty() else "—", 1.0) _add_order_cell(row, _simulated_time_text(int(order.get("orderedMinute", 0)), false), 1.0) var report_value: Variant = order.get("reportMinute", null) var report_text := "—" if bool(order.get("isPending", false)): report_text = "Pending" elif bool(order.get("hasResult", report_value != null)) and report_value != null: report_text = _simulated_time_text(int(report_value), false) _add_order_cell(row, report_text, 1.0) var cancel := Button.new() cancel.text = "Cancel" cancel.size_flags_horizontal = Control.SIZE_EXPAND_FILL cancel.size_flags_stretch_ratio = 0.8 cancel.pressed.connect(_cancel_order.bind(str(order.get("code", "")))) row.add_child(cancel) func _add_order_cell(row: HBoxContainer, value: String, ratio: float) -> void: var label := Label.new() label.text = value label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART label.size_flags_horizontal = Control.SIZE_EXPAND_FILL label.size_flags_stretch_ratio = ratio row.add_child(label) func _cancel_order(code: String) -> void: CCSService.send_batch([{"type": "cancel_order", "code": code, "minutes": 0}]) func _open_time_dialog() -> void: if _is_final_orders(): return time_preset.select(0) custom_target.visible = false _update_time_preview(0) time_dialog.popup_centered(Vector2i(600, 350)) func _update_time_preview(index: int) -> void: var minutes := int(time_preset.get_item_metadata(index)) custom_target.visible = minutes < 0 if minutes < 0: minutes = _custom_target_minutes() time_preview.text = "Clock will advance to %s." % _simulated_time_text(int(CCSService.attempt.get("simulatedMinute", 0)) + maxi(0, minutes)) time_dialog.get_ok_button().disabled = minutes <= 0 func _custom_target_minutes() -> int: var value := custom_target.text.strip_edges().replace(" ", "T") if value.length() == 16: value += ":00" var target := Time.get_unix_time_from_datetime_string(value) if target <= 0: return 0 var start_text := str(CCSService.attempt.get("simulatedStartUtc", "")).trim_suffix("Z") var start_unix := Time.get_unix_time_from_datetime_string(start_text) var current_unix := start_unix + int(CCSService.attempt.get("simulatedMinute", 0)) * 60 return maxi(0, int((target - current_unix) / 60.0)) func _commit_time() -> void: var minutes := int(time_preset.get_item_metadata(time_preset.selected)) if minutes < 0: minutes = _custom_target_minutes() if minutes > 0: CCSService.send_command("advance", "", minutes) func _open_location_dialog() -> void: if _is_final_orders(): return location_option.clear() for raw_location in CCSService.attempt.get("allowedLocations", []): var location := str(raw_location) location_option.add_item(_humanize_code(location)) location_option.set_item_metadata(location_option.item_count - 1, location) location_dialog.get_ok_button().disabled = location_option.item_count == 0 location_dialog.popup_centered(Vector2i(540, 280)) func _commit_location() -> void: if location_option.item_count > 0: CCSService.send_command("location", str(location_option.get_item_metadata(location_option.selected))) func _request_end_case() -> void: if _is_final_orders(): CCSService.send_command("exit_case") else: end_dialog.popup_centered(Vector2i(560, 260)) func leave_case() -> void: abandon_dialog.popup_centered(Vector2i(520, 220)) func _confirm_abandon() -> void: if leaving: return leaving = true CCSService.abandon() func _on_abandoned() -> void: if leaving: leaving = false main.show_screen("ccs_cases_2" if CCSService.launch_origin == "ccs_cases_2" else "ccs_hub") func _render_chart() -> void: for definition in CHART_TABS: var category := str(definition[1]) if category == "orders": continue var label: RichTextLabel = chart_views.get(category) if label == null: continue label.text = _chart_category_bbcode(category) func _chart_category_bbcode(category: String) -> String: if category == "vitals": return _vital_history_bbcode() var entries := _chart_entries(category) if entries.is_empty(): return "[center][color=#8490a5]No %s available.[/color][/center]" % str(_tab_title(category)).to_lower() var text := "" for raw_entry in entries: var entry: Dictionary = raw_entry text += "[b]%s[/b] [color=#8490a5]%s[/color]\n%s\n\n" % [ _escape_bbcode(str(entry.get("title", _tab_title(category)))), _escape_bbcode(_simulated_time_text(int(entry.get("simulatedMinute", 0)), false)), _escape_bbcode(str(entry.get("text", ""))) ] return text func _chart_entries(category: String) -> Array: var result: Array = [] for raw_entry in CCSService.attempt.get("chart", []): var entry: Dictionary = raw_entry var actual := str(entry.get("category", "progress")) if actual == "exam": actual = "progress" elif actual == "results": actual = "other_test" if actual == category: result.append(entry) return result func _vital_history_bbcode() -> String: var snapshots: Array = CCSService.attempt.get("vitalHistory", []) if snapshots.is_empty(): return _vitals_bbcode(CCSService.attempt.get("vitals", {})) var text := "" for raw_snapshot in snapshots: var snapshot: Dictionary = raw_snapshot text += "[b]%s[/b]\n%s\n\n" % [ _escape_bbcode(_simulated_time_text(int(snapshot.get("simulatedMinute", 0)), false)), _vitals_bbcode(snapshot.get("values", {})) ] return text func _vitals_bbcode(vitals: Dictionary) -> String: var lines: Array[String] = [] for key in _ordered_vital_keys(vitals): lines.append("[b]%s:[/b] %s" % [_escape_bbcode(_vital_label(key)), _escape_bbcode(str(vitals[key]))]) return "\n".join(lines) func _history_bbcode() -> String: var sections: Array = CCSService.attempt.get("historySections", []) if sections.is_empty(): return "[b]History of present illness[/b]\n%s\n\n[b]Initial appearance[/b]\n%s" % [ _escape_bbcode(str(CCSService.attempt.get("history", ""))), _escape_bbcode(str(CCSService.attempt.get("appearance", ""))) ] var text := "" for raw_section in sections: var section: Dictionary = raw_section text += "[b]%s[/b]\n%s\n\n" % [_escape_bbcode(str(section.get("title", ""))), _escape_bbcode(str(section.get("text", "")))] return text func _tick() -> void: if CCSService.attempt.is_empty(): return var now := Time.get_unix_time_from_system() var created_text := str(CCSService.attempt.get("createdUtc", "")).trim_suffix("Z") var created := Time.get_unix_time_from_datetime_string(created_text) var elapsed := maxi(0, int(now - created)) var phase_deadline := CCSService.deadline_unix() var hard_deadline := CCSService.hard_deadline_unix() var display_deadline := phase_deadline if _is_final_orders() else hard_deadline var remaining := maxi(0, int(ceil(display_deadline - now))) var phase_remaining := maxi(0, int(ceil(phase_deadline - now))) simulated_clock.text = "Elapsed SIMULATED case time: %s" % _elapsed_simulated_text(int(CCSService.attempt.get("simulatedMinute", 0))) elapsed_clock.text = "Elapsed REAL time: %s" % _duration_text(elapsed) remaining_clock.text = ( "Legacy untimed case" if hard_deadline <= 0 else "%s REAL time: %s" % ["Final-order" if _is_final_orders() else "Remaining", _duration_text(remaining)] ) if phase_remaining == 0 and phase_deadline > 0 and not CCSService.is_loading: CCSService.load_attempt(str(CCSService.attempt.get("id", ""))) func _simulated_time_text(minute: int, include_day: bool = true) -> String: var start_text := str(CCSService.attempt.get("simulatedStartUtc", "")).trim_suffix("Z") var unix := Time.get_unix_time_from_datetime_string(start_text) if unix <= 0: return "Minute %d" % minute var value := Time.get_datetime_dict_from_unix_time(unix + minute * 60) var hour := int(value.hour) var suffix := "AM" if hour < 12 else "PM" var display_hour := hour % 12 if display_hour == 0: display_hour = 12 var day_number := 1 + int(floor(float(minute) / 1440.0)) return "%s%d:%02d %s" % ["Day %d @ " % day_number if include_day else "", display_hour, int(value.minute), suffix] func _elapsed_simulated_text(minutes: int) -> String: @warning_ignore("integer_division") return "%d Days %02d Hrs %02d Mins" % [minutes / 1440, (minutes % 1440) / 60, minutes % 60] func _duration_text(seconds: int) -> String: @warning_ignore("integer_division") return "%02d:%02d" % [seconds / 60, seconds % 60] func _ordered_vital_keys(vitals: Dictionary) -> Array[String]: var ordered: Array[String] = [] for key in ["temperature", "bloodPressure", "pulse", "respirations", "oxygenSaturation", "height", "weight", "bmi"]: if vitals.has(key): ordered.append(key) var remaining: Array[String] = [] for raw_key in vitals.keys(): var key := str(raw_key) if not key in ordered: remaining.append(key) remaining.sort() ordered.append_array(remaining) return ordered func _vital_label(key: String) -> String: match key: "bloodPressure": return "Blood Pressure" "oxygenSaturation": return "Oxygen Saturation" "bmi": return "Body Mass Index" _: return _humanize_code(key) func _clinical_text(region: String) -> RichTextLabel: var label := RichTextLabel.new() label.bbcode_enabled = true label.selection_enabled = true label.fit_content = true label.scroll_active = false label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART label.add_theme_font_override("normal_font", Brand.font) label.add_theme_font_override("bold_font", Brand.font) label.set_meta("ccs_base_font_size", 16) label.set_meta("ccs_region", region) label.gui_input.connect(_text_input.bind(label)) clinical_labels.append(label) return label func _text_input(event: InputEvent, label: RichTextLabel) -> void: if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT and not event.pressed: call_deferred("_commit_selection", label) func _commit_selection(label: RichTextLabel) -> void: var text := label.get_selected_text() if text.is_empty(): return var source := label.get_parsed_text() var start := source.find(text) if start >= 0: CCSService.toggle_highlight(str(CCSService.attempt.get("id", "")), str(label.get_meta("ccs_region", "")), start, start + text.length()) func _zoom(delta: float) -> void: CCSService.adjust_text_scale(delta) _apply_text_scale() func _apply_text_scale() -> void: for label in clinical_labels: var font_size := maxi(12, int(round(float(label.get_meta("ccs_base_font_size", 16)) * CCSService.text_scale))) label.add_theme_font_size_override("normal_font_size", font_size) label.add_theme_font_size_override("bold_font_size", font_size) func _report() -> void: if report_dialog != null: report_dialog.open(CCSService.attempt) func _report_submitted(_id: String) -> void: _set_status("Case report submitted.", false) func _request_failed(message: String) -> void: if visible: awaiting_exam_result = false _set_status(message) func _set_status(message: String, is_error: bool = true) -> void: status.text = message status.visible = not message.is_empty() status.add_theme_color_override("font_color", Color("ff6b7f") if is_error else Brand.SUCCESS) func _catalog_name(code: String) -> String: if staged_order_names.has(code): return str(staged_order_names[code]) for raw_order in CCSService.orders: var order: Dictionary = raw_order if str(order.get("code", "")) == code: return str(order.get("canonicalName", _humanize_code(code))) return _humanize_code(code) func _tab_title(category: String) -> String: for definition in CHART_TABS: if str(definition[1]) == category: return str(definition[0]) return _humanize_code(category) func _humanize_code(value: String) -> String: return value.replace("_", " ").capitalize() func _escape_bbcode(value: String) -> String: return value.replace("[", "[lb]").replace("]", "[rb]") func _is_final_orders() -> bool: return str(CCSService.attempt.get("state", "")) == "final_orders" func _clear(parent: Node) -> void: for child in parent.get_children(): child.queue_free() func _update_responsive_layout() -> void: if action_grid == null: return var narrow := size.x > 0 and size.x < RESPONSIVE_BREAKPOINT header_layout.vertical = narrow action_grid.columns = 2 if narrow else 4 toolbar.size_flags_horizontal = Control.SIZE_EXPAND_FILL if narrow else Control.SIZE_SHRINK_END for button in action_buttons: button.custom_minimum_size.x = 0 if narrow else 220 queue_sort() func _on_theme_changed(_dark: bool) -> void: call_deferred("_apply_theme") func _apply_theme() -> void: if header_panel == null: return var dark: bool = theme_service != null and theme_service.is_dark_mode() header_panel.add_theme_stylebox_override("panel", Brand.content_card_box(dark, false)) workspace.add_theme_stylebox_override("panel", Brand.content_card_box(dark, false)) heading.add_theme_color_override("font_color", Brand.text_color(dark)) allotted_label.add_theme_color_override("font_color", Brand.ROYAL_BLUE if not dark else Brand.SKY_BLUE) for label in [simulated_clock, elapsed_clock, remaining_clock]: label.add_theme_color_override("font_color", Brand.text_color(dark)) final_orders_notice.add_theme_color_override("font_color", Brand.AMBER) for button in toolbar_buttons + action_buttons: button.add_theme_font_override("font", Brand.font) button.add_theme_color_override("font_color", Brand.text_color(dark)) button.add_theme_color_override("font_hover_color", Brand.text_color(dark)) button.add_theme_stylebox_override("normal", Brand.content_card_box(dark, false)) button.add_theme_stylebox_override("hover", Brand.content_card_box(dark, true)) for label in clinical_labels: label.add_theme_color_override("default_color", Brand.text_color(dark)) _apply_dialog_theme(dark) Brand.set_theme_toggle_icon(theme_button, dark) theme_button.tooltip_text = "Switch to light mode" if dark else "Switch to dark mode" _apply_text_scale() func _apply_dialog_theme(dark: bool) -> void: var popup_theme := Theme.new() var text := Brand.text_color(dark) var muted := Brand.muted_color(dark) var panel := Brand.content_card_box(dark, false) panel.content_margin_left = 18 panel.content_margin_right = 18 panel.content_margin_top = 16 panel.content_margin_bottom = 16 for type_name in ["Label", "Button", "CheckBox", "OptionButton", "LineEdit", "PopupMenu"]: popup_theme.set_font("font", type_name, Brand.font) popup_theme.set_color("font_color", type_name, text) popup_theme.set_font("normal_font", "RichTextLabel", Brand.font) popup_theme.set_font("bold_font", "RichTextLabel", Brand.font) popup_theme.set_color("default_color", "RichTextLabel", text) popup_theme.set_color("font_selected_color", "RichTextLabel", text) popup_theme.set_color("font_hover_color", "Button", text) popup_theme.set_color("font_pressed_color", "Button", text) popup_theme.set_color("font_disabled_color", "Button", muted) popup_theme.set_color("font_hover_color", "OptionButton", text) popup_theme.set_color("font_pressed_color", "OptionButton", text) popup_theme.set_color("font_hover_color", "CheckBox", text) popup_theme.set_color("font_pressed_color", "CheckBox", text) popup_theme.set_color("font_hover_color", "PopupMenu", text) popup_theme.set_color("font_accelerator_color", "PopupMenu", muted) popup_theme.set_color("font_placeholder_color", "LineEdit", muted) popup_theme.set_color("title_color", "Window", text) popup_theme.set_font("title_font", "Window", Brand.font) for type_name in ["Button", "OptionButton"]: popup_theme.set_stylebox("normal", type_name, Brand.secondary_button_box(dark)) popup_theme.set_stylebox("hover", type_name, Brand.secondary_button_box(dark, true)) popup_theme.set_stylebox("pressed", type_name, Brand.secondary_button_box(dark, true)) popup_theme.set_stylebox("normal", "LineEdit", Brand.field_box(dark)) popup_theme.set_stylebox("focus", "LineEdit", Brand.field_box(dark, true)) for type_name in ["Window", "AcceptDialog", "ConfirmationDialog"]: popup_theme.set_stylebox("panel", type_name, panel) popup_theme.set_stylebox("panel", "PopupMenu", panel) popup_theme.set_stylebox("hover", "PopupMenu", Brand.secondary_button_box(dark, true)) for dialog in _ccs_dialogs(): dialog.theme = popup_theme