Skip to content

Commit

Permalink
add SCU configuration page (#1296)
Browse files Browse the repository at this point in the history
  • Loading branch information
panentheos authored May 2, 2024
1 parent e4f3928 commit 09d8db2
Show file tree
Hide file tree
Showing 10 changed files with 452 additions and 18 deletions.
8 changes: 8 additions & 0 deletions assets/js/scu.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
document.querySelectorAll<HTMLElement>('[data-scu-form]').forEach((el) => {
el.addEventListener('submit', (ev) => {
// eslint-disable-next-line no-alert
if (!window.confirm(el.dataset.prompt)) {
ev.preventDefault();
}
});
});
1 change: 1 addition & 0 deletions assets/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ module.exports = function (env) {
entry: {
app: './js/app.ts',
single_sign: './js/SingleSignApp.tsx',
scu: './js/scu.ts'
},
output: {
path: path.resolve(__dirname, '../priv/static/js'),
Expand Down
59 changes: 43 additions & 16 deletions lib/signs_ui/config/state.ex
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ defmodule SignsUi.Config.State do
configured_headways: ConfiguredHeadways.t(),
chelsea_bridge_announcements: String.t(),
sign_groups: SignGroups.t(),
sign_stops: map()
sign_stops: map(),
scus_migrated: %{String.t() => boolean()}
}

def start_link(opts \\ []) do
Expand Down Expand Up @@ -71,12 +72,20 @@ defmodule SignsUi.Config.State do
GenServer.call(pid, {:update_sign_groups, changes})
end

@spec update_scu(GenServer.server(), String.t(), boolean()) :: :ok
def update_scu(pid \\ __MODULE__, id, migrated) do
GenServer.call(pid, {:update_scu, id, migrated})
end

@spec init(any()) :: {:ok, t()}
def init(_) do
schedule_clean(self(), 60_000)
config_store = Application.get_env(:signs_ui, :config_store)
response = config_store.read() |> Jason.decode!()

{sign_stops, scu_ids} = parse_signs_json()
scu_lookup = Map.get(response, "scus_migrated", %{})

state = %{
signs:
response
Expand All @@ -88,7 +97,8 @@ defmodule SignsUi.Config.State do
|> ConfiguredHeadways.parse_configured_headways_json(),
chelsea_bridge_announcements: Map.get(response, "chelsea_bridge_announcements", "off"),
sign_groups: response |> Map.get("sign_groups", %{}) |> SignGroups.from_json(),
sign_stops: parse_signs_json()
sign_stops: sign_stops,
scus_migrated: Map.new(scu_ids, &{&1, Map.get(scu_lookup, &1, false)})
}

{:ok, state}
Expand Down Expand Up @@ -120,6 +130,12 @@ defmodule SignsUi.Config.State do
{:reply, {:ok, new_state}, new_state}
end

def handle_call({:update_scu, id, migrated}, _from, state) do
state = update_in(state, [:scus_migrated], &Map.replace(&1, id, migrated))
save_state(state)
{:reply, :ok, state}
end

def handle_info(:clean, %{signs: sign_configs} = state) do
schedule_clean(self(), 60_000)
new_state = %{state | signs: Utilities.clean_configs(sign_configs)}
Expand Down Expand Up @@ -197,7 +213,8 @@ defmodule SignsUi.Config.State do
configured_headways: configured_headways,
chelsea_bridge_announcements: chelsea_bridge_announcements,
sign_groups: sign_groups,
sign_stops: sign_stops
sign_stops: sign_stops,
scus_migrated: scus_migrated
}) do
config_store = Application.get_env(:signs_ui, :config_store)

Expand All @@ -207,7 +224,8 @@ defmodule SignsUi.Config.State do
"configured_headways" =>
Config.ConfiguredHeadways.format_configured_headways_for_json(configured_headways),
"chelsea_bridge_announcements" => chelsea_bridge_announcements,
"sign_groups" => sign_groups
"sign_groups" => sign_groups,
"scus_migrated" => scus_migrated
},
pretty: true
)
Expand Down Expand Up @@ -244,18 +262,27 @@ defmodule SignsUi.Config.State do
|> File.read!()
|> Jason.decode!(keys: :atoms)

for %{id: id, source_config: %{sources: sources}} <- signs_json,
%{stop_id: stop_id, routes: routes, direction_id: direction_id} <- sources,
route_id <- routes,
reduce: %{} do
acc ->
Map.update(
acc,
%{stop_id: stop_id, route_id: route_id, direction_id: direction_id},
[id],
&[id | &1]
)
end
sign_stops =
for %{id: id, source_config: %{sources: sources}} <- signs_json,
%{stop_id: stop_id, routes: routes, direction_id: direction_id} <- sources,
route_id <- routes,
reduce: %{} do
acc ->
Map.update(
acc,
%{stop_id: stop_id, route_id: route_id, direction_id: direction_id},
[id],
&[id | &1]
)
end

scu_ids =
for %{scu_id: scu_id} <- signs_json,
uniq: true do
scu_id
end

{sign_stops, scu_ids}
end

defp schedule_clean(pid, ms) do
Expand Down
28 changes: 28 additions & 0 deletions lib/signs_ui_web/controllers/scu_controller.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
defmodule SignsUiWeb.ScuController do
use SignsUiWeb, :controller

def index(conn, _params) do
scus =
SignsUi.Config.State.get_all().scus_migrated
|> Enum.sort_by(&elem(&1, 0))

render(conn, "index.html", layout: {SignsUiWeb.LayoutView, "scu.html"}, scus: scus)
end

def update(conn, %{"migrated" => migrated, "scu_id" => scu_id}) do
case Guardian.Plug.current_claims(conn) |> SignsUiWeb.AuthManager.claims_access_level() do
:admin ->
value =
case migrated do
"true" -> true
_ -> false
end

SignsUi.Config.State.update_scu(scu_id, value)
redirect(conn, to: "/scu")

_ ->
send_resp(conn, 403, "Admin only")
end
end
end
2 changes: 2 additions & 0 deletions lib/signs_ui_web/router.ex
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ defmodule SignsUiWeb.Router do
])

get("/viewer", MessagesController, :index)
get("/scu", ScuController, :index)
post("/scu", ScuController, :update)
end

scope "/", SignsUiWeb do
Expand Down
15 changes: 15 additions & 0 deletions lib/signs_ui_web/templates/layout/scu.html.eex
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>SCU configuration</title>
<link rel="stylesheet" href="<%= static_path(@conn, "/vendor/bootstrap.min.css") %>">
<link rel="stylesheet" href="<%= static_path(@conn, "/css/app.css") %>">
</head>
<body>
<%= @inner_content %>
<script src="<%= static_path(@conn, "/js/scu.js") %>"></script>
</body>
</html>
37 changes: 37 additions & 0 deletions lib/signs_ui_web/templates/scu/index.html.eex
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<div class="container">
<h1>SCU configuration</h1>
<p>
Controls which SCU application the system will use, on a per-station basis.
<strong>Caution:</strong> setting these values incorrectly will cause signs to stop working.
Do not change any values here unless instructed by someone conducting an in-station SCU upgrade.
</p>
<table class="table table-sm table-striped">
<thead>
<tr>
<th>SCU</th>
<th>Application</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<%= for {scu_id, migrated} <- @scus do %>
<tr>
<td><%= scu_id %></td>
<td><%= if migrated do %>New<% else %>Legacy<% end %></td>
<td>
<%= if !assigns[:read_only_view] do %>
<% new_text = if(migrated, do: "legacy", else: "new") %>
<form action="" method="post" data-scu-form data-prompt="Change <%= scu_id %> to <%= new_text %> application?">
<input type="hidden" name="_csrf_token" value="<%= Phoenix.Controller.get_csrf_token() %>" />
<input type="hidden" name="scu_id" value="<%= scu_id %>" />
<button class="btn btn-sm btn-outline-secondary" name="migrated" value="<%= if migrated do %>false<% else %>true<% end %>">
Change to <%= new_text %> application
</button>
</form>
<% end %>
</td>
</tr>
<% end %>
</tbody>
</table>
</div>
3 changes: 3 additions & 0 deletions lib/signs_ui_web/views/scu_view.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
defmodule SignsUiWeb.ScuView do
use SignsUiWeb, :view
end
Loading

0 comments on commit 09d8db2

Please sign in to comment.