Skip to content

Commit 53c2dfd

Browse files
iceHub82claude
andcommitted
refactor: drop jQuery/moment/daterangepicker and de-duplicate the page models
Audit items 2, 4 and 7. Dates: the two single-date pickers are now <input type="date">, which removes the daterangepicker and moment.js CDN dependencies. The custom change handlers went with them - htmx already does this via `hx-trigger="load, change from:#dashboard-controls"` plus `hx-include="#dashboard-controls"`, so Index.cshtml now has no script section at all. That section was also loading htmx a second time on top of _Layout. jQuery: its only remaining callers were those handlers and one selector in color-modes.js. Both gone, so jquery/ is deleted. Bootstrap 5 and htmx need it for nothing. wwwroot/js/site.js is deleted - no page ever referenced it. Its job was to populate the hidden theme input on load, which meant the first chart of every session rendered light even in dark mode. The chart now reads the theme per request via `hx-vals="js:{theme: preferredTheme()}"`, so there is no hidden input to keep in sync and no race with htmx's `load` trigger, which fires before DOMContentLoaded handlers. Toggling the theme fires a `refresh` trigger. Page models: IndexModel and BacktestModel were byte-for-byte identical and each held a third copy of the ticker list. Both pages now share DashboardModel, which projects its dropdown from MinimalApiExtension.Tickers so the options cannot drift from the ids the endpoint accepts. Backtest.cshtml: dropped the ~80 commented-out lines cloned from Index.cshtml and gave the dropdown an hx-target so it stops trying to replace itself. Verified in Chrome against a running instance: one request on load carrying theme=dark, ticker and date changes each firing one correctly-parameterised request, the theme toggle firing one more with theme=light, and zero JS console errors. Build warning-clean, 14 xUnit and 15 pytest pass. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent 8956e6e commit 53c2dfd

15 files changed

Lines changed: 74 additions & 19564 deletions
Lines changed: 8 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1-
@page
2-
@model BacktestModel
1+
@page
2+
@model DashboardModel
33

44
@{
55
ViewData["Title"] = "Backtest";
66
}
77

8+
@* Work in progress: the endpoint downloads live data but does not plot it yet. *@
9+
810
<div class="row mt-3 p-2">
911
<div class="col">
1012
<h1 class="h2">Backtest</h1>
@@ -13,88 +15,14 @@
1315
<div class="row p-2">
1416
<div class="col-3">
1517
<div class="form-floating">
16-
@Html.DropDownList("tickerDd", new SelectList(Model.Tickers, "Id", "Name"), new { @class = "form-select", @hx_get = "/api/dashboard/backtest", hx_trigger = "change" })
18+
@Html.DropDownList("tickerDd", Model.Tickers,
19+
new { @class = "form-select", hx_get = "/api/dashboard/backtest", hx_trigger = "change", hx_target = "#backtest-chart" })
1720
<label>Stock</label>
1821
</div>
1922
</div>
20-
@* <div class="col-3">
21-
<div class="form-floating">
22-
<input class="form-control" type="text" name="startDate" value="@Model.StartDate">
23-
<label>Start Date</label>
24-
</div>
25-
</div>
26-
<div class="col-3">
27-
<div class="form-floating">
28-
<input class="form-control" type="text" name="endDate" value="@Model.EndDate">
29-
<label>End Date</label>
30-
</div>
31-
</div> *@
3223
</div>
33-
@* <div class="row p-2">
24+
<div class="row">
3425
<div class="col">
35-
<div class="form-check form-check-inline">
36-
<input class="form-check-input" type="checkbox" id="openCb" name="openCb" checked>
37-
<label class="form-check-label" for="openCb">
38-
Open
39-
</label>
40-
</div>
41-
<div class="form-check form-check-inline">
42-
<input class="form-check-input" type="checkbox" id="closeCb" name="closeCb" checked>
43-
<label class="form-check-label" for="closeCb">
44-
Close
45-
</label>
46-
</div>
26+
<div id="backtest-chart" class="chart"></div>
4727
</div>
4828
</div>
49-
<div class="row">
50-
<div class="col" hx-indicator="#chart-spinner">
51-
<div id="chart-spinner" class="dashboard-spinner spinner-border text-primary htmx-indicator" role="status"></div>
52-
<div id="stock-chart" class="chart" role="button"
53-
hx-get="/api/dashboard/stock"
54-
hx-include="[name='tickerDd'], [name='startDate'], [name='endDate'], [name='openCb'], [name='closeCb'], [name='theme']"
55-
hx-trigger="load, click">
56-
</div>
57-
</div>
58-
</div> *@
59-
60-
<input type="hidden" name="theme" />
61-
62-
@section Scripts {
63-
64-
65-
@* <script>
66-
let theme = localStorage.getItem('theme')
67-
let themeInput = $("[name='theme']")
68-
if (theme === 'auto') {
69-
themeInput.val(window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light')
70-
} else {
71-
themeInput.val(theme)
72-
}
73-
74-
$('input[name="startDate"]').daterangepicker({
75-
singleDatePicker: true,
76-
showDropdowns: true,
77-
autoclose: true
78-
}, function(start, end, label) {
79-
$('input[name="startDate"]').val(start.format('YYYY/MM/DD'))
80-
triggerDashboardUpdate()
81-
});
82-
83-
$('input[name="endDate"]').daterangepicker({
84-
singleDatePicker: true,
85-
showDropdowns: true,
86-
autoclose: true
87-
}, function(start, end, label) {
88-
$('input[name="endDate"]').val(start.format('YYYY/MM/DD'))
89-
triggerDashboardUpdate()
90-
});
91-
92-
$('#tickerDd, [name="openCb"], [name="closeCb"]').change(function() {
93-
triggerDashboardUpdate()
94-
});
95-
96-
function triggerDashboardUpdate() {
97-
htmx.trigger('#stock-chart', 'click')
98-
}
99-
</script> *@
100-
}

PythonNet.App.Web/Pages/Backtest.cshtml.cs

Lines changed: 0 additions & 20 deletions
This file was deleted.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
using Microsoft.AspNetCore.Mvc.RazorPages;
2+
using Microsoft.AspNetCore.Mvc.Rendering;
3+
4+
namespace PythonNet.App.Web.Pages;
5+
6+
/// <summary>
7+
/// Shared by Index and Backtest - both are the same controls over the same tickers. The ticker list is
8+
/// projected from <see cref="MinimalApiExtension.Tickers"/> so the dropdown cannot drift from the ids
9+
/// the endpoint actually accepts.
10+
/// </summary>
11+
public class DashboardModel : PageModel
12+
{
13+
public IEnumerable<SelectListItem> Tickers { get; } = MinimalApiExtension.Tickers
14+
.Select(t => new SelectListItem(t.Value.Ticker, t.Key.ToString()))
15+
.ToList();
16+
17+
public string StartDate { get; set; } = "2021-01-01";
18+
public string EndDate { get; set; } = "2023-01-01";
19+
}
Lines changed: 39 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
@page
2-
@model IndexModel
1+
@page
2+
@model DashboardModel
33

44
@{
55
ViewData["Title"] = "Home";
@@ -10,84 +10,54 @@
1010
<h1 class="h2">Dashboard</h1>
1111
</div>
1212
</div>
13-
<div class="row p-2">
14-
<div class="col-3">
15-
<div class="form-floating">
16-
@Html.DropDownList("tickerDd", new SelectList(Model.Tickers, "Id", "Name"), new { @class = "form-select" })
17-
<label>Stock</label>
13+
14+
<div id="dashboard-controls">
15+
<div class="row p-2">
16+
<div class="col-3">
17+
<div class="form-floating">
18+
@Html.DropDownList("tickerDd", Model.Tickers, new { @class = "form-select" })
19+
<label>Stock</label>
20+
</div>
1821
</div>
19-
20-
</div>
21-
<div class="col-3">
22-
<div class="form-floating">
23-
<input class="form-control" type="text" name="startDate" value="@Model.StartDate">
24-
<label>Start Date</label>
22+
<div class="col-3">
23+
<div class="form-floating">
24+
<input class="form-control" type="date" name="startDate" value="@Model.StartDate">
25+
<label>Start Date</label>
26+
</div>
2527
</div>
26-
</div>
27-
<div class="col-3">
28-
<div class="form-floating">
29-
<input class="form-control" type="text" name="endDate" value="@Model.EndDate">
30-
<label>End Date</label>
28+
<div class="col-3">
29+
<div class="form-floating">
30+
<input class="form-control" type="date" name="endDate" value="@Model.EndDate">
31+
<label>End Date</label>
32+
</div>
3133
</div>
3234
</div>
33-
</div>
34-
<div class="row p-2">
35-
<div class="col">
36-
<div class="form-check form-check-inline">
37-
<input class="form-check-input" type="checkbox" id="openCb" name="openCb" checked>
38-
<label class="form-check-label" for="openCb">
39-
Open
40-
</label>
41-
</div>
42-
<div class="form-check form-check-inline">
43-
<input class="form-check-input" type="checkbox" id="closeCb" name="closeCb" checked>
44-
<label class="form-check-label" for="closeCb">
45-
Close
46-
</label>
35+
<div class="row p-2">
36+
<div class="col">
37+
<div class="form-check form-check-inline">
38+
<input class="form-check-input" type="checkbox" id="openCb" name="openCb" checked>
39+
<label class="form-check-label" for="openCb">
40+
Open
41+
</label>
42+
</div>
43+
<div class="form-check form-check-inline">
44+
<input class="form-check-input" type="checkbox" id="closeCb" name="closeCb" checked>
45+
<label class="form-check-label" for="closeCb">
46+
Close
47+
</label>
48+
</div>
4749
</div>
4850
</div>
4951
</div>
52+
5053
<div class="row">
5154
<div class="col" hx-indicator="#chart-spinner">
5255
<div id="chart-spinner" class="dashboard-spinner spinner-border text-primary htmx-indicator" role="status"></div>
53-
<div id="stock-chart" class="chart" role="button"
56+
<div id="stock-chart" class="chart"
5457
hx-get="/api/dashboard/stock"
55-
hx-include="[name='tickerDd'], [name='startDate'], [name='endDate'], [name='openCb'], [name='closeCb'], [name='theme']"
56-
hx-trigger="load, click">
58+
hx-include="#dashboard-controls"
59+
hx-vals="js:{theme: preferredTheme()}"
60+
hx-trigger="load, change from:#dashboard-controls, refresh">
5761
</div>
5862
</div>
5963
</div>
60-
61-
<input type="hidden" name="theme" />
62-
63-
@section Scripts {
64-
<script src="/lib/htmx/htmx.min.js"></script>
65-
66-
<script>
67-
$('input[name="startDate"]').daterangepicker({
68-
singleDatePicker: true,
69-
showDropdowns: true,
70-
autoclose: true
71-
}, function(start, end, label) {
72-
$('input[name="startDate"]').val(start.format('YYYY/MM/DD'))
73-
triggerDashboardUpdate()
74-
});
75-
76-
$('input[name="endDate"]').daterangepicker({
77-
singleDatePicker: true,
78-
showDropdowns: true,
79-
autoclose: true
80-
}, function(start, end, label) {
81-
$('input[name="endDate"]').val(start.format('YYYY/MM/DD'))
82-
triggerDashboardUpdate()
83-
});
84-
85-
$('#tickerDd, [name="openCb"], [name="closeCb"]').change(function() {
86-
triggerDashboardUpdate()
87-
});
88-
89-
function triggerDashboardUpdate() {
90-
htmx.trigger('#stock-chart', 'click')
91-
}
92-
</script>
93-
}

PythonNet.App.Web/Pages/Index.cshtml.cs

Lines changed: 0 additions & 20 deletions
This file was deleted.

PythonNet.App.Web/Pages/Shared/_Layout.cshtml

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
<meta name="description" content="PythonNet">
99
<title>@ViewData["Title"] - PythonNet</title>
1010
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
11-
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/daterangepicker/daterangepicker.css" />
1211
<link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
1312
</head>
1413
<body>
@@ -131,11 +130,8 @@
131130
</main>
132131
</div>
133132
</div>
134-
<script src="~/lib/jquery/dist/jquery.min.js"></script>
135133
<script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
136-
<script src="https://cdn.jsdelivr.net/momentjs/latest/moment.min.js"></script>
137-
<script src="https://cdn.jsdelivr.net/npm/daterangepicker/daterangepicker.min.js"></script>
138-
<script src="/lib/htmx/htmx.min.js"></script>
134+
<script src="~/lib/htmx/htmx.min.js"></script>
139135

140136
@await RenderSectionAsync("Scripts", required: false)
141137
</body>

PythonNet.App.Web/wwwroot/js/color-modes.js

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,12 +53,16 @@
5353
themeSwitcher.setAttribute('aria-label', themeSwitcherLabel)
5454

5555
if (focus) {
56-
themeSwitcher.focus()
57-
$("[name='theme']").val(getPreferredTheme())
58-
htmx.trigger('#stock-chart', 'click')
56+
themeSwitcher.focus()
57+
// The chart is rendered server-side, so a theme change means re-fetching it.
58+
htmx.trigger('#stock-chart', 'refresh')
5959
}
6060
}
6161

62+
// Read by hx-vals on the chart, which evaluates it per request - no hidden input to keep in sync,
63+
// and no race against htmx's `load` trigger, which fires before DOMContentLoaded handlers.
64+
window.preferredTheme = getPreferredTheme
65+
6266
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => {
6367
const storedTheme = getStoredTheme()
6468
if (storedTheme !== 'light' && storedTheme !== 'dark') {

PythonNet.App.Web/wwwroot/js/site.js

Lines changed: 0 additions & 7 deletions
This file was deleted.

PythonNet.App.Web/wwwroot/lib/jquery/LICENSE.txt

Lines changed: 0 additions & 21 deletions
This file was deleted.

0 commit comments

Comments
 (0)