defmodule DaProductAppWeb.SbomComponentLive do use DaProductAppWeb, :live_view import Logger alias DaProductApp.Users alias DaProductAppWeb.Router.Helpers, as: Routes alias DaProductApp.SBOM alias DaProductApp.SBOM.Component # on_mount DaProductAppWeb.Live.SetCurrentPage # def mount(_params, _session, socket) do # components = SBOM.list_components() # Fetch existing components # {:ok, assign(socket, components: components, form: to_form(Component.changeset(%Component{}, %{})))} # end @impl true def mount(_params, session, socket) do components = SBOM.list_components() # Fetch existing components changeset = build_changeset() current_user = session["user_token"] |> Users.get_user_by_session_token() || nil socket = socket |> assign(:current_user, current_user) |> assign(:show_sidebar, true) # Enable sidebar for dashboard |> assign(:page_title, "SBOM Component") # Set the page title {:ok, assign(socket, modal: false, slide_over: false, slide_over: "organization", group_size: "md", pagination_page: 1, total_pages: 10, form: to_form(Component.changeset(%Component{}, %{})), form2: to_form(Component.changeset(%Component{}, %{})), show_childbom_spdx: false, show_checksum_details: false, components: components, active_tab: :live )} end @impl true def handle_event("toggle_childbom", _params, socket) do new_value = !socket.assigns.show_childbom_spdx {:noreply, assign(socket, show_childbom_spdx: new_value)} end def handle_event("toggle_checksum_type", %{"value" => "File"}, socket) do {:noreply, assign(socket, show_checksum_details: true)} end def handle_event("toggle_checksum_type", _params, socket) do {:noreply, assign(socket, show_checksum_details: false)} end @impl true def handle_event("validate", %{"object" => object_params}, socket) do changeset = object_params |> build_changeset() |> Map.put(:action, :validate) {:noreply, assign_form(socket, changeset)} end def handle_event("submit", %{"component" => component_params}, socket) do Logger.debug("Received component params: #{inspect(component_params)}") # Ensure required integer fields are set correctly component_params = component_params |> Map.put("user_id", 1) |> Map.put("application_id", 1) |> Map.put("organization_id", 1) # Check if required fields are actually present required_fields = ["component_name", "version", "supplier_type", "supplier_name", "relationship", "checksum_type", "checksum_algorithm", "checksum_value"] missing_fields = Enum.filter(required_fields, fn field -> Map.get(component_params, field, "") |> String.trim() == "" end) if missing_fields != [] do Logger.error("Missing required fields: #{inspect(missing_fields)}") {:noreply, put_flash(socket, :error, "Missing required fields: #{Enum.join(missing_fields, ", ")}")} else case DaProductApp.SBOM.create_component(component_params) do {:ok, _component} -> socket = socket |> put_flash(:info, "Component created successfully") |> push_patch(to: "/sbomcomponent") {:noreply, socket} {:error, changeset} -> Logger.error("Failed to insert component: #{inspect(changeset.errors)}") {:noreply, assign(socket, :form, to_form(changeset))} end end end @impl true def handle_params(params, _url, socket) do Logger.debug("Component: #{inspect(params)}") slide_over = params["origin"] || nil slidefor = params["slidefor"] || nil socket = load_data(socket) {:noreply, assign(socket, slide_over: slide_over, slidefor: slidefor)} end defp load_data(socket) do components = DaProductApp.SBOM.list_components() assign(socket, components: components) end @impl true #def handle_event("submit", %{"object" => object_params}, socket) do #def handle_event("submit", %{"component" => component_params}, socket) do # Logger.debug("Component: #{inspect(component_params)}", []) # Correct usage # changeset = build_changeset(object_params) # case SBOM.create_component(component_params) do # {:ok, _component} -> # socket = # socket # |> put_flash(:success, "Component successfully created") # |> assign(:components, SBOM.list_components()) # Refresh the list # {:noreply, socket} # {:error, changeset} -> # {:noreply, assign(socket, form: to_form(changeset))} # end # case validate_changeset(changeset) do # {:ok, _object} -> # socket = # socket # |> put_flash(:success, "Object successfully created") # |> assign_form(build_changeset()) # # {:noreply, socket} # # {:error, changeset} -> # socket = # socket # |> put_flash(:error, inspect(changeset.errors)) # {:noreply, assign_form(socket, changeset)} # end # end defp assign_form(socket, changeset) do assign(socket, form: to_form(changeset, as: :object)) end defp build_changeset(params \\ %{}) do data = %{} types = %{ text: :string, select: :string, checkbox_group: {:array, :string}, radio_group: :string, textarea: :string, checkbox: :boolean, color: :string, date: :date, datetime: :naive_datetime, email: :string, file: :string, hidden: :string, month: :string, number: :integer, password: :string, radio: :string, range: :integer, search: :string, tel: :string, time: :time, url: :string, week: :string, switch: :boolean } {data, types} |> Ecto.Changeset.cast(params, Map.keys(types)) |> Ecto.Changeset.validate_required([:text]) |> Ecto.Changeset.validate_acceptance(:checkbox) |> Ecto.Changeset.validate_length(:text, min: 3, max: 50) end defp validate_changeset(changeset) do Ecto.Changeset.apply_action(changeset, :validate) end @impl true def handle_event("delete_component", %{"id" => id}, socket) do SBOM.delete_component(id) {:noreply, assign(socket, components: SBOM.list_components())} end @impl true def handle_params(params, _uri, socket) do case socket.assigns.live_action do :index -> {:noreply, assign(socket, modal: false, slide_over: false)} :modal -> {:noreply, assign(socket, modal: params["size"])} :slide_over -> {:noreply, assign(socket, slide_over: params["origin"])} :pagination -> {:noreply, assign(socket, pagination_page: String.to_integer(params["page"]))} end end @impl true def render(assigns) do ~H""" SwiftBOM - SBOM Generator

Organization Info

Document Name

ACME-INFUSION-1.0-SBOM-DRAFT

Application

Primary Component

Application Name

Components

<.button label="+" link_type="live_patch" to={~p"/sbomcomponent/right?slidefor=component"} />
<%= for component <- @components do %> <% end %>
Component Name Version Supplier Action
<%= component.component_name %> <%= component.version %> <%= component.supplier_name %> <.icon_button tooltip="Clock" size="xs" phx-click="delete_component" color="danger"> <.icon name="hero-x-circle" class="w-5 h-5" />
<%= if @slide_over do %> <.slide_over origin={@slide_over} title={@slidefor}>
<.form for={@form} phx-submit="submit"> <.field type="checkbox" label="Child BOM exists" field={@form[:childbom]} phx-click="toggle_childbom" /> <%= if @show_childbom_spdx do %> <.field type="file" label="Child BOM SPDX" field={@form[:child_bom_spdx]} /> <% end %> <.field label="Component Name" field={@form[:component_name]} /> <.field label="Version" field={@form[:version]} />
<.field field={@form[:supplier_name]}> <.input placeholder="Enter name" />
<.field field={@form[:supplier_type]} type="select" options={[{"Organization", "organization"}, {"Person", "person"}, {"Tools", "tools"}]} help_text="Select Suppliers type" /> <.field label="Relationship" field={@form[:relationship]} type="select" options={[{"Included","included"}, {"Primary","primary"}]} /> <.field field={@form[:checksum_type]} type="select" options={[{"File","File"}, {"Package","Package"}]} phx-click="toggle_checksum_type" help_text="Select Component types" /> <%= if @show_checksum_details do %> <.field label="Algorithm" field={@form[:checksum_algorithm]} type="select" options={[{"SHA1","SHA1"}, {"SHA256","SHA256"},{"MD5", "MD5"}]} /> <.field label="File Name" field={@form[:checksum_filename]} /> <.field label="Checksum Value" field={@form[:checksum_value]} /> <% end %> <.field type="hidden" field={@form[:organization_id]} value="1" /> <.field type="hidden" field={@form[:application_id]} value="1" /> <.field type="hidden" field={@form[:user_id]} value="1" />
<.button type="button" label="Cancel" color="white" phx-click={PetalComponents.SlideOver.hide_slide_over(@slide_over)} /> <.button type="submit" label="Submit" />
<% end %> """ end @impl true def handle_event("close_slide_over", _, socket) do {:noreply, push_patch(socket, to: "/sbomcomponent")} end end