TradeSkillMaster/Core/UI/MainUI/Dashboard.lua

937 lines
31 KiB
Lua
Raw Normal View History

2020-11-13 14:13:12 -05:00
-- ------------------------------------------------------------------------------ --
-- TradeSkillMaster --
-- https://tradeskillmaster.com --
-- All Rights Reserved - Detailed license information included with addon. --
-- ------------------------------------------------------------------------------ --
local _, TSM = ...
local Dashboard = TSM.MainUI:NewPackage("Dashboard")
local L = TSM.Include("Locale").GetTable()
local TempTable = TSM.Include("Util.TempTable")
local Money = TSM.Include("Util.Money")
local Math = TSM.Include("Util.Math")
local Theme = TSM.Include("Util.Theme")
local Analytics = TSM.Include("Util.Analytics")
local Settings = TSM.Include("Service.Settings")
local UIElements = TSM.Include("UI.UIElements")
local private = {
settings = nil,
characterGuilds = {},
tempTimeTable = {},
selectedTimeRange = nil,
}
local SECONDS_PER_DAY = 60 * 60 * 24
local MIN_GRAPH_STEP_SIZE = TSM.IsWowClassic() and COPPER_PER_GOLD or (COPPER_PER_GOLD * 1000)
local TIME_RANGE_LOOKUP = {
["1d"] = SECONDS_PER_DAY,
["1w"] = SECONDS_PER_DAY * 7,
["1m"] = SECONDS_PER_DAY * 30,
["3m"] = SECONDS_PER_DAY * 91,
["6m"] = SECONDS_PER_DAY * 183,
["1y"] = SECONDS_PER_DAY * 365,
["2y"] = SECONDS_PER_DAY * 730,
["all"] = -1,
}
-- ============================================================================
-- Module Functions
-- ============================================================================
function Dashboard.OnInitialize()
private.settings = Settings.NewView()
:AddKey("global", "mainUIContext", "dashboardDividedContainer")
:AddKey("global", "mainUIContext", "dashboardUnselectedCharacters")
:AddKey("global", "mainUIContext", "dashboardTimeRange")
private.selectedTimeRange = private.settings.dashboardTimeRange
TSM.MainUI.RegisterTopLevelPage(L["Dashboard"], private.GetDashboardFrame)
end
-- ============================================================================
-- Dashboard UI
-- ============================================================================
function private.GetDashboardFrame()
TSM.UI.AnalyticsRecordPathChange("main", "dashboard")
private.selectedTimeRange = private.settings.dashboardTimeRange
wipe(private.characterGuilds)
local prevUnselectedCharacters = TempTable.Acquire()
for characterGuild in pairs(private.settings.dashboardUnselectedCharacters) do
prevUnselectedCharacters[characterGuild] = true
end
wipe(private.settings.dashboardUnselectedCharacters)
for characterGuild in TSM.Accounting.GoldTracker.CharacterGuildIterator() do
tinsert(private.characterGuilds, characterGuild)
private.settings.dashboardUnselectedCharacters[characterGuild] = prevUnselectedCharacters[characterGuild] or nil
end
TempTable.Release(prevUnselectedCharacters)
local frame = UIElements.New("DividedContainer", "dashboard")
:SetSettingsContext(private.settings, "dashboardDividedContainer")
:SetMinWidth(200, 407)
:SetBackgroundColor("PRIMARY_BG")
:SetLeftChild(UIElements.New("Frame", "news")
:SetLayout("VERTICAL")
:SetBackgroundColor("PRIMARY_BG")
:AddChild(UIElements.New("Text", "text")
:SetSize("AUTO", 24)
:SetMargin(8)
:SetFont("BODY_BODY1_BOLD")
:SetText(L["News & Information"])
)
:AddChild(UIElements.New("ScrollFrame", "content")
:SetPadding(8, 8, 0, 0)
)
)
:SetRightChild(UIElements.New("Frame", "content")
:SetLayout("VERTICAL")
:SetPadding(8)
:SetBackgroundColor("PRIMARY_BG")
:SetScript("OnUpdate", private.ContentOnUpdate)
:AddChild(UIElements.New("Frame", "goldHeader")
:SetLayout("HORIZONTAL")
:SetHeight(24)
:AddChild(UIElements.New("Text", "text")
:SetWidth("AUTO")
:SetMargin(0, 8, 0, 0)
:SetFont("BODY_BODY1_BOLD")
:SetText(L["Player Gold"])
)
:AddChild(UIElements.New("MultiselectionDropdown", "playerDropdown")
:SetSize(157, 22)
:SetItems(private.characterGuilds, private.characterGuilds)
:SetUnselectedItemKeys(private.settings.dashboardUnselectedCharacters)
:SetSelectionText(L["No Players"], L["%d Players"], L["All Players"])
:SetScript("OnSelectionChanged", private.DropdownOnSelectionChanged)
)
:AddChild(UIElements.New("Spacer"))
:AddChild(UIElements.New("Text", "hoverTime")
:SetWidth("AUTO")
:SetFont("BODY_BODY3_MEDIUM")
:SetText("")
)
:AddChild(UIElements.New("Frame", "timeBtns")
:SetLayout("HORIZONTAL")
:AddChild(UIElements.New("Button", "1d")
:SetMargin(8, 0, 0, 0)
:SetSize(20, 20)
:SetFont("TABLE_TABLE1")
:SetTextColor("ACTIVE_BG_ALT")
:SetContext(TIME_RANGE_LOOKUP["1d"])
:SetText(L["1D"])
:SetScript("OnClick", private.TimeBtnOnClick)
)
:AddChild(UIElements.New("Button", "1w")
:SetMargin(8, 0, 0, 0)
:SetSize(20, 20)
:SetFont("TABLE_TABLE1")
:SetTextColor("ACTIVE_BG_ALT")
:SetContext(TIME_RANGE_LOOKUP["1w"])
:SetText(L["1W"])
:SetScript("OnClick", private.TimeBtnOnClick)
)
:AddChild(UIElements.New("Button", "1m")
:SetMargin(8, 0, 0, 0)
:SetSize(20, 20)
:SetFont("TABLE_TABLE1")
:SetTextColor("ACTIVE_BG_ALT")
:SetContext(TIME_RANGE_LOOKUP["1m"])
:SetText(L["1M"])
:SetScript("OnClick", private.TimeBtnOnClick)
)
:AddChild(UIElements.New("Button", "3m")
:SetMargin(8, 0, 0, 0)
:SetSize(20, 20)
:SetFont("TABLE_TABLE1")
:SetTextColor("ACTIVE_BG_ALT")
:SetContext(TIME_RANGE_LOOKUP["3m"])
:SetText(L["3M"])
:SetScript("OnClick", private.TimeBtnOnClick)
)
:AddChild(UIElements.New("Button", "6m")
:SetMargin(8, 0, 0, 0)
:SetSize(20, 20)
:SetFont("TABLE_TABLE1")
:SetTextColor("ACTIVE_BG_ALT")
:SetContext(TIME_RANGE_LOOKUP["6m"])
:SetText(L["6M"])
:SetScript("OnClick", private.TimeBtnOnClick)
)
:AddChild(UIElements.New("Button", "1y")
:SetMargin(8, 0, 0, 0)
:SetSize(20, 20)
:SetFont("TABLE_TABLE1")
:SetTextColor("ACTIVE_BG_ALT")
:SetContext(TIME_RANGE_LOOKUP["1y"])
:SetText(L["1Y"])
:SetScript("OnClick", private.TimeBtnOnClick)
)
:AddChild(UIElements.New("Button", "2y")
:SetMargin(8, 0, 0, 0)
:SetSize(20, 20)
:SetFont("TABLE_TABLE1")
:SetTextColor("ACTIVE_BG_ALT")
:SetContext(TIME_RANGE_LOOKUP["2y"])
:SetText(L["2Y"])
:SetScript("OnClick", private.TimeBtnOnClick)
)
:AddChild(UIElements.New("Button", "all")
:SetMargin(8, 0, 0, 0)
:SetSize(20, 20)
:SetFont("TABLE_TABLE1")
:SetTextColor("TEXT")
:SetContext(TIME_RANGE_LOOKUP["all"])
:SetText(ALL)
:SetScript("OnClick", private.TimeBtnOnClick)
)
:AddChild(UIElements.New("Button", "resetZoom")
:SetMargin(8, 0, 0, 0)
:SetSize(100, 20)
:SetFont("TABLE_TABLE1")
:SetTextColor("TEXT")
:SetContext(TIME_RANGE_LOOKUP["all"])
:SetText(L["Reset Zoom"])
:SetScript("OnClick", private.TimeBtnOnClick)
)
)
)
:AddChild(UIElements.New("Graph", "goldGraph")
:SetMargin(0, 0, 8, 8)
:SetAxisStepFunctions(private.GraphXStepFunc, private.GraphYStepFunc)
:SetXRange(TSM.Accounting.GoldTracker.GetGraphTimeRange(private.settings.dashboardUnselectedCharacters))
:SetYValueFunction(private.GetGraphYValue)
:SetFormatFunctions(private.GraphFormatX, private.GraphFormatY)
:SetScript("OnZoomChanged", private.GraphOnZoomChanged)
:SetScript("OnHoverUpdate", private.GraphOnHoverUpdate)
)
:AddChild(UIElements.New("Frame", "summary")
:SetLayout("HORIZONTAL")
:SetHeight(48)
:SetBackgroundColor("PRIMARY_BG_ALT", true)
:AddChild(UIElements.New("Frame", "range")
:SetLayout("VERTICAL")
:SetPadding(8, 8, 2, 2)
:AddChild(UIElements.New("Frame", "high")
:SetLayout("HORIZONTAL")
:SetHeight(20)
:SetMargin(0, 0, 0, 4)
:AddChild(UIElements.New("Text", "label")
:SetWidth("AUTO")
:SetFont("TABLE_TABLE1")
:SetTextColor("ACTIVE_BG_ALT")
:SetText(L["HIGH"])
)
:AddChild(UIElements.New("Spacer", "spacer"))
:AddChild(UIElements.New("Text", "value")
:SetWidth("AUTO")
:SetFont("TABLE_TABLE1")
:SetJustifyH("RIGHT")
)
)
:AddChild(UIElements.New("Frame", "low")
:SetLayout("HORIZONTAL")
:SetHeight(20)
:AddChild(UIElements.New("Text", "label")
:SetWidth("AUTO")
:SetFont("TABLE_TABLE1")
:SetTextColor("ACTIVE_BG_ALT")
:SetText(L["LOW"])
)
:AddChild(UIElements.New("Spacer", "spacer"))
:AddChild(UIElements.New("Text", "value")
:SetWidth("AUTO")
:SetFont("TABLE_TABLE1")
:SetJustifyH("RIGHT")
)
)
)
:AddChild(UIElements.New("Texture", "line1")
:SetWidth(1)
:SetTexture("ACTIVE_BG")
)
:AddChild(UIElements.New("Frame", "daily")
:SetLayout("VERTICAL")
:SetPadding(8, 8, 2, 2)
:AddChild(UIElements.New("Frame", "sales")
:SetLayout("HORIZONTAL")
:SetHeight(20)
:SetMargin(0, 0, 0, 4)
:AddChild(UIElements.New("Text", "label")
:SetWidth("AUTO")
:SetFont("TABLE_TABLE1")
:SetTextColor("ACTIVE_BG_ALT")
:SetText(L["DAILY SALES"])
)
:AddChild(UIElements.New("Spacer", "spacer"))
:AddChild(UIElements.New("Text", "value")
:SetWidth("AUTO")
:SetFont("TABLE_TABLE1")
:SetJustifyH("RIGHT")
)
)
:AddChild(UIElements.New("Frame", "purchases")
:SetLayout("HORIZONTAL")
:SetHeight(20)
:AddChild(UIElements.New("Text", "label")
:SetWidth("AUTO")
:SetFont("TABLE_TABLE1")
:SetTextColor("ACTIVE_BG_ALT")
:SetText(L["DAILY PURCHASES"])
)
:AddChild(UIElements.New("Spacer", "spacer"))
:AddChild(UIElements.New("Text", "value")
:SetWidth("AUTO")
:SetFont("TABLE_TABLE1")
:SetJustifyH("RIGHT")
)
)
)
:AddChild(UIElements.New("Texture", "line2")
:SetWidth(1)
:SetTexture("ACTIVE_BG")
)
:AddChild(UIElements.New("Frame", "top")
:SetLayout("VERTICAL")
:SetPadding(8, 8, 2, 2)
:AddChild(UIElements.New("Frame", "sale")
:SetLayout("HORIZONTAL")
:SetHeight(20)
:SetMargin(0, 0, 0, 4)
:AddChild(UIElements.New("Text", "label")
:SetWidth("AUTO")
:SetFont("TABLE_TABLE1")
:SetTextColor("ACTIVE_BG_ALT")
:SetText(L["TOP SALE"])
)
:AddChild(UIElements.New("Spacer", "spacer"))
:AddChild(UIElements.New("Text", "value")
:SetWidth("AUTO")
:SetFont("TABLE_TABLE1")
:SetJustifyH("RIGHT")
)
)
:AddChild(UIElements.New("Frame", "expense")
:SetLayout("HORIZONTAL")
:SetHeight(20)
:AddChild(UIElements.New("Text", "label")
:SetWidth("AUTO")
:SetFont("TABLE_TABLE1")
:SetTextColor("ACTIVE_BG_ALT")
:SetText(L["TOP PURCHASE"])
)
:AddChild(UIElements.New("Spacer", "spacer"))
:AddChild(UIElements.New("Text", "value")
:SetWidth("AUTO")
:SetFont("TABLE_TABLE1")
:SetJustifyH("RIGHT")
)
)
)
)
:AddChild(UIElements.New("Frame", "details")
:SetLayout("VERTICAL")
:SetMargin(0, 0, 8, 0)
:SetPadding(8)
:SetBackgroundColor("PRIMARY_BG_ALT", true)
:AddChild(UIElements.New("Text", "salesLabel")
:SetHeight(20)
:SetFont("TABLE_TABLE1")
:SetTextColor("ACTIVE_BG_ALT")
:SetText(L["SALES"])
)
:AddChild(UIElements.New("Frame", "salesTotal")
:SetLayout("HORIZONTAL")
:SetHeight(20)
:AddChild(UIElements.New("Text", "text")
:SetWidth("AUTO")
:SetFont("BODY_BODY3_MEDIUM")
:SetText(L["Total Gold Earned"]..":")
)
:AddChild(UIElements.New("Spacer", "spacer"))
:AddChild(UIElements.New("Text", "amount")
:SetWidth("AUTO")
:SetFont("TABLE_TABLE1")
:SetJustifyH("RIGHT")
)
)
:AddChild(UIElements.New("Frame", "salesAvg")
:SetLayout("HORIZONTAL")
:SetHeight(20)
:SetMargin(0, 0, 4, 0)
:AddChild(UIElements.New("Text", "label")
:SetWidth("AUTO")
:SetFont("BODY_BODY3_MEDIUM")
:SetText(L["Average Earned per Day"]..":")
)
:AddChild(UIElements.New("Spacer", "spacer"))
:AddChild(UIElements.New("Text", "amount")
:SetWidth("AUTO")
:SetFont("TABLE_TABLE1")
:SetJustifyH("RIGHT")
)
)
:AddChild(UIElements.New("Frame", "salesTop")
:SetLayout("HORIZONTAL")
:SetHeight(20)
:SetMargin(0, 0, 4, 0)
:AddChild(UIElements.New("Text", "label")
:SetWidth("AUTO")
:SetFont("BODY_BODY3_MEDIUM")
:SetText(L["Top Item"]..":")
)
:AddChild(UIElements.New("Spacer", "spacer"))
:AddChild(UIElements.New("Button", "item")
:SetWidth("AUTO")
:SetFont("ITEM_BODY3")
:SetJustifyH("RIGHT")
)
)
:AddChild(UIElements.New("Texture", "line1")
:SetHeight(1)
:SetMargin(-8, -8, 4, 4)
:SetTexture("ACTIVE_BG")
)
:AddChild(UIElements.New("Text", "expensesLabel")
:SetHeight(20)
:SetFont("TABLE_TABLE1")
:SetTextColor("ACTIVE_BG_ALT")
:SetText(L["EXPENSES"])
)
:AddChild(UIElements.New("Frame", "expensesTotal")
:SetLayout("HORIZONTAL")
:SetHeight(20)
:AddChild(UIElements.New("Text", "text")
:SetWidth("AUTO")
:SetFont("BODY_BODY3_MEDIUM")
:SetText(L["Total Gold Spent"]..":")
)
:AddChild(UIElements.New("Spacer", "spacer"))
:AddChild(UIElements.New("Text", "amount")
:SetWidth("AUTO")
:SetFont("TABLE_TABLE1")
:SetJustifyH("RIGHT")
)
)
:AddChild(UIElements.New("Frame", "expensesAvg")
:SetLayout("HORIZONTAL")
:SetHeight(20)
:SetMargin(0, 0, 4, 0)
:AddChild(UIElements.New("Text", "label")
:SetWidth("AUTO")
:SetFont("BODY_BODY3_MEDIUM")
:SetText(L["Average Spent per Day"]..":")
)
:AddChild(UIElements.New("Spacer", "spacer"))
:AddChild(UIElements.New("Text", "amount")
:SetWidth("AUTO")
:SetFont("TABLE_TABLE1")
:SetJustifyH("RIGHT")
)
)
:AddChild(UIElements.New("Frame", "expensesTop")
:SetLayout("HORIZONTAL")
:SetHeight(20)
:SetMargin(0, 0, 4, 0)
:AddChild(UIElements.New("Text", "label")
:SetWidth("AUTO")
:SetFont("BODY_BODY3_MEDIUM")
:SetText(L["Top Item"]..":")
)
:AddChild(UIElements.New("Spacer", "spacer"))
:AddChild(UIElements.New("Button", "item")
:SetWidth("AUTO")
:SetFont("ITEM_BODY3")
:SetJustifyH("RIGHT")
)
)
:AddChild(UIElements.New("Texture", "line2")
:SetHeight(1)
:SetMargin(-8, -8, 4, 4)
:SetTexture("ACTIVE_BG")
)
:AddChild(UIElements.New("Text", "profitLabel")
:SetHeight(20)
:SetFont("TABLE_TABLE1")
:SetTextColor("ACTIVE_BG_ALT")
:SetText(L["PROFIT"])
)
:AddChild(UIElements.New("Frame", "profitTotal")
:SetLayout("HORIZONTAL")
:SetHeight(20)
:AddChild(UIElements.New("Text", "text")
:SetWidth("AUTO")
:SetFont("BODY_BODY3_MEDIUM")
:SetText(L["Total Profit"]..":")
)
:AddChild(UIElements.New("Spacer", "spacer"))
:AddChild(UIElements.New("Text", "amount")
:SetWidth("AUTO")
:SetFont("TABLE_TABLE1")
:SetJustifyH("RIGHT")
)
)
:AddChild(UIElements.New("Frame", "profitAvg")
:SetLayout("HORIZONTAL")
:SetHeight(20)
:SetMargin(0, 0, 4, 0)
:AddChild(UIElements.New("Text", "label")
:SetWidth("AUTO")
:SetFont("BODY_BODY3_MEDIUM")
:SetText(L["Average Profit per Day"]..":")
)
:AddChild(UIElements.New("Spacer", "spacer"))
:AddChild(UIElements.New("Text", "amount")
:SetWidth("AUTO")
:SetFont("TABLE_TABLE1")
:SetJustifyH("RIGHT")
)
)
:AddChild(UIElements.New("Frame", "profitTop")
:SetLayout("HORIZONTAL")
:SetHeight(20)
:SetMargin(0, 0, 4, 0)
:AddChild(UIElements.New("Text", "label")
:SetWidth("AUTO")
:SetFont("BODY_BODY3_MEDIUM")
:SetText(L["Top Item"]..":")
)
:AddChild(UIElements.New("Spacer", "spacer"))
:AddChild(UIElements.New("Button", "item")
:SetWidth("AUTO")
:SetFont("ITEM_BODY3")
:SetJustifyH("RIGHT")
)
)
)
)
frame:GetElement("content.goldHeader.timeBtns.resetZoom"):Hide()
frame:GetElement("content.goldHeader.hoverTime"):Hide()
local newsContent = frame:GetElement("news.content")
local newsEntries = TSM.GetAppNews()
if newsEntries then
for i, info in ipairs(newsEntries) do
newsContent:AddChild(UIElements.New("Frame", "news"..i)
:SetLayout("VERTICAL")
:SetPadding(0, 0, i == 1 and 6 or 12, 0)
:AddChild(UIElements.New("Text", "date")
:SetHeight(20)
:SetFont("BODY_BODY3")
:SetText(date("%b %d, %Y", info.timestamp))
)
:AddChild(UIElements.New("Text", "title")
:SetHeight(20)
:SetFont("BODY_BODY2_BOLD")
:SetText(info.title)
)
:AddChild(UIElements.New("Text", "content")
:SetHeight(80)
:SetPadding(0, 0, 4, 0)
:SetFont("BODY_BODY3")
:SetText(info.content)
)
:AddChild(UIElements.New("Text", "readMore")
:SetHeight(20)
:SetPadding(0, 0, 4, 0)
:SetFont("BODY_BODY3")
:SetTextColor("INDICATOR")
:SetText(L["Read More"])
)
)
:AddChildNoLayout(UIElements.New("Button", "btn")
:AddAnchor("TOPLEFT", "news"..i)
:AddAnchor("BOTTOMRIGHT", "news"..i)
:SetContext(info)
:SetScript("OnClick", private.ButtonOnClick)
)
end
end
return frame
end
-- ============================================================================
-- Local Script Handlers
-- ============================================================================
function private.ButtonOnClick(button)
local info = button:GetContext()
Analytics.Action("NEWS_READ_MORE", info.title)
button:GetBaseElement():ShowDialogFrame(UIElements.New("Frame", "frame")
:SetLayout("VERTICAL")
:SetSize(600, 450)
:AddAnchor("CENTER")
:SetBackgroundColor("FRAME_BG")
:SetBorderColor("ACTIVE_BG")
:AddChild(UIElements.New("Text", "title")
:SetHeight(44)
:SetMargin(16, 16, 16, 8)
:SetFont("BODY_BODY1_BOLD")
:SetJustifyH("CENTER")
:SetText(info.title)
)
:AddChild(UIElements.New("Input", "linkInput")
:SetHeight(26)
:SetMargin(16, 16, 0, 16)
:SetBackgroundColor("PRIMARY_BG_ALT")
:SetValidateFunc(private.LinkValidateFunc)
:SetContext(info.link)
:SetValue(info.link)
)
:AddChild(UIElements.New("Text", "content")
:SetMargin(16, 16, 0, 16)
:SetFont("BODY_BODY3")
:SetJustifyV("TOP")
:SetText(info.content)
)
:AddChild(UIElements.New("Frame", "buttons")
:SetLayout("HORIZONTAL")
:SetHeight(26)
:SetMargin(16, 16, 0, 16)
:AddChild(UIElements.New("Spacer", "spacer"))
:AddChild(UIElements.New("ActionButton", "confirmBtn")
:SetWidth(126)
:SetText(CLOSE)
:SetScript("OnClick", private.DialogCloseBtnOnClick)
)
)
)
end
function private.LinkValidateFunc(input, value)
return value == input:GetContext()
end
function private.DialogCloseBtnOnClick(button)
button:GetBaseElement():HideDialog()
end
-- ============================================================================
-- Private Helper Functions
-- ============================================================================
function private.GraphFormatX(timestamp, suggestedStep)
if suggestedStep > SECONDS_PER_DAY * 14 then
return date("%b '%y", timestamp)
elseif suggestedStep > SECONDS_PER_DAY * 2 then
return date("%b %d", timestamp)
elseif suggestedStep > SECONDS_PER_DAY / 6 then
return date("%a", timestamp)
else
if GetCVar("timeMgrUseMilitaryTime") == "1" then
return date("%H:%M", timestamp)
else
return strtrim(date("%I %p", timestamp), "0")
end
end
end
function private.GraphFormatY(value, suggestedStep, isTooltip)
if isTooltip then
return Money.ToString(value, nil, "OPT_TRIM")
end
if TSM.IsWowClassic() and value < COPPER_PER_GOLD * 1000 then
-- "###g"
return floor(value / COPPER_PER_GOLD)..Money.GetGoldText()
elseif TSM.IsWowClassic() and value < COPPER_PER_GOLD * 1000 * 10 then
-- "#.##Kg"
return format("%.2f", value / (COPPER_PER_GOLD * 1000)).."k"..Money.GetGoldText()
elseif value < COPPER_PER_GOLD * 1000 * 1000 then
-- "###Kg"
return floor(value / (COPPER_PER_GOLD * 1000)).."k"..Money.GetGoldText()
elseif value < COPPER_PER_GOLD * 1000 * 1000 * 10 then
-- "#.##Mg"
return format("%.2f", value / (COPPER_PER_GOLD * 1000 * 1000)).."M"..Money.GetGoldText()
else
-- "###Mg"
return floor(value / (COPPER_PER_GOLD * 1000 * 1000)).."M"..Money.GetGoldText()
end
end
function private.GetGraphYValue(xValue)
return TSM.Accounting.GoldTracker.GetGoldAtTime(xValue, private.settings.dashboardUnselectedCharacters)
end
function private.ContentOnUpdate(contentFrame)
contentFrame:SetScript("OnUpdate", nil)
private.UpdateTimeButtons(contentFrame:GetElement("goldHeader.timeBtns"))
private.UpdateGraph(contentFrame)
end
function private.DropdownOnSelectionChanged(dropdown)
for _, key in ipairs(private.characterGuilds) do
private.settings.dashboardUnselectedCharacters[key] = not dropdown:ItemIsSelectedByKey(key) or nil
end
private.UpdateGraph(dropdown:GetParentElement():GetParentElement())
end
function private.TimeBtnOnClick(button)
local timeRange = button:GetContext()
assert(timeRange)
private.selectedTimeRange = timeRange
private.settings.dashboardTimeRange = timeRange
private.UpdateTimeButtons(button:GetParentElement())
private.UpdateGraph(button:GetParentElement():GetParentElement():GetParentElement())
end
function private.UpdateGraph(contentFrame)
-- update the graph
local minTime, maxTime = TSM.Accounting.GoldTracker.GetGraphTimeRange(private.settings.dashboardUnselectedCharacters)
local goldGraph = contentFrame:GetElement("goldGraph")
local zoomStart, zoomEnd = goldGraph:GetZoom()
if private.selectedTimeRange == TIME_RANGE_LOOKUP["all"] then
zoomStart = minTime
zoomEnd = maxTime
elseif private.selectedTimeRange then
zoomStart = max(time() - private.selectedTimeRange, minTime)
zoomEnd = time()
end
goldGraph:SetXRange(minTime, maxTime)
:SetZoom(zoomStart, zoomEnd)
:Draw()
private.PopulateDetails(contentFrame)
end
function private.GraphOnZoomChanged(graph)
private.selectedTimeRange = nil
private.settings.dashboardTimeRange = -1
private.UpdateTimeButtons(graph:GetElement("__parent.goldHeader.timeBtns"))
private.PopulateDetails(graph:GetElement("__parent"))
end
function private.GraphOnHoverUpdate(graph, hoverTime)
local goldHeader = graph:GetElement("__parent.goldHeader")
if hoverTime then
local timeStr = nil
if GetCVar("timeMgrUseMilitaryTime") == "1" then
timeStr = date("%H:%M %b %d, %Y", hoverTime)
else
timeStr = gsub(date("%I:%M %p %b %d, %Y", hoverTime), "^0", "")
end
goldHeader:GetElement("timeBtns"):Hide()
goldHeader:GetElement("hoverTime")
:SetText(timeStr)
:Show()
else
goldHeader:GetElement("timeBtns"):Show()
goldHeader:GetElement("hoverTime"):Hide()
private.UpdateTimeButtons(goldHeader:GetElement("timeBtns"))
end
goldHeader:Draw()
end
function private.UpdateTimeButtons(frame)
frame:ShowAllChildren()
local resetButton = frame:GetElement("resetZoom")
if private.selectedTimeRange then
for _, button in frame:LayoutChildrenIterator() do
if button ~= resetButton then
button:SetTextColor(private.selectedTimeRange == button:GetContext() and "TEXT" or "ACTIVE_BG_ALT")
end
end
resetButton:Hide()
else
for _, button in frame:LayoutChildrenIterator() do
button:Hide()
end
resetButton:Show()
end
frame:GetParentElement():Draw()
end
function private.GraphXStepFunc(prevValue, suggestedStep)
local year, day, month, hour, min, sec = strsplit(",", date("%Y,%d,%m,%H,%M,%S", prevValue))
private.tempTimeTable.year = tonumber(year)
private.tempTimeTable.day = tonumber(day)
private.tempTimeTable.month = tonumber(month)
private.tempTimeTable.hour = tonumber(hour)
private.tempTimeTable.min = tonumber(min)
private.tempTimeTable.sec = tonumber(sec)
if suggestedStep > SECONDS_PER_DAY * 14 then
private.tempTimeTable.month = private.tempTimeTable.month + 1
private.tempTimeTable.day = 1
private.tempTimeTable.hour = 0
private.tempTimeTable.min = 0
private.tempTimeTable.sec = 0
elseif suggestedStep > SECONDS_PER_DAY / 6 then
private.tempTimeTable.day = private.tempTimeTable.day + 1
if private.tempTimeTable.hour == 23 then
-- add an extra hour to avoid DST issues
private.tempTimeTable.hour = 1
else
private.tempTimeTable.hour = 0
end
private.tempTimeTable.min = 0
private.tempTimeTable.sec = 0
else
private.tempTimeTable.hour = private.tempTimeTable.hour + 1
private.tempTimeTable.min = 0
private.tempTimeTable.sec = 0
end
local newValue = time(private.tempTimeTable)
assert(newValue > prevValue)
return newValue
end
function private.GraphYStepFunc(mode, ...)
if mode == "RANGE" then
local yMin, yMax, maxNumSteps = ...
-- find the smallest 10^X step size which still looks good
local minStep = max((yMax - yMin) / maxNumSteps / 10, yMax / 200)
local stepSize = MIN_GRAPH_STEP_SIZE
while stepSize < minStep do
stepSize = stepSize * 10
end
yMin = Math.Floor(yMin, stepSize)
yMax = Math.Ceil(yMax + stepSize / 3, stepSize)
if yMin == yMax then
yMax = yMax + stepSize
end
return yMin, yMax
elseif mode == "NEXT" then
local prevValue, yMax = ...
local stepSize = MIN_GRAPH_STEP_SIZE
while stepSize < yMax / 1000 do
stepSize = stepSize * 10
end
return Math.Floor(prevValue, stepSize) + stepSize
else
error("Invalid mode")
end
end
function private.PopulateDetails(contentFrame)
local goldGraph = contentFrame:GetElement("goldGraph")
local unselectedCharacters = next(private.settings.dashboardUnselectedCharacters) and private.settings.dashboardUnselectedCharacters or nil
local timeFilterStart, timeFilterEnd, numDays = nil, nil, nil
if private.selectedTimeRange and private.selectedTimeRange ~= -1 then
timeFilterStart = time() - private.selectedTimeRange
timeFilterEnd = time()
numDays = ceil(private.selectedTimeRange / SECONDS_PER_DAY)
elseif not private.selectedTimeRange then
timeFilterStart, timeFilterEnd = goldGraph:GetZoom()
numDays = ceil((timeFilterEnd - timeFilterStart) / SECONDS_PER_DAY)
else
local timeStart, timeEnd = goldGraph:GetXRange()
numDays = ceil((timeEnd - timeStart) / SECONDS_PER_DAY)
end
numDays = max(numDays, 1)
local saleTotal, salePerDay, saleTopItem, saleTopValue, saleTotalQuantity = 0, nil, nil, 0, 0
local buyTotal, buyPerDay, buyTopItem, buyTopValue, buyTotalQuantity = 0, nil, nil, 0, 0
local profitTopItem = nil
local query = TSM.Accounting.GetSummaryQuery(timeFilterStart, timeFilterEnd, unselectedCharacters)
local saleNumDays, buyNumDays = 1, 1
local saleItemTotals = TempTable.Acquire()
local buyItemTotals = TempTable.Acquire()
local saleItemNum = TempTable.Acquire()
local buyItemNum = TempTable.Acquire()
for _, recordType, itemString, price, quantity, timestamp in query:Iterator() do
if recordType == "sale" then
local daysAgo = floor((time() - timestamp) / (24 * 60 * 60))
saleNumDays = max(saleNumDays, daysAgo)
saleItemTotals[itemString] = (saleItemTotals[itemString] or 0) + price * quantity
saleTopValue = max(saleTopValue, price)
saleTotalQuantity = saleTotalQuantity + quantity
saleItemNum[itemString] = (saleItemNum[itemString] or 0) + quantity
elseif recordType == "buy" then
local daysAgo = floor((time() - timestamp) / (24 * 60 * 60))
buyNumDays = max(buyNumDays, daysAgo)
buyItemTotals[itemString] = (buyItemTotals[itemString] or 0) + price * quantity
buyTopValue = max(buyTopValue, price)
buyTotalQuantity = buyTotalQuantity + quantity
buyItemNum[itemString] = (buyItemNum[itemString] or 0) + quantity
else
error("Invalid recordType: "..tostring(recordType))
end
end
query:Release()
local topSaleItemTotal = 0
for itemString, itemTotal in pairs(saleItemTotals) do
saleTotal = saleTotal + itemTotal
if itemTotal > topSaleItemTotal then
saleTopItem = itemString
topSaleItemTotal = itemTotal
end
end
salePerDay = Math.Round(saleTotal / saleNumDays)
local topBuyItemTotal = 0
for itemString, itemTotal in pairs(buyItemTotals) do
buyTotal = buyTotal + itemTotal
if itemTotal > topBuyItemTotal then
buyTopItem = itemString
topBuyItemTotal = itemTotal
end
end
buyPerDay = Math.Round(buyTotal / buyNumDays)
local topItemProfit = 0
for itemString in pairs(saleItemNum) do
if buyItemNum[itemString] then
local profit = (saleItemTotals[itemString] / saleItemNum[itemString] - buyItemTotals[itemString] / buyItemNum[itemString]) * min(saleItemNum[itemString], buyItemNum[itemString])
if profit > topItemProfit then
profitTopItem = itemString
topItemProfit = profit
end
end
end
TempTable.Release(saleItemTotals)
TempTable.Release(buyItemTotals)
TempTable.Release(saleItemNum)
TempTable.Release(buyItemNum)
local profitTotal = saleTotal - buyTotal
local profitPerDay = salePerDay - buyPerDay
local rangeLow, rangeHigh = goldGraph:GetYRange()
contentFrame:GetElement("summary.range.low.value")
:SetText(Money.ToString(rangeLow, nil, "OPT_TRIM") or "-")
contentFrame:GetElement("summary.range.high.value")
:SetText(Money.ToString(rangeHigh, nil, "OPT_TRIM") or "-")
contentFrame:GetElement("summary.daily.sales.value")
:SetText(saleTotalQuantity and Math.Round(saleTotalQuantity / numDays) or "-")
contentFrame:GetElement("summary.daily.purchases.value")
:SetText(buyTotalQuantity and Math.Round(buyTotalQuantity / numDays) or "-")
contentFrame:GetElement("summary.top.sale.value")
:SetText(Money.ToString(Math.Round(saleTopValue, TSM.IsWowClassic() and 1 or COPPER_PER_GOLD), nil, "OPT_TRIM") or "-")
contentFrame:GetElement("summary.top.expense.value")
:SetText(Money.ToString(Math.Round(buyTopValue, TSM.IsWowClassic() and 1 or COPPER_PER_GOLD), nil, "OPT_TRIM") or "-")
contentFrame:GetElement("details.salesTotal.amount")
:SetText(Money.ToString(saleTotal))
contentFrame:GetElement("details.salesAvg.amount")
:SetText(Money.ToString(salePerDay))
contentFrame:GetElement("details.salesTop.item")
:SetText(TSM.UI.GetColoredItemName(saleTopItem) or "-")
:SetTooltip(saleTopItem)
contentFrame:GetElement("details.expensesTotal.amount")
:SetText(Money.ToString(buyTotal))
contentFrame:GetElement("details.expensesAvg.amount")
:SetText(Money.ToString(buyPerDay))
contentFrame:GetElement("details.expensesTop.item")
:SetText(TSM.UI.GetColoredItemName(buyTopItem) or "-")
:SetTooltip(buyTopItem)
contentFrame:GetElement("details.profitTotal.amount")
:SetText(Money.ToString(profitTotal, profitTotal < 0 and Theme.GetFeedbackColor("RED"):GetTextColorPrefix() or nil))
contentFrame:GetElement("details.profitAvg.amount")
:SetText(Money.ToString(profitPerDay, profitPerDay < 0 and Theme.GetFeedbackColor("RED"):GetTextColorPrefix() or nil))
contentFrame:GetElement("details.profitTop.item")
:SetText(TSM.UI.GetColoredItemName(profitTopItem) or "-")
:SetTooltip(profitTopItem)
contentFrame:Draw()
end