This repository has been archived by the owner on Mar 29, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 20
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
740 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
290 changes: 290 additions & 0 deletions
290
modules/portmaster/src/app/pages/dashboard/dashboard.component.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,290 @@ | ||
<div class="w-full gap-4 p-4 dashboard-grid"> | ||
<header class="flex flex-row items-center justify-between w-full" id="header"> | ||
<h1 class="flex flex-col flex-grow text-lg font-light text-white"> | ||
Dashboard | ||
<span class="text-sm font-normal text-secondary">Welcome back | ||
<ng-container *ngIf="!!profile"> | ||
, <span class="text-primary">{{ profile.username }}</span>! | ||
</ng-container> | ||
</span> | ||
</h1> | ||
|
||
<div class="flex flex-row gap-8"> | ||
<ng-container *ngIf="!!profile"> | ||
<div class="flex flex-col text-xs leading-4"> | ||
<span class="font-light text-secondary">Your current plan is <span | ||
class="font-normal text-green-300">{{ profile.current_plan?.name || 'N/A' }}</span></span> | ||
<span class="font-light text-secondary">and ends {{ profile.subscription ? ' in ': ''}} <span | ||
class="font-normal text-primary"> | ||
{{ profile.subscription ? (profile.subscription.ends_at | timeAgo) : 'never'}} | ||
</span></span> | ||
</div> | ||
|
||
<button *ngIf="profile.subscription" | ||
class="text-xs font-normal text-white cursor-pointer btn bg-blue bg-opacity-80 hover:bg-opacity-100 hover:bg-blue"> | ||
Account Details | ||
</button> | ||
|
||
<button *ngIf="!profile.subscription" | ||
class="text-xs font-normal text-white cursor-pointer btn bg-blue bg-opacity-80 hover:bg-opacity-100 hover:bg-blue"> | ||
Subscribe | ||
</button> | ||
|
||
</ng-container> | ||
</div> | ||
</header> | ||
|
||
<!-- Feature Cards --> | ||
<ng-template #featureCard let-data> | ||
<div class="feature-card" [class.disabled]="!!data.disabled"> | ||
<ng-container *ngIf="!!data.disabled"> | ||
<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" class="disabled-bg"> | ||
<defs> | ||
<pattern id="pattern_63Hoo" patternUnits="userSpaceOnUse" width="9.5" height="9.5" | ||
patternTransform="rotate(45)"> | ||
<line x1="0" y="0" x2="0" y2="9.5" stroke="currentColor" stroke-width="1" /> | ||
</pattern> | ||
</defs> | ||
<rect width="100%" height="100%" fill="url(#pattern_63Hoo)" :opacity="1" /> | ||
</svg> | ||
</ng-container> | ||
|
||
<header> | ||
<ng-container *ngTemplateOutlet="data.icon"></ng-container> | ||
<span> | ||
{{ data.title }} | ||
</span> | ||
|
||
<div class="relative flex flex-row self-start flex-grow" *ngIf="!!data.disabled || !!data.beta"> | ||
<div class="flex-grow"></div> | ||
<!-- | ||
<svg *ngIf="!!data.disabled" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" | ||
stroke-width="1.5" stroke="currentColor" class="z-10 w-6 h-6 raltive"> | ||
<path stroke-linecap="round" stroke-linejoin="round" | ||
d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126zM12 15.75h.007v.008H12v-.008z" /> | ||
</svg> | ||
--> | ||
|
||
<div *ngIf="!!data.beta && !data.disabled" | ||
class="absolute top-0 right-0 flex flex-col items-center justify-center gap-0 text-yellow-300"> | ||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" | ||
stroke="currentColor" class="relative z-10 w-6 h-6"> | ||
<path stroke-linecap="round" stroke-linejoin="round" | ||
d="M9.75 3.104v5.714a2.25 2.25 0 01-.659 1.591L5 14.5M9.75 3.104c-.251.023-.501.05-.75.082m.75-.082a24.301 24.301 0 014.5 0m0 0v5.714c0 .597.237 1.17.659 1.591L19.8 15.3M14.25 3.104c.251.023.501.05.75.082M19.8 15.3l-1.57.393A9.065 9.065 0 0112 15a9.065 9.065 0 00-6.23-.693L5 14.5m14.8.8l1.402 1.402c1.232 1.232.65 3.318-1.067 3.611A48.309 48.309 0 0112 21c-2.773 0-5.491-.235-8.135-.687-1.718-.293-2.3-2.379-1.067-3.61L5 14.5" /> | ||
</svg> | ||
<span class="uppercase text-xxs">BETA</span> | ||
</div> | ||
</div> | ||
</header> | ||
<div> | ||
<span | ||
class="font-normal ml-7 text-xxs text-secondary">{{ data.plan ? (data.disabled ? 'Upgrade required' : 'Purchased') : 'Free / Forever'}}</span> | ||
</div> | ||
|
||
<div *ngIf="!data.disabled && !!data.hasToggle" class="absolute right-4 bottom-4"> | ||
<sfng-toggle [ngModel]="true"></sfng-toggle> | ||
</div> | ||
|
||
<div class="ribbon" *ngIf="!!data.disabled && !!data.plan"><span class="ribbon__content">{{ data.plan }}</span> | ||
</div> | ||
</div> | ||
</ng-template> | ||
|
||
<ng-template #spnIcon> | ||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke="currentColor" class="text-green-300"> | ||
<g fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"> | ||
<path | ||
d="M6.488 15.581c.782.781.782 2.048 0 2.829-.782.781-2.049.781-2.83 0-.782-.781-.782-2.048 0-2.829.781-.781 2.048-.781 2.83 0M13.415 3.586c.782.781.782 2.048 0 2.829-.782.781-2.049.781-2.83 0-.782-.781-.782-2.048 0-2.829.781-.781 2.049-.781 2.83 0M20.343 15.58c.782.781.782 2.048 0 2.829-.782.781-2.049.781-2.83 0-.782-.781-.782-2.048 0-2.829.781-.781 2.048-.781 2.83 0"> | ||
</path> | ||
<path | ||
d="M17.721 18.581C16.269 20.071 14.246 21 12 21c-1.146 0-2.231-.246-3.215-.68M4.293 15.152c-.56-1.999-.352-4.21.769-6.151.574-.995 1.334-1.814 2.205-2.449M13.975 5.254c2.017.512 3.834 1.799 4.957 3.743.569.985.899 2.041 1.018 3.103"> | ||
</path> | ||
</g> | ||
</svg> | ||
</ng-template> | ||
|
||
<ng-template #secureDns> | ||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1" stroke="currentColor"> | ||
<path stroke-linecap="round" stroke-linejoin="round" | ||
d="M12 21a9.004 9.004 0 008.716-6.747M12 21a9.004 9.004 0 01-8.716-6.747M12 21c2.485 0 4.5-4.03 4.5-9S14.485 3 12 3m0 18c-2.485 0-4.5-4.03-4.5-9S9.515 3 12 3m0 0a8.997 8.997 0 017.843 4.582M12 3a8.997 8.997 0 00-7.843 4.582m15.686 0A11.953 11.953 0 0112 10.5c-2.998 0-5.74-1.1-7.843-2.918m15.686 0A8.959 8.959 0 0121 12c0 .778-.099 1.533-.284 2.253m0 0A17.919 17.919 0 0112 16.5c-3.162 0-6.133-.815-8.716-2.247m0 0A9.015 9.015 0 013 12c0-1.605.42-3.113 1.157-4.418" /> | ||
</svg> | ||
</ng-template> | ||
|
||
<ng-template #history> | ||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1" stroke="currentColor"> | ||
<path stroke-linecap="round" stroke-linejoin="round" | ||
d="M12 6.042A8.967 8.967 0 006 3.75c-1.052 0-2.062.18-3 .512v14.25A8.987 8.987 0 016 18c2.305 0 4.408.867 6 2.292m0-14.25a8.966 8.966 0 016-2.292c1.052 0 2.062.18 3 .512v14.25A8.987 8.987 0 0018 18a8.967 8.967 0 00-6 2.292m0-14.25v14.25" /> | ||
</svg> | ||
</ng-template> | ||
|
||
<ng-template #bw> | ||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1" stroke="currentColor"> | ||
<path stroke-linecap="round" stroke-linejoin="round" | ||
d="M3 13.125C3 12.504 3.504 12 4.125 12h2.25c.621 0 1.125.504 1.125 1.125v6.75C7.5 20.496 6.996 21 6.375 21h-2.25A1.125 1.125 0 013 19.875v-6.75zM9.75 8.625c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125v11.25c0 .621-.504 1.125-1.125 1.125h-2.25a1.125 1.125 0 01-1.125-1.125V8.625zM16.5 4.125c0-.621.504-1.125 1.125-1.125h2.25C20.496 3 21 3.504 21 4.125v15.75c0 .621-.504 1.125-1.125 1.125h-2.25a1.125 1.125 0 01-1.125-1.125V4.125z" /> | ||
</svg> | ||
|
||
</ng-template> | ||
|
||
<ng-template #support> | ||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1" stroke="currentColor"> | ||
<path stroke-linecap="round" stroke-linejoin="round" | ||
d="M15.75 6a3.75 3.75 0 11-7.5 0 3.75 3.75 0 017.5 0zM4.501 20.118a7.5 7.5 0 0114.998 0A17.933 17.933 0 0112 21.75c-2.676 0-5.216-.584-7.499-1.632z" /> | ||
</svg> | ||
</ng-template> | ||
|
||
<div class="feature-card-container" id="features"> | ||
<label> | ||
Features | ||
<sfng-tipup></sfng-tipup> | ||
</label> | ||
|
||
<div class="feature-card-list"> | ||
<ng-container *ngTemplateOutlet="featureCard; context: {$implicit: { | ||
title: 'Secure DNS', | ||
icon: secureDns, | ||
hasToggle: true, | ||
}}"></ng-container> | ||
|
||
<ng-container *ngTemplateOutlet="featureCard; context: {$implicit: { | ||
title: 'Safing Privacy Network', | ||
icon: spnIcon, | ||
plan: 'unlimited', | ||
hasToggle: true, | ||
}}"></ng-container> | ||
|
||
<ng-container *ngTemplateOutlet="featureCard; context: {$implicit: { | ||
title: 'Bandwidth', | ||
icon: bw, | ||
disabled: false, | ||
plan: 'plus', | ||
beta: true, | ||
}}"></ng-container> | ||
|
||
<ng-container *ngTemplateOutlet="featureCard; context: {$implicit: { | ||
title: 'History', | ||
icon: history, | ||
disabled: true, | ||
plan: 'plus', | ||
}}"></ng-container> | ||
|
||
<ng-container *ngTemplateOutlet="featureCard; context: {$implicit: { | ||
title: 'Priority Support', | ||
icon: support, | ||
disabled: true, | ||
plan: 'unlimited' | ||
}}"></ng-container> | ||
</div> | ||
|
||
<span class="font-light text-xxs text-primary"> | ||
Want more features? | ||
<a href="#" class="font-normal underline text-primary">Upgrade your plan now! 🚀</a> | ||
or | ||
<a href="#" class="font-normal underline text-primary">find out more</a> | ||
</span> | ||
</div> | ||
|
||
|
||
<div class="feature-card-container" id="stats"> | ||
<label> | ||
Statistics | ||
<sfng-tipup></sfng-tipup> | ||
</label> | ||
|
||
<!-- Mini Stats --> | ||
<div class="feature-card-list"> | ||
<div class="mini-stat"> | ||
<label>Trackers Blocked</label> | ||
<span>1.2k</span> | ||
</div> | ||
|
||
<div class="mini-stat"> | ||
<label>Active Connections</label> | ||
<span>{{ activeConnections }}</span> | ||
</div> | ||
|
||
<div class="mini-stat"> | ||
<label>Active Apps</label> | ||
<span>{{ activeProfiles }}</span> | ||
</div> | ||
|
||
<div class="mini-stat"> | ||
<label>Bandwidth</label> | ||
<span class="grid items-center grid-flow-col gap-2 auto-cols-fr"> | ||
<span class="flex flex-row items-center gap-1 p-1 whitespace-nowrap"> | ||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" | ||
stroke="currentColor" class="w-5 h-5 text-secondary"> | ||
<path stroke-linecap="round" stroke-linejoin="round" d="M8.25 6.75L12 3m0 0l3.75 3.75M12 3v18" /> | ||
</svg> | ||
2.5 MB/s | ||
</span> | ||
|
||
<span class="flex flex-row items-center gap-1 p-1 whitespace-nowrap"> | ||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" | ||
stroke="currentColor" class="w-5 h-5 text-secondary"> | ||
<path stroke-linecap="round" stroke-linejoin="round" d="M15.75 17.25L12 21m0 0l-3.75-3.75M12 21V3" /> | ||
</svg> | ||
1 kB/s | ||
</span> | ||
</span> | ||
</div> | ||
|
||
<div class="mini-stat"> | ||
<label>SPN Identities</label> | ||
<span>{{ activeIdentities }}</span> | ||
</div> | ||
</div> | ||
</div> | ||
|
||
<div class="flex flex-col flex-grow gap-4" id="countries"> | ||
<div class="flex-grow feature-card-container"> | ||
<label> | ||
Recent Connections per Country | ||
<sfng-tipup></sfng-tipup> | ||
</label> | ||
<div class="block w-full"> | ||
<ul class="list-none auto-grid-4"> | ||
<li *ngFor="let country of (connectionsPerCountry | keyvalue); trackBy: trackCountry" | ||
[routerLink]="['/monitor']" [queryParams]="{q: 'country:' + country.key}" | ||
(mouseenter)="onCountryHover(country.key)" (mouseleave)="onCountryHover(null)" | ||
class="flex flex-row items-center p-2 bg-gray-300 rounded-md cursor-pointer hover:bg-gray-400"> | ||
<div class="flex flex-row items-center flex-grow gap-2"> | ||
<span class="flex-shrink-0" *ngIf="!!country.key" [appCountryFlags]="country.key"></span> | ||
<span | ||
class="overflow-hidden text-xs text-secondary whitespace-nowrap">{{ countryNames[country.key] || country.key || 'N/A' }}</span> | ||
</div> | ||
<span class="ml-2">{{ country.value }}</span> | ||
</li> | ||
</ul> | ||
</div> | ||
</div> | ||
|
||
<div class="flex-grow feature-card-container" id="blocked"> | ||
<label> | ||
Recently Blocked Applications | ||
<sfng-tipup></sfng-tipup> | ||
</label> | ||
<div class="block w-full"> | ||
<ul class="list-none auto-grid-3"> | ||
<li *ngFor="let profile of (blockedProfiles | keyvalue); trackBy: trackApp" | ||
(mouseenter)="onProfileHover(profile.key)" (mouseleave)="onProfileHover(null)" | ||
class="flex flex-row items-center p-2 bg-gray-300 rounded-md cursor-pointer hover:bg-gray-400"> | ||
<div class="flex flex-row items-center flex-grow gap-2"> | ||
<app-icon [profile]="profile.value.profile"></app-icon> | ||
<span class="text-xs text-secondary">{{ profile.value.profile.Name }}</span> | ||
</div> | ||
{{ profile.value.count }} | ||
</li> | ||
</ul> | ||
</div> | ||
</div> | ||
</div> | ||
|
||
<div class="flex-grow feature-card-container" id="connmap"> | ||
<label> | ||
Connection Targets | ||
<sfng-tipup></sfng-tipup> | ||
</label> | ||
<div class="block w-full h-96" #map> | ||
</div> | ||
</div> | ||
</div> |
Oops, something went wrong.