Compare commits
4 commits
276882ff63
...
da68c1b631
Author | SHA1 | Date | |
---|---|---|---|
da68c1b631 | |||
9a713bf18c | |||
30e02f1c38 | |||
97a2464b14 |
8 changed files with 207 additions and 131 deletions
|
@ -4,7 +4,9 @@
|
||||||
<.navbar_brand class="ml-1">
|
<.navbar_brand class="ml-1">
|
||||||
<.navbar_item>
|
<.navbar_item>
|
||||||
<.title is-4>
|
<.title is-4>
|
||||||
Freedive
|
<.link patch={~p"/"} class="has-text-dark">
|
||||||
|
Freedive
|
||||||
|
</.link>
|
||||||
</.title>
|
</.title>
|
||||||
</.navbar_item>
|
</.navbar_item>
|
||||||
|
|
||||||
|
@ -12,7 +14,7 @@
|
||||||
</.navbar_brand>
|
</.navbar_brand>
|
||||||
|
|
||||||
<.navbar_menu id="navbar_top" class="mr-1">
|
<.navbar_menu id="navbar_top" class="mr-1">
|
||||||
<%= if @current_user do %>
|
<%!-- <%= if @current_user do %>
|
||||||
<.navbar_start>
|
<.navbar_start>
|
||||||
<.navbar_item has-dropdown is-hoverable>
|
<.navbar_item has-dropdown is-hoverable>
|
||||||
<.navbar_link>
|
<.navbar_link>
|
||||||
|
@ -78,7 +80,7 @@
|
||||||
</.navbar_dropdown>
|
</.navbar_dropdown>
|
||||||
</.navbar_item>
|
</.navbar_item>
|
||||||
</.navbar_start>
|
</.navbar_start>
|
||||||
<% end %>
|
<% end %> --%>
|
||||||
|
|
||||||
<.navbar_end>
|
<.navbar_end>
|
||||||
<%= if @current_user do %>
|
<%= if @current_user do %>
|
||||||
|
@ -94,14 +96,14 @@
|
||||||
</.navbar_link>
|
</.navbar_link>
|
||||||
|
|
||||||
<.navbar_dropdown>
|
<.navbar_dropdown>
|
||||||
<.link href={~p"/updates"} class="navbar-item">
|
<%!-- <.link href={~p"/updates"} class="navbar-item">
|
||||||
Software updates
|
Software updates
|
||||||
</.link>
|
</.link>
|
||||||
<.navbar_divider />
|
<.navbar_divider />
|
||||||
<.link href={~p"/packages"} class="navbar-item">
|
<.link href={~p"/packages"} class="navbar-item">
|
||||||
Packages
|
Packages
|
||||||
</.link>
|
</.link> --%>
|
||||||
<.link href={~p"/services"} class="navbar-item">
|
<.link patch={~p"/services"} class="navbar-item">
|
||||||
Services
|
Services
|
||||||
</.link>
|
</.link>
|
||||||
</.navbar_dropdown>
|
</.navbar_dropdown>
|
||||||
|
@ -116,7 +118,7 @@
|
||||||
|
|
||||||
<.navbar_dropdown>
|
<.navbar_dropdown>
|
||||||
<%= if @current_user do %>
|
<%= if @current_user do %>
|
||||||
<.link href={~p"/users/settings"} class="navbar-item">
|
<.link patch={~p"/users/settings"} class="navbar-item">
|
||||||
Settings
|
Settings
|
||||||
</.link>
|
</.link>
|
||||||
<.link href={~p"/users/log_out"} method="delete" class="navbar-item">
|
<.link href={~p"/users/log_out"} method="delete" class="navbar-item">
|
||||||
|
|
|
@ -1,98 +1,102 @@
|
||||||
defmodule FreediveWeb.HomeLive do
|
defmodule FreediveWeb.HomeLive do
|
||||||
use FreediveWeb, :live_view
|
use FreediveWeb.LiliformLive
|
||||||
|
|
||||||
def render(assigns) do
|
def render(assigns) do
|
||||||
~H"""
|
~H"""
|
||||||
<.block class="px-2 py-4">
|
<.page name="Home" filters={@filters} details={@details}>
|
||||||
<.panel is-info>
|
<FreediveWeb.HomeLive.Components.items_block
|
||||||
<.panel_heading>
|
items={@items}
|
||||||
Home
|
selected_item={@selected_item}
|
||||||
</.panel_heading>
|
details={@details}
|
||||||
|
/>
|
||||||
<.panel_tabs is-hidden-mobile>
|
</.page>
|
||||||
<a class="is-active">All</a>
|
|
||||||
<a>Compute</a>
|
|
||||||
<a>Storage</a>
|
|
||||||
<a>Network</a>
|
|
||||||
<a>System</a>
|
|
||||||
</.panel_tabs>
|
|
||||||
|
|
||||||
<.panel_tabs is-hidden-tablet>
|
|
||||||
<a title="All" is-active>
|
|
||||||
<.icon for="all" color="auto" />
|
|
||||||
</a>
|
|
||||||
<a title="Compute">
|
|
||||||
<.icon for="compute" color="auto" />
|
|
||||||
</a>
|
|
||||||
<a title="Storage">
|
|
||||||
<.icon for="storage" color="auto" />
|
|
||||||
</a>
|
|
||||||
<a title="Network">
|
|
||||||
<.icon for="network" color="auto" />
|
|
||||||
</a>
|
|
||||||
<a title="System">
|
|
||||||
<.icon for="system" color="auto" />
|
|
||||||
</a>
|
|
||||||
</.panel_tabs>
|
|
||||||
|
|
||||||
<.panel_block>
|
|
||||||
<.control has-icons-left>
|
|
||||||
<input
|
|
||||||
class="input is-info"
|
|
||||||
type="text"
|
|
||||||
placeholder="Search"
|
|
||||||
name="search"
|
|
||||||
value={@query}
|
|
||||||
/>
|
|
||||||
<.icon for="search" size="1.5rem" aria-hidden="true" is-left />
|
|
||||||
</.control>
|
|
||||||
</.panel_block>
|
|
||||||
|
|
||||||
<.link patch={~p"/services"} class="panel-block pt-1">
|
|
||||||
<span class="panel-icon">
|
|
||||||
<.icon for="puzzle" color="auto" aria-hidden="true" />
|
|
||||||
</span>
|
|
||||||
<div class="mt-2 ml-2">Services</div>
|
|
||||||
</.link>
|
|
||||||
<.link patch={~p"/packages"} class="panel-block pt-1">
|
|
||||||
<span class="panel-icon">
|
|
||||||
<.icon for="package" color="auto" aria-hidden="true" />
|
|
||||||
</span>
|
|
||||||
<span class="mt-2 ml-2">Packages</span>
|
|
||||||
</.link>
|
|
||||||
<.link patch={~p"/updates"} class="panel-block pt-1">
|
|
||||||
<span class="panel-icon">
|
|
||||||
<.icon for="hard-drive-download" color="auto" aria-hidden="true" />
|
|
||||||
</span>
|
|
||||||
<span class="mt-2 ml-2">Software updates</span>
|
|
||||||
</.link>
|
|
||||||
</.panel>
|
|
||||||
</.block>
|
|
||||||
<.section>
|
|
||||||
<.box>
|
|
||||||
<.button phx-click="color" phx-value-enable="true">
|
|
||||||
Color
|
|
||||||
</.button>
|
|
||||||
<.button phx-click="color" phx-value-enable="false">
|
|
||||||
Grayscale
|
|
||||||
</.button>
|
|
||||||
</.box>
|
|
||||||
</.section>
|
|
||||||
"""
|
"""
|
||||||
end
|
end
|
||||||
|
|
||||||
def mount(_params, _session, socket) do
|
def items() do
|
||||||
socket = assign(socket, query: "all")
|
%{
|
||||||
{:ok, socket}
|
"services" => %{
|
||||||
|
name: "Services",
|
||||||
|
path: "/services",
|
||||||
|
icon: "blocks",
|
||||||
|
description: "Manage system services",
|
||||||
|
system: true,
|
||||||
|
account: false,
|
||||||
|
compute: false,
|
||||||
|
storage: false,
|
||||||
|
network: false
|
||||||
|
},
|
||||||
|
"accounts" => %{
|
||||||
|
name: "Account settings",
|
||||||
|
path: "/users/settings",
|
||||||
|
icon: "user-cog",
|
||||||
|
description: "Manage user accounts",
|
||||||
|
system: false,
|
||||||
|
account: true,
|
||||||
|
compute: false,
|
||||||
|
storage: false,
|
||||||
|
network: false
|
||||||
|
}
|
||||||
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
def handle_event("color", %{"enable" => "true"}, socket) do
|
def filters() do
|
||||||
Freedive.Features.enable(:colorhash)
|
[
|
||||||
{:noreply, assign(socket, query: "color")}
|
%{
|
||||||
|
title: "System",
|
||||||
|
key: :system,
|
||||||
|
icon: "system",
|
||||||
|
active: true
|
||||||
|
},
|
||||||
|
%{
|
||||||
|
title: "Account",
|
||||||
|
key: :account,
|
||||||
|
icon: "account",
|
||||||
|
active: false
|
||||||
|
}
|
||||||
|
# %{
|
||||||
|
# title: "Compute",
|
||||||
|
# key: :compute,
|
||||||
|
# icon: "compute",
|
||||||
|
# active: false
|
||||||
|
# },
|
||||||
|
# %{
|
||||||
|
# title: "Storage",
|
||||||
|
# key: :storage,
|
||||||
|
# icon: "storage",
|
||||||
|
# active: false
|
||||||
|
# },
|
||||||
|
# %{
|
||||||
|
# title: "Network",
|
||||||
|
# key: :network,
|
||||||
|
# icon: "network",
|
||||||
|
# active: false
|
||||||
|
# }
|
||||||
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
def handle_event("color", %{"enable" => "false"}, socket) do
|
def search(items, query) do
|
||||||
Freedive.Features.disable(:colorhash)
|
Enum.filter(items, fn {_, item} ->
|
||||||
{:noreply, assign(socket, query: "grayscale")}
|
String.contains?(String.downcase(item.name), String.downcase(query))
|
||||||
|
end)
|
||||||
|
|> Enum.into(%{}, fn {name, item} -> {name, item} end)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defmodule FreediveWeb.HomeLive.Components do
|
||||||
|
use Liliform.Component
|
||||||
|
import Liliform.Icon
|
||||||
|
|
||||||
|
def items_block(assigns) do
|
||||||
|
~H"""
|
||||||
|
<%= for {_name, item} <- @items do %>
|
||||||
|
<.link class="panel-block pt-1" patch={item.path}>
|
||||||
|
<span class="panel-icon">
|
||||||
|
<.icon for={item.icon} color="auto" aria-hidden="true" />
|
||||||
|
</span>
|
||||||
|
<div class="mt-2 ml-2"><%= item.name %></div>
|
||||||
|
</.link>
|
||||||
|
<% end %>
|
||||||
|
"""
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -48,7 +48,6 @@ defmodule FreediveWeb.LiliformLive do
|
||||||
socket =
|
socket =
|
||||||
case Kernel.length(Map.keys(searched_items)) == 1 do
|
case Kernel.length(Map.keys(searched_items)) == 1 do
|
||||||
true ->
|
true ->
|
||||||
IO.inspect(Map.keys(searched_items) |> List.first())
|
|
||||||
assign(socket, %{
|
assign(socket, %{
|
||||||
selected_item: Map.get(searched_items, Map.keys(searched_items) |> List.first()),
|
selected_item: Map.get(searched_items, Map.keys(searched_items) |> List.first()),
|
||||||
details: true
|
details: true
|
||||||
|
@ -103,6 +102,11 @@ defmodule FreediveWeb.LiliformLive do
|
||||||
|
|
||||||
{:noreply, socket}
|
{:noreply, socket}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def(handle_params(params, _url, socket)) do
|
||||||
|
socket = assign(socket, :query, Map.get(params, "search", ""))
|
||||||
|
{:noreply, socket}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@ defmodule FreediveWeb.ServiceLive do
|
||||||
"sshd" => %{
|
"sshd" => %{
|
||||||
name: "sshd",
|
name: "sshd",
|
||||||
path: "/services/ssh",
|
path: "/services/ssh",
|
||||||
icon: "blocks",
|
icon: "lock",
|
||||||
description: "Secure Shell Daemon",
|
description: "Secure Shell Daemon",
|
||||||
enabled: true,
|
enabled: true,
|
||||||
running: true
|
running: true
|
||||||
|
@ -102,27 +102,28 @@ defmodule FreediveWeb.ServiceLive.Components do
|
||||||
<div class="mt-2 ml-2">Back</div>
|
<div class="mt-2 ml-2">Back</div>
|
||||||
</.link>
|
</.link>
|
||||||
|
|
||||||
<.panel_block_div>
|
<.panel_media>
|
||||||
<div class="column is-narrow">
|
<:icon>
|
||||||
<.icon for={@selected_item.name} color="auto" aria-hidden="true" />
|
<.icon_raw for={@selected_item.icon} color="auto" size="3rem" aria-hidden="true" />
|
||||||
</div>
|
<%!-- <Lucideicons.accessibility height="2rem" width="2rem" /> --%>
|
||||||
<div class="columns is-vcentered is-fullwidth">
|
</:icon>
|
||||||
<div class="column is-narrow">
|
|
||||||
<.title is-4>
|
<.title is-4>
|
||||||
<%= @selected_item.name %>
|
<%= @selected_item.name %>
|
||||||
</.title>
|
</.title>
|
||||||
|
<.subtitle>
|
||||||
|
<%= @selected_item.description %>
|
||||||
|
</.subtitle>
|
||||||
|
|
||||||
|
<:actions>
|
||||||
|
<div class="columns">
|
||||||
|
<div class="column is-narrow">
|
||||||
|
<.icon for="running" color="auto" aria-hidden="true" />
|
||||||
|
<.icon for="enabled" color="auto" aria-hidden="true" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="column">
|
</:actions>
|
||||||
<.subtitle>
|
</.panel_media>
|
||||||
<%= @selected_item.description %>
|
|
||||||
</.subtitle>
|
|
||||||
</div>
|
|
||||||
<div class="column is-narrow">
|
|
||||||
<.icon for="running" color="auto" aria-hidden="true" />
|
|
||||||
<.icon for="enabled" color="auto" aria-hidden="true" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</.panel_block_div>
|
|
||||||
"""
|
"""
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -83,6 +83,24 @@ defmodule Liliform.Icon do
|
||||||
"""
|
"""
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Renders an icon without the icon-text wrapper.
|
||||||
|
"""
|
||||||
|
attr :for, :string, required: true, doc: "icon name"
|
||||||
|
attr :size, :string, default: nil, doc: "size of icon"
|
||||||
|
attr :color, :string, default: nil, doc: "color of icon"
|
||||||
|
attr :rest, :global
|
||||||
|
|
||||||
|
def icon_raw(assigns) do
|
||||||
|
assigns =
|
||||||
|
assigns
|
||||||
|
|> set_bulma_classes()
|
||||||
|
|
||||||
|
~H"""
|
||||||
|
<.icon_svg for={@for} height={@size} width={@size} {icon_color(assigns)} {@rest} />
|
||||||
|
"""
|
||||||
|
end
|
||||||
|
|
||||||
defp icon_svg(assigns) do
|
defp icon_svg(assigns) do
|
||||||
base_assigns =
|
base_assigns =
|
||||||
assigns
|
assigns
|
||||||
|
@ -101,19 +119,31 @@ defmodule Liliform.Icon do
|
||||||
color -> color
|
color -> color
|
||||||
end
|
end
|
||||||
|
|
||||||
case assigns.for do
|
auto_color =
|
||||||
"alert" -> [class: "has-text-danger"]
|
case assigns.for do
|
||||||
"info" -> [class: "has-text-info"]
|
"alert" -> [class: "has-text-danger"]
|
||||||
"success" -> [class: "has-text-success"]
|
"info" -> [class: "has-text-info"]
|
||||||
"warning" -> [class: "has-text-warning"]
|
"success" -> [class: "has-text-success"]
|
||||||
"error" -> [class: "has-text-danger"]
|
"warning" -> [class: "has-text-warning"]
|
||||||
"all" -> [color: "magenta"]
|
"error" -> [class: "has-text-danger"]
|
||||||
"compute" -> [color: "blue"]
|
"all" -> [color: "magenta"]
|
||||||
"storage" -> [color: "green"]
|
"compute" -> [color: "blue"]
|
||||||
"network" -> [color: "orange"]
|
"storage" -> [color: "green"]
|
||||||
"system" -> [color: "purple"]
|
"network" -> [color: "orange"]
|
||||||
"account" -> [color: "darkblue"]
|
"system" -> [color: "purple"]
|
||||||
_ -> [color: color]
|
"account" -> [color: "darkblue"]
|
||||||
|
_ -> [color: color]
|
||||||
|
end
|
||||||
|
|
||||||
|
case color do
|
||||||
|
"auto" ->
|
||||||
|
auto_color
|
||||||
|
|
||||||
|
"" ->
|
||||||
|
[]
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
[color: color]
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
[class: "has-text-dark"]
|
[class: "has-text-dark"]
|
||||||
|
|
|
@ -24,6 +24,7 @@ defmodule Liliform.Media do
|
||||||
@doc """
|
@doc """
|
||||||
Renders a media-left.
|
Renders a media-left.
|
||||||
"""
|
"""
|
||||||
|
attr :class, :string, default: "", doc: "additional classes"
|
||||||
attr :rest, :global
|
attr :rest, :global
|
||||||
|
|
||||||
slot :inner_block, required: true
|
slot :inner_block, required: true
|
||||||
|
@ -34,7 +35,7 @@ defmodule Liliform.Media do
|
||||||
|> set_bulma_classes()
|
|> set_bulma_classes()
|
||||||
|
|
||||||
~H"""
|
~H"""
|
||||||
<figure class="media-left" {@rest}>
|
<figure class={["media-left", @class]} {@rest}>
|
||||||
<%= render_slot(@inner_block) %>
|
<%= render_slot(@inner_block) %>
|
||||||
</figure>
|
</figure>
|
||||||
"""
|
"""
|
||||||
|
@ -43,6 +44,7 @@ defmodule Liliform.Media do
|
||||||
@doc """
|
@doc """
|
||||||
Renders a media-content.
|
Renders a media-content.
|
||||||
"""
|
"""
|
||||||
|
attr :class, :string, default: "", doc: "additional classes"
|
||||||
attr :rest, :global
|
attr :rest, :global
|
||||||
|
|
||||||
slot :inner_block, required: true
|
slot :inner_block, required: true
|
||||||
|
@ -53,7 +55,7 @@ defmodule Liliform.Media do
|
||||||
|> set_bulma_classes()
|
|> set_bulma_classes()
|
||||||
|
|
||||||
~H"""
|
~H"""
|
||||||
<div class="media-content" {@rest}>
|
<div class={["media-content", @class]} {@rest}>
|
||||||
<%= render_slot(@inner_block) %>
|
<%= render_slot(@inner_block) %>
|
||||||
</div>
|
</div>
|
||||||
"""
|
"""
|
||||||
|
@ -62,6 +64,7 @@ defmodule Liliform.Media do
|
||||||
@doc """
|
@doc """
|
||||||
Renders a media-right.
|
Renders a media-right.
|
||||||
"""
|
"""
|
||||||
|
attr :class, :string, default: "", doc: "additional classes"
|
||||||
attr :rest, :global
|
attr :rest, :global
|
||||||
|
|
||||||
slot :inner_block, required: true
|
slot :inner_block, required: true
|
||||||
|
@ -72,7 +75,7 @@ defmodule Liliform.Media do
|
||||||
|> set_bulma_classes()
|
|> set_bulma_classes()
|
||||||
|
|
||||||
~H"""
|
~H"""
|
||||||
<div class="media-right" {@rest}>
|
<div class={["media-right", @class]} {@rest}>
|
||||||
<%= render_slot(@inner_block) %>
|
<%= render_slot(@inner_block) %>
|
||||||
</div>
|
</div>
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -50,7 +50,7 @@ defmodule Liliform.Page do
|
||||||
phx-click="filter"
|
phx-click="filter"
|
||||||
phx-value-key={filter.key}
|
phx-value-key={filter.key}
|
||||||
>
|
>
|
||||||
<.icon for={filter.icon} color="auto" />
|
<.icon for={filter.icon} color={if filter.active, do: "gray", else: "auto"} />
|
||||||
</a>
|
</a>
|
||||||
<% end %>
|
<% end %>
|
||||||
</.panel_tabs>
|
</.panel_tabs>
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
defmodule Liliform.Panel do
|
defmodule Liliform.Panel do
|
||||||
use Liliform.Component
|
use Liliform.Component
|
||||||
|
import Liliform.Media
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Renders a panel.
|
Renders a panel.
|
||||||
|
@ -81,6 +82,37 @@ defmodule Liliform.Panel do
|
||||||
"""
|
"""
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Renders panel-media.
|
||||||
|
"""
|
||||||
|
attr :class, :string, default: "", doc: "additional classes"
|
||||||
|
attr :rest, :global
|
||||||
|
|
||||||
|
slot :icon, required: true
|
||||||
|
slot :actions, required: true
|
||||||
|
slot :inner_block, required: true
|
||||||
|
|
||||||
|
def panel_media(assigns) do
|
||||||
|
assigns =
|
||||||
|
assigns
|
||||||
|
|> set_bulma_classes()
|
||||||
|
|
||||||
|
~H"""
|
||||||
|
<div class={["panel-block", @class]} {@rest}>
|
||||||
|
<.media class="is-fullwidth">
|
||||||
|
<.media_left>
|
||||||
|
<%= render_slot(@icon) %>
|
||||||
|
</.media_left>
|
||||||
|
<.media_content class="is-fullwidth">
|
||||||
|
<%= render_slot(@inner_block) %>
|
||||||
|
</.media_content>
|
||||||
|
<.media_right>
|
||||||
|
<%= render_slot(@actions) %>
|
||||||
|
</.media_right>
|
||||||
|
</.media>
|
||||||
|
</div>
|
||||||
|
"""
|
||||||
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Renders a panel-tabs.
|
Renders a panel-tabs.
|
||||||
|
|
Loading…
Reference in a new issue