Skip to content

Commit 8f94a39

Browse files
authored
Merge pull request #136 from yknx4/features/fix-view
feat: migrate main views to LiveView
2 parents e58bc3c + 539ffe8 commit 8f94a39

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+1013
-574
lines changed

config/config.exs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ config :phoenix, :generators,
2626
config :phoenix, :json_library, Jason
2727

2828
config :scrivener_html,
29-
routes_helper: Librecov.Router.Helpers
29+
routes_helper: Librecov.Router.Helpers,
30+
view_style: :bootstrap
3031

3132
config :librecov, PlugBasicAuth, enable: false
3233

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
defmodule Librecov.BuildLive.Commit do
2+
use Surface.Component
3+
alias Librecov.Common.Icon
4+
alias Surface.Components.Link
5+
import Librecov.CommonView
6+
7+
prop build, :struct, required: true
8+
9+
def render(assigns) do
10+
~F"""
11+
{#if @build.commit_message}
12+
{@build.commit_message}
13+
{#if @build.commit_sha}
14+
<Link to={commit_link(@build.project, @build.commit_sha)}>
15+
<Icon family="fab" icon={repository_class(@build.project)} />
16+
</Link>
17+
{/if}
18+
{/if}
19+
"""
20+
end
21+
end
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
defmodule Librecov.RepositoryLive.JobRow do
2+
use Surface.Component
3+
alias Librecov.Router.Helpers, as: Routes
4+
5+
import Librecov.CommonView
6+
alias Librecov.RepositoryLive.CoverageDiff
7+
alias Surface.Components.LiveRedirect
8+
9+
prop job, :struct, required: true
10+
11+
def render(assigns) do
12+
~F"""
13+
<tr>
14+
<td class="text-center fs-sm">
15+
<span class="fw-semibold">
16+
<LiveRedirect label={"##{@job.job_number}"} to={Routes.job_show_path(@socket, :show, @job)} />
17+
</span>
18+
</td>
19+
<td class="text-center fs-sm">
20+
<span class={"fw-semibold text-#{coverage_badge(@job.coverage)}"}>{format_coverage(@job.coverage)}</span>
21+
</td>
22+
<td class="text-start fs-sm">
23+
<span class="fw-semibold">
24+
{#if !is_nil(@job.previous_coverage)}
25+
<CoverageDiff diff={@job.coverage - @job.previous_coverage} />
26+
{#else}
27+
N/A
28+
{/if}
29+
</span>
30+
</td>
31+
<td class="text-start d-none d-sm-table-cell">
32+
{@job.run_at |> human_time_ago}
33+
</td>
34+
<td class="text-center d-none d-sm-table-cell">
35+
{@job.files_count}
36+
</td>
37+
</tr>
38+
"""
39+
end
40+
end
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
defmodule Librecov.BuildLive.Show do
2+
use Librecov.Web, :live_view
3+
import Librecov.CommonView
4+
5+
alias Librecov.Repo
6+
alias Librecov.Build
7+
alias Librecov.BuildLive.Commit
8+
alias Librecov.FileService
9+
10+
@impl true
11+
def mount(_params, _session, socket) do
12+
{:ok, socket}
13+
end
14+
15+
@impl true
16+
def handle_params(%{"id" => id} = params, _, socket) do
17+
build = Repo.get!(Build, id) |> Repo.preload([:jobs, :project])
18+
job_ids = Enum.map(build.jobs, & &1.id)
19+
file_params = FileService.files_with_filter(job_ids, params)
20+
21+
{:noreply,
22+
socket
23+
|> assign(:page_title, page_title(socket.assigns.live_action))
24+
|> assign(:build, build)
25+
|> assign(:file_params, file_params)}
26+
end
27+
28+
defp page_title(:show), do: "Show Build"
29+
defp page_title(:edit), do: "Edit Build"
30+
end
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
<div class="content" id="build-info">
2+
<!-- Quick Overview -->
3+
<div class="row">
4+
<div class="col-lg-12 py-4">
5+
<nav aria-label="breadcrumb">
6+
<ol class="breadcrumb">
7+
<li class="breadcrumb-item"><%= live_redirect @build.project.name, to: ViewHelper.project_path(@socket, :show, @build.project) %></li>
8+
<li class="breadcrumb-item active" aria-current="page">Build #<%= @build.build_number %></li>
9+
</ol>
10+
</nav>
11+
</div>
12+
<div class="col-6 col-lg-3">
13+
<div class="block block-rounded block-link-shadow text-center">
14+
<div class="block-content block-content-full">
15+
<div class="fs-2 fw-semibold text-primary"><%= format_coverage(@build.coverage) %></div>
16+
</div>
17+
<div class="block-content py-2 bg-body-light">
18+
<p class="fw-medium fs-sm text-muted mb-0">
19+
Coverage
20+
</p>
21+
</div>
22+
</div>
23+
</div>
24+
<div class="col-6 col-lg-3">
25+
<div class="block block-rounded block-link-shadow text-center">
26+
<div class="block-content block-content-full">
27+
<div class="fs-2 fw-semibold text-dark"><%= human_time_ago(@build.inserted_at) %></div>
28+
</div>
29+
<div class="block-content py-2 bg-body-light">
30+
<p class="fw-medium fs-sm text-muted mb-0">
31+
Latest Change
32+
</p>
33+
</div>
34+
</div>
35+
</div>
36+
<div class="col-6">
37+
<div class="block block-rounded block-link-shadow text-center">
38+
<div class="block-content block-content-full">
39+
<div class="fs-2 fw-semibold text-dark">
40+
<%= live_component Commit, build: @build %>
41+
</div>
42+
</div>
43+
<div class="block-content py-2 bg-body-light">
44+
<p class="fw-medium fs-sm text-muted mb-0">
45+
Latest Commit
46+
</p>
47+
</div>
48+
</div>
49+
</div>
50+
</div>
51+
<!-- END Quick Overview -->
52+
53+
<!-- All Orders -->
54+
<div class="block block-rounded">
55+
<div class="block-header block-header-default">
56+
<h3 class="block-title">Jobs</h3>
57+
</div>
58+
<div class="block-content">
59+
<!-- All Orders Table -->
60+
<div class="table-responsive">
61+
<table class="table table-borderless table-striped table-vcenter">
62+
<thead>
63+
<tr>
64+
<th class="text-center" style="width: 100px;">Number</th>
65+
<th class="d-none d-sm-table-cell text-center">Coverage</th>
66+
<th>Diff</th>
67+
<th class="d-none d-xl-table-cell">Run Time</th>
68+
<th class="d-none d-xl-table-cell text-center">Files Count</th>
69+
</tr>
70+
</thead>
71+
<tbody>
72+
<%= for job <- @build.jobs do %>
73+
<%= live_component Librecov.RepositoryLive.JobRow, job: job %>
74+
<% end %>
75+
76+
</tbody>
77+
</table>
78+
</div>
79+
<!-- END All Orders Table -->
80+
81+
</div>
82+
</div>
83+
<!-- END All Orders -->
84+
</div>
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
defmodule Librecov.Common.Icon do
2+
use Surface.Component
3+
4+
prop family, :string, required: true
5+
prop icon, :string, required: true
6+
7+
def render(assigns) do
8+
~F"""
9+
<i class={"#{@family} fa-#{@icon}"} />
10+
"""
11+
end
12+
end
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
defmodule Librecov.FileLive.List do
2+
use Surface.Component
3+
import Librecov.CommonView
4+
alias Surface.Components.Link
5+
alias Surface.Components.LivePatch
6+
alias Librecov.Common.Icon
7+
alias Librecov.RepositoryLive.CoverageDiff
8+
alias Librecov.Router.Helpers, as: Routes
9+
import Scrivener.HTML
10+
prop paginator, :struct, required: true
11+
prop files, :list, required: true
12+
prop filters, :list, required: false
13+
prop order, :tuple, required: true
14+
prop path_fn, :fun, required: true
15+
prop path_args, :list, required: true
16+
17+
def raw_filters do
18+
%{
19+
"changed" => "Changed",
20+
"cov_changed" => "Coverage changed",
21+
"covered" => "Covered",
22+
"unperfect" => "Unperfect"
23+
}
24+
end
25+
26+
defp row_order(order, k) do
27+
if elem(order, 0) == k && elem(order, 1) == "desc", do: "asc", else: "desc"
28+
end
29+
30+
defp sort_icon("desc"), do: "down"
31+
defp sort_icon(:desc), do: "down"
32+
defp sort_icon("asc"), do: "up"
33+
defp sort_icon(:asc), do: "up"
34+
35+
def render(assigns) do
36+
order_args = [order_field: elem(assigns.order, 0), order_direction: elem(assigns.order, 1)]
37+
38+
~F"""
39+
<div class="block block-rounded">
40+
<div class="block-header block-header-default">
41+
<h3 class="block-title">Files <span class="small">({@paginator.total_entries})</span></h3>
42+
<div class="block-options">
43+
<div class="dropdown">
44+
<button
45+
type="button"
46+
class="btn-block-option"
47+
id="dropdown-ecom-filters"
48+
data-bs-toggle="dropdown"
49+
aria-haspopup="true"
50+
aria-expanded="false"
51+
>
52+
Filters <i class="fa fa-angle-down ms-1" />
53+
</button>
54+
<div class="dropdown-menu dropdown-menu-end" aria-labelledby="dropdown-ecom-filters" style="">
55+
{#for {k, v} <- raw_filters()}
56+
{#if Enum.any?(@filters, &(&1 == k))}
57+
<LivePatch
58+
class="dropdown-item d-flex align-items-center justify-content-between"
59+
label={v}
60+
to={apply(@path_fn, @path_args ++ [[{:filters, @filters -- [k]} | order_args]])}
61+
/>
62+
{#else}
63+
<LivePatch
64+
class="dropdown-item d-flex align-items-center justify-content-between"
65+
label={v}
66+
to={apply(@path_fn, @path_args ++ [[{:filters, [k | @filters]} | order_args]])}
67+
/>
68+
{/if}
69+
{/for}
70+
</div>
71+
</div>
72+
</div>
73+
</div>
74+
<div class="block-content">
75+
<!-- All Orders Table -->
76+
<div class="table-responsive">
77+
<table class="table table-borderless table-striped table-vcenter">
78+
<thead>
79+
<tr>
80+
{#for {k, v} <- %{"coverage" => "Coverage", "diff" => "Diff", "name" => "Name"}}
81+
<th class="d-none d-sm-table-cell text-center">
82+
<LivePatch to={apply(
83+
@path_fn,
84+
@path_args ++ [[filters: @filters, order_field: k, order_direction: row_order(@order, k)]]
85+
)}>
86+
<span>{v}</span>
87+
<Icon family="fas" icon={"sort-#{elem(@order, 1) |> sort_icon}"} :if={elem(@order, 0) == k} />
88+
</LivePatch>
89+
</th>
90+
{/for}
91+
</tr>
92+
</thead>
93+
<tbody>
94+
{#for file <- @files}
95+
<tr>
96+
<td class="d-none d-sm-table-cell text-center fs-sm">{format_coverage(file.coverage)}</td>
97+
<td class="text-center fs-sm">
98+
<span class="fw-semibold">
99+
<CoverageDiff diff={file.coverage - file.previous_coverage} :if={file.previous_coverage} />
100+
</span>
101+
</td>
102+
103+
<td class="text-center d-none d-xl-table-cell fs-sm">
104+
<Link class="fw-semibold" label={file.name} to={Routes.file_show_path(@socket, :show, file)} />
105+
</td>
106+
</tr>
107+
{/for}
108+
</tbody>
109+
</table>
110+
</div>
111+
<!-- END All Orders Table -->
112+
113+
<!-- Pagination -->
114+
<nav aria-label="Photos Search Navigation" :if={@paginator.total_pages > 1}>
115+
{pagination_links(
116+
@socket,
117+
@paginator,
118+
Enum.drop(@path_args, 2),
119+
path: @path_fn,
120+
action: Enum.at(@path_args, 1),
121+
filters: @filters,
122+
order_field: elem(@order, 0),
123+
order_direction: elem(@order, 1)
124+
)}
125+
</nav>
126+
<!-- END Pagination -->
127+
</div>
128+
</div>
129+
"""
130+
end
131+
end

0 commit comments

Comments
 (0)