Skip to content

Commit

Permalink
Dashboard and notifications
Browse files Browse the repository at this point in the history
dennisreimann committed Apr 18, 2024

Verified

This commit was signed with the committer’s verified signature.
1 parent 8d1549f commit b8579c6
Showing 7 changed files with 230 additions and 39 deletions.
57 changes: 57 additions & 0 deletions BTCPayApp.UI/Components/DateDisplay.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
@using System.Globalization

<time datetime="@DateTimeOffset.ToString("o", CultureInfo.InvariantCulture)"
data-relative="@ToTimeAgo(DateTimeOffset)"
data-initial="@Format"
@attributes="InputAttributes">
@GetDisplayDate()
</time>

@code {
public enum DateDisplayFormat
{
Relative,
Localized
}

[Parameter, EditorRequired]
public DateTimeOffset DateTimeOffset { get; set; }

[Parameter]
public DateDisplayFormat Format { get; set; }

[Parameter(CaptureUnmatchedValues = true)]
public Dictionary<string, object>? InputAttributes { get; set; }

private static string ToTimeAgo(DateTimeOffset date) => ToTimeAgo(DateTimeOffset.UtcNow - date);

private static string ToTimeAgo(DateTime date) => ToTimeAgo(DateTimeOffset.UtcNow - date);

private static string ToTimeAgo(TimeSpan diff) =>
diff.TotalSeconds > 0 ? $"{TimeString(diff)} ago" : $"in {TimeString(diff.Negate())}";

private static string TimeString(TimeSpan timeSpan)
{
if (timeSpan.TotalMinutes < 1)
{
return $"{(int)timeSpan.TotalSeconds} second{Plural((int)timeSpan.TotalSeconds)}";
}
if (timeSpan.TotalHours < 1)
{
return $"{(int)timeSpan.TotalMinutes} minute{Plural((int)timeSpan.TotalMinutes)}";
}
return timeSpan.Days < 1
? $"{(int)timeSpan.TotalHours} hour{Plural((int)timeSpan.TotalHours)}"
: $"{(int)timeSpan.TotalDays} day{Plural((int)timeSpan.TotalDays)}";
}

private static string Plural(int value)
{
return value == 1 ? string.Empty : "s";
}

private string GetDisplayDate() =>
Format == DateDisplayFormat.Relative
? ToTimeAgo(DateTimeOffset)
: DateTimeOffset.ToString("g", CultureInfo.InvariantCulture);
}
47 changes: 47 additions & 0 deletions BTCPayApp.UI/Components/NotificationItem.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<a href="#" class="notification d-flex align-items-center dropdown-item border-bottom border-light gap-3 py-3 px-4 @(Seen ? null : "bg-light")">
<div>
<Icon Symbol="@NotificationIcon(Type)" />
</div>
<div class="content">
<div class="fw-semibold text-wrap">
@Body
</div>
<small class="text-muted">
<DateDisplay DateTimeOffset="Created" />
</small>
</div>
</a>

@code {
[Parameter, EditorRequired]
public string? Id { get; set; }

[Parameter, EditorRequired]
public string? Type { get; set; }

[Parameter, EditorRequired]
public DateTimeOffset Created { get; set; }

[Parameter, EditorRequired]
public string? Body { get; set; }

[Parameter]
public bool Seen { get; set; }

private static string NotificationIcon(string type)
{
return type switch
{
"invoice_expired" => "notifications-invoice-failure",
"invoice_expiredpaidpartial" => "notifications-invoice-failure",
"invoice_failedtoconfirm" => "notifications-invoice-failure",
"invoice_confirmed" => "notifications-invoice-settled",
"invoice_paidafterexpiration" => "notifications-invoice-settled",
"external-payout-transaction" => "notifications-payout",
"payout_awaitingapproval" => "notifications-payout",
"payout_awaitingpayment" => "notifications-payout-approved",
"newversion" => "notifications-new-version",
_ => "note"
};
}
}
3 changes: 3 additions & 0 deletions BTCPayApp.UI/Components/NotificationItem.razor.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
::deep .icon {
--icon-size: 2rem;
}
91 changes: 59 additions & 32 deletions BTCPayApp.UI/Pages/DashboardPage.razor
Original file line number Diff line number Diff line change
@@ -10,9 +10,14 @@

<AuthorizeView>
<Authorized>
<section class="container d-flex align-items-center justify-content-between py-4">
<div class="fw-semibold">
@context.User.Identity?.Name!
<section class="container d-flex align-items-center justify-content-between py-3">
<div class="d-flex align-items-center justify-content-between gap-2">
<div class="fw-semibold">
@context.User.Identity?.Name
</div>
<NavLink class="btn btn-sm rounded-pill py-1 px-3 btn-outline-light" href="@Routes.Logout" Match="NavLinkMatch.All">
<span>Logout</span>
</NavLink>
</div>
<div class="notifications">
<NavLink class="btn btn-light rounded-circle" href="@Routes.Notifications" Match="NavLinkMatch.All">
@@ -21,7 +26,7 @@
</NavLink>
</div>
</section>
<section class="container d-flex flex-column gap-4">
<section class="container d-flex flex-column gap-4 py-3">
<div class="amount text-center">
<div class="sats fw-bold fs-1">615,000,000 sats</div>
<div class="fiat fw-semibold text-muted">$174,465.79</div>
@@ -37,40 +42,62 @@
</NavLink>
</nav>
</section>
<section class="container">
<h2>Getting started</h2>
<ul class="list-group list-group-flush list-group-links">
<li class="list-group-item">
<NavLink class="nav-link" href="#" Match="NavLinkMatch.All">
<span>Configure node</span>
<Icon symbol="caret-right"/>
</NavLink>
</li>
<li class="list-group-item">
<NavLink class="nav-link" href="#" Match="NavLinkMatch.All">
<span>Set up wallet</span>
<Icon symbol="caret-right"/>
</NavLink>
</li>
<section class="container py-3">
<div class="box">
<h2>Getting started</h2>
<ul class="list-group list-group-flush list-group-links">
<li class="list-group-item">
<NavLink class="nav-link" href="#" Match="NavLinkMatch.All">
<span>Configure node</span>
<Icon symbol="caret-right"/>
</NavLink>
</li>
<li class="list-group-item">
<NavLink class="nav-link" href="#" Match="NavLinkMatch.All">
<span>Set up wallet</span>
<Icon symbol="caret-right"/>
</NavLink>
</li>
<li class="list-group-item">
<NavLink class="nav-link" href="#" Match="NavLinkMatch.All">
<span>Set up recovery tools</span>
<Icon symbol="caret-right"/>
</NavLink>
</li>
<li class="list-group-item">
<NavLink class="nav-link" href="#" Match="NavLinkMatch.All">
<span>Configure LSP</span>
<Icon symbol="caret-right"/>
</NavLink>
</li>
</ul>
</div>
</section>
<section class="container py-3">
<h2>Recent Activity</h2>
<ul class="list-group list-group-flush">
<li class="list-group-item">
<NavLink class="nav-link" href="#" Match="NavLinkMatch.All">
<span>Set up recovery tools</span>
<Icon symbol="caret-right"/>
</NavLink>
<div class="d-flex align-items-center justify-content-between">
<span>3940..CkGJ</span>
<span class="text-end">$29.13</span>
</div>
<div class="d-flex align-items-center justify-content-between">
<span class="text-muted">March 21 at 6:15am</span>
<span class="text-muted text-end">177,032 sats</span>
</div>
</li>
<li class="list-group-item">
<NavLink class="nav-link" href="#" Match="NavLinkMatch.All">
<span>Configure LSP</span>
<Icon symbol="caret-right"/>
</NavLink>
<div class="d-flex align-items-center justify-content-between">
<span>CkGJ..3940</span>
<span class="text-end">$29.13</span>
</div>
<div class="d-flex align-items-center justify-content-between">
<span class="text-muted">March 21 at 6:15am</span>
<span class="text-muted text-end">177,032 sats</span>
</div>
</li>
</ul>
</section>
<section class="container">
<NavLink class="btn btn-outline-danger" href="@Routes.Logout" Match="NavLinkMatch.All">
<span>Logout</span>
</NavLink>
</section>
</Authorized>
<Authorizing>
<section class="d-flex justify-content-center min-vh-100">
4 changes: 4 additions & 0 deletions BTCPayApp.UI/Pages/DashboardPage.razor.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
h2 {
font-size: var(--btcpay-font-size-m);
font-weight: var(--btcpay-font-weight-semibold);
}
53 changes: 51 additions & 2 deletions BTCPayApp.UI/Pages/NotificationsPage.razor
Original file line number Diff line number Diff line change
@@ -10,19 +10,68 @@
<h1>Notifications</h1>
</Titlebar>
</SectionContent>
<section class="container">

<section>
@if (Model.Notifications.Any())
{
<div class="notifications">
@foreach (var n in Model.Notifications)
{
<NotificationItem Id="@n.Id" Type="@n.Type" Created="@n.Created" Seen="@n.Seen" Body="@n.Body"/>
}
</div>
}
else
{
<p class="text-muted">There are no transactions, yet.</p>
}
</section>

@code {
private NotificationsModel Model { get; set; } = new();

protected override async Task OnInitializedAsync()
{
Model.Notifications =
[
new Notification
{
Id = "1",
Type = "invoice_confirmed",
Created = DateTimeOffset.Now - TimeSpan.FromSeconds(21),
Body = "Invoice GZ090Z03 is settled.",
Seen = false
},
new Notification
{
Id = "2",
Type = "payout_awaitingapproval",
Created = DateTimeOffset.Now - TimeSpan.FromHours(6),
Body = "A new payout is awaiting your approval.",
Seen = true
},
new Notification
{
Id = "3",
Type = "invoice_failedtoconfirm",
Created = DateTimeOffset.Now - TimeSpan.FromDays(3),
Body = "Invoice GZ090Z03 has payments that failed to confirm on time.",
Seen = true
}
];
}

private class NotificationsModel
{
public string[] Notifications { get; set; } = [];
public Notification[] Notifications { get; set; } = [];
}

private class Notification
{
public string? Id { get; set; }
public string? Type { get; set; }
public DateTimeOffset Created { get; set; }
public string? Body { get; set; }
public bool Seen { get; set; }
}
}
14 changes: 9 additions & 5 deletions BTCPayApp.UI/wwwroot/css/global.css
Original file line number Diff line number Diff line change
@@ -56,10 +56,6 @@
min-height: 100dvh !important;
}

main section + section {
margin-top: 1.5rem;
}

.tile {
--section-padding: 1.5rem;
--section-border-radius: var(--btcpay-border-radius-l);
@@ -114,10 +110,18 @@ main section + section {
color: var(--btcpay-success-text);
}

/* Box */
.box {
padding: var(--btcpay-space-m);
background: var(--btcpay-light);
border-radius: var(--btcpay-border-radius-xl);
}

/* List Group */
.list-group-flush.list-group-links .list-group-item {
.list-group-flush .list-group-item {
--btcpay-list-group-item-padding-y: .75rem;
--btcpay-list-group-item-padding-x: 0;
--btcpay-list-group-bg: transparent;
}

.list-group-flush.list-group-links .list-group-item > a {

0 comments on commit b8579c6

Please sign in to comment.