Files
step3/scripts/ui/dashboard.gd
T
2026-07-11 02:05:43 -05:00

161 lines
5.4 KiB
GDScript

extends VBoxContainer
const Style := preload("res://scripts/ui/ui_style.gd")
var main: Control
var status_label: Label
var daily_label: Label
var elo_label: Label
var streak_label: Label
var bank_label: Label
var error_label: Label
var begin_button: Button
var retry_button: Button
func _ready() -> void:
set_anchors_preset(Control.PRESET_FULL_RECT)
add_theme_constant_override("separation", 18)
if not QuestionBankService.questions_changed.is_connected(_refresh):
QuestionBankService.questions_changed.connect(_refresh)
if not QuestionBankService.ratings_changed.is_connected(_refresh):
QuestionBankService.ratings_changed.connect(_refresh)
if not ProfileService.profile_changed.is_connected(_refresh):
ProfileService.profile_changed.connect(_refresh)
var title := Label.new()
title.text = "Core Mode"
Style.set_label(title, 36, Style.TEXT)
add_child(title)
var subtitle := Label.new()
subtitle.text = "Begin a continuous STEP 3 question run. Keep your energy up, build score, and stay on pace."
subtitle.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
Style.set_label(subtitle, 17, Style.MUTED)
add_child(subtitle)
var summary := HBoxContainer.new()
summary.add_theme_constant_override("separation", 14)
add_child(summary)
daily_label = _add_metric(summary, "Today")
elo_label = _add_metric(summary, "ELO")
streak_label = _add_metric(summary, "Streak")
bank_label = _add_metric(summary, "Question Bank")
var action_panel := PanelContainer.new()
action_panel.add_theme_stylebox_override("panel", Style.panel_box(Color("172337"), Color("405475")))
action_panel.size_flags_horizontal = Control.SIZE_EXPAND_FILL
add_child(action_panel)
var actions := VBoxContainer.new()
actions.add_theme_constant_override("separation", 12)
action_panel.add_child(actions)
status_label = Label.new()
status_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
Style.set_label(status_label, 22, Style.CYAN)
actions.add_child(status_label)
error_label = Label.new()
error_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
Style.set_label(error_label, 15, Style.RED)
actions.add_child(error_label)
var button_row := HBoxContainer.new()
button_row.add_theme_constant_override("separation", 12)
actions.add_child(button_row)
begin_button = Button.new()
begin_button.text = "Begin"
begin_button.custom_minimum_size = Vector2(260, 60)
Style.style_primary_button(begin_button)
begin_button.pressed.connect(_begin_core_mode)
button_row.add_child(begin_button)
retry_button = Button.new()
retry_button.text = "Retry Questions"
retry_button.custom_minimum_size = Vector2(170, 54)
Style.style_chip(retry_button, Style.AMBER)
retry_button.pressed.connect(QuestionBankService.retry_now)
button_row.add_child(retry_button)
var spacer := Control.new()
spacer.size_flags_vertical = Control.SIZE_EXPAND_FILL
add_child(spacer)
func on_show(_payload: Variant = null) -> void:
if QuestionBankService.load_state == "idle" and AuthState.is_logged_in:
QuestionBankService.load_questions_from_api()
_refresh()
func _add_metric(parent: HBoxContainer, metric_name: String) -> Label:
var panel := PanelContainer.new()
panel.add_theme_stylebox_override("panel", Style.panel_box(Style.SURFACE, Color("33415c")))
panel.size_flags_horizontal = Control.SIZE_EXPAND_FILL
parent.add_child(panel)
var label := Label.new()
label.text = metric_name
label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
Style.set_label(label, 18, Style.TEXT)
panel.add_child(label)
return label
func _refresh() -> void:
if daily_label == null:
return
var profile := ProfileService.get_profile()
var answered := int(profile.get("daily_questions_answered", 0))
var goal := int(profile.get("daily_goal_questions", 10))
if goal < 1:
goal = 1
var progress := int(round(float(answered) / float(goal) * 100.0))
if progress > 100:
progress = 100
daily_label.text = "Today\n%d/%d questions\n%d%% complete" % [answered, goal, progress]
elo_label.text = "ELO\n%d overall\n%d rated" % [
QuestionBankService.overall_elo,
QuestionBankService.rated_question_count
]
streak_label.text = "Streak\n%d current\n%d best" % [
int(profile.get("current_streak", 0)),
int(profile.get("best_streak", 0))
]
bank_label.text = "Question Bank\n%d available\n%s" % [
QuestionBankService.get_all_questions().size(),
QuestionBankService.load_state.capitalize()
]
var state := QuestionBankService.load_state
var has_questions := QuestionBankService.has_questions()
begin_button.disabled = not has_questions
retry_button.disabled = state == "loading"
retry_button.visible = state != "ready"
error_label.text = ""
if state == "ready":
status_label.text = BlockSessionService.core_status_message if BlockSessionService.core_status_message != "" else "Question bank ready. Begin core mode when you are ready."
elif state == "loading":
status_label.text = "Loading question bank..."
elif state == "retrying":
status_label.text = "Question API unavailable. Retrying in %d seconds." % QuestionBankService.next_retry_seconds
error_label.text = QuestionBankService.last_error
elif state == "failed":
status_label.text = "Question bank is not available."
error_label.text = QuestionBankService.last_error
else:
status_label.text = "Question bank has not loaded yet."
func _begin_core_mode() -> void:
if not QuestionBankService.has_questions():
return
if BlockSessionService.start_core_run():
main.show_screen("player")
else:
_refresh()