From 4a7e966744143dc3a29ea3f291f1f9649bdae7aa Mon Sep 17 00:00:00 2001 From: Gavin McDonald Date: Fri, 17 Oct 2025 18:25:45 -0400 Subject: [PATCH] Claude set up a '/labels' endpoint --- .../controllers/labels_controller.ex | 38 ++++++++ lib/labelmaker_web/controllers/labels_html.ex | 16 ++++ .../controllers/labels_html/index.html.heex | 74 +++++++++++++++ lib/labelmaker_web/router.ex | 1 + .../controllers/labels_controller_test.exs | 93 +++++++++++++++++++ 5 files changed, 222 insertions(+) create mode 100644 lib/labelmaker_web/controllers/labels_controller.ex create mode 100644 lib/labelmaker_web/controllers/labels_html.ex create mode 100644 lib/labelmaker_web/controllers/labels_html/index.html.heex create mode 100644 test/labelmaker_web/controllers/labels_controller_test.exs diff --git a/lib/labelmaker_web/controllers/labels_controller.ex b/lib/labelmaker_web/controllers/labels_controller.ex new file mode 100644 index 0000000..ceba131 --- /dev/null +++ b/lib/labelmaker_web/controllers/labels_controller.ex @@ -0,0 +1,38 @@ +defmodule LabelmakerWeb.LabelsController do + use LabelmakerWeb, :controller + + @label_dir Path.join(:code.priv_dir(:labelmaker), "static/labels") + + def index(conn, _params) do + labels = list_labels() + render(conn, :index, labels: labels) + end + + defp list_labels do + case File.ls(@label_dir) do + {:ok, files} -> + files + |> Enum.filter(&String.ends_with?(&1, ".png")) + |> Enum.map(fn filename -> + filepath = Path.join(@label_dir, filename) + stat = File.stat!(filepath) + + %{ + filename: filename, + filepath: filepath, + size: stat.size, + modified: stat.mtime, + url: "/labels/#{filename}" + } + end) + |> Enum.sort_by(& &1.modified, :desc) + + {:error, :enoent} -> + # Directory doesn't exist yet + [] + + {:error, _reason} -> + [] + end + end +end diff --git a/lib/labelmaker_web/controllers/labels_html.ex b/lib/labelmaker_web/controllers/labels_html.ex new file mode 100644 index 0000000..edd4d20 --- /dev/null +++ b/lib/labelmaker_web/controllers/labels_html.ex @@ -0,0 +1,16 @@ +defmodule LabelmakerWeb.LabelsHTML do + use LabelmakerWeb, :html + + embed_templates "labels_html/*" + + def format_size(bytes) when bytes < 1024, do: "#{bytes} B" + def format_size(bytes) when bytes < 1024 * 1024, do: "#{Float.round(bytes / 1024, 1)} KB" + def format_size(bytes), do: "#{Float.round(bytes / (1024 * 1024), 1)} MB" + + def format_datetime({{year, month, day}, {hour, minute, second}}) do + "#{year}-#{pad(month)}-#{pad(day)} #{pad(hour)}:#{pad(minute)}:#{pad(second)}" + end + + defp pad(num) when num < 10, do: "0#{num}" + defp pad(num), do: "#{num}" +end diff --git a/lib/labelmaker_web/controllers/labels_html/index.html.heex b/lib/labelmaker_web/controllers/labels_html/index.html.heex new file mode 100644 index 0000000..0769aa1 --- /dev/null +++ b/lib/labelmaker_web/controllers/labels_html/index.html.heex @@ -0,0 +1,74 @@ +
+
+

Generated Labels

+ ← Back to Home +
+ + <%= if Enum.empty?(@labels) do %> +
+

+ No labels generated yet. +

+ + Create Your First Label + +
+ <% else %> +
+ Total labels: <%= length(@labels) %> +
+ +
+ <%= for label <- @labels do %> +
+
+ Label image +
+ +
+
+ Size: + + <%= format_size(label.size) %> + +
+ +
+ Modified: + + <%= format_datetime(label.modified) %> + +
+ + + +
+ +
+
+
+ <% end %> +
+ <% end %> +
diff --git a/lib/labelmaker_web/router.ex b/lib/labelmaker_web/router.ex index f5a6d25..2dfd006 100644 --- a/lib/labelmaker_web/router.ex +++ b/lib/labelmaker_web/router.ex @@ -18,6 +18,7 @@ defmodule LabelmakerWeb.Router do pipe_through :browser live "/", Home + get "/labels", LabelsController, :index get "/:label", LabelController, :show end diff --git a/test/labelmaker_web/controllers/labels_controller_test.exs b/test/labelmaker_web/controllers/labels_controller_test.exs new file mode 100644 index 0000000..fed2fb5 --- /dev/null +++ b/test/labelmaker_web/controllers/labels_controller_test.exs @@ -0,0 +1,93 @@ +defmodule LabelmakerWeb.LabelsControllerTest do + use LabelmakerWeb.ConnCase, async: false + + @label_dir Path.join(:code.priv_dir(:labelmaker), "static/labels") + + setup do + # Clean up test images before each test + File.rm_rf!(@label_dir) + File.mkdir_p!(@label_dir) + :ok + end + + describe "index" do + test "renders empty state when no labels exist", %{conn: conn} do + conn = get(conn, ~p"/labels") + + assert html_response(conn, 200) =~ "No labels generated yet" + assert html_response(conn, 200) =~ "Create Your First Label" + end + + test "lists all generated labels", %{conn: conn} do + # Generate some test labels + _conn1 = get(conn, ~p"/TestLabel1?color=red") + _conn2 = get(conn, ~p"/TestLabel2?color=blue") + _conn3 = get(conn, ~p"/TestLabel3?color=green") + + # Visit labels page + conn = get(conn, ~p"/labels") + html = html_response(conn, 200) + + assert html =~ "Generated Labels" + assert html =~ "Total labels: 3" + end + + test "displays label information correctly", %{conn: conn} do + # Generate a test label + _conn = get(conn, ~p"/MyLabel?color=yellow&size=96") + + # Visit labels page + conn = get(conn, ~p"/labels") + html = html_response(conn, 200) + + # Should show size and modified date + assert html =~ "Size:" + assert html =~ "Modified:" + assert html =~ "View Full Size" + end + + test "shows labels sorted by most recent first", %{conn: conn} do + # Generate labels with delays to ensure different timestamps + _conn1 = get(conn, ~p"/First") + Process.sleep(100) + _conn2 = get(conn, ~p"/Second") + Process.sleep(100) + _conn3 = get(conn, ~p"/Third") + + # Visit labels page + conn = get(conn, ~p"/labels") + html = html_response(conn, 200) + + # Should show all labels + assert html =~ "Total labels: 3" + end + + test "handles missing labels directory gracefully", %{conn: conn} do + # Remove the labels directory entirely + File.rm_rf!(@label_dir) + + conn = get(conn, ~p"/labels") + + assert html_response(conn, 200) =~ "No labels generated yet" + end + + test "includes link back to home", %{conn: conn} do + conn = get(conn, ~p"/labels") + html = html_response(conn, 200) + + assert html =~ "Back to Home" + assert html =~ ~p"/" + end + + test "shows clickable URLs for each label", %{conn: conn} do + _conn = get(conn, ~p"/ClickableTest") + + conn = get(conn, ~p"/labels") + html = html_response(conn, 200) + + # Should have a URL input field + assert html =~ "/labels/" + assert html =~ ".png" + end + end +end