Added RWD to new HTTP Proxy UI

This commit is contained in:
Toby Chui 2025-06-11 21:24:43 +08:00
parent 7164b74d4a
commit 6d0c0be8c2
6 changed files with 82 additions and 257 deletions

View File

@ -51,7 +51,7 @@
padding: 1.4em; padding: 1.4em;
border-radius: .6em; border-radius: .6em;
box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.2), 0px 8px 16px rgba(0, 0, 0, 0.2); box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.2), 0px 8px 16px rgba(0, 0, 0, 0.2);
z-index: 9; z-index: 8;
max-width: 840px; max-width: 840px;
} }
@ -104,6 +104,30 @@
height: 80vh; height: 80vh;
border-radius: 0; border-radius: 0;
overflow-y: scroll; overflow-y: scroll;
overflow-x: hidden;
width: 100%;
}
/* Override the ui grid */
#httprpEditModalSideMenu{
width: 16% !important;
}
#httprpEditModalContentWindow{
width: 84% !important;
}
#httprpEditModal{
padding-left: 0;
padding-right: 0;
}
.editorSideMenuText{
display:none;
}
.hrpedit_menu_item{
height: 2.4em;
} }
.httpProxyEditClosePC{ .httpProxyEditClosePC{
@ -111,6 +135,9 @@
} }
.httpProxyEditCloseMobile{ .httpProxyEditCloseMobile{
display:block !important; display:block !important;
position: absolute;
left: 1.25em !important;
bottom: 1em;
} }
} }
@ -130,7 +157,7 @@
width: 100%; width: 100%;
height: 100%; height: 100%;
background-color: rgba(0, 0, 0, 0.5); background-color: rgba(0, 0, 0, 0.5);
z-index: 8; z-index: 7;
backdrop-filter: blur(3px); backdrop-filter: blur(3px);
} }
@ -192,41 +219,42 @@
<!-- Modal for editing a HTTP Proxy Rule --> <!-- Modal for editing a HTTP Proxy Rule -->
<div id="httprpEditModalWrapper"> <div id="httprpEditModalWrapper">
<div id="httprpEditModal" editing-host=""> <div id="httprpEditModal" editing-host="">
<div class="ui stackable grid" style="height:100%;"> <div class="ui grid" style="height:100%;">
<div class="four wide column"> <div id="httprpEditModalSideMenu" class="four wide column">
<div class="ui secondary fluid vertical menu"> <div class="ui secondary fluid vertical menu">
<a class="active item hrpedit_menu_item" cfgpage="downstream"> <a class="active item hrpedit_menu_item" cfgpage="downstream">
<i class="angle double white right icon"></i> Downstream <i class="angle double white right icon"></i> <span class="editorSideMenuText">Downstream</span>
</a> </a>
<a class="item hrpedit_menu_item" cfgpage="upstream"> <a class="item hrpedit_menu_item" cfgpage="upstream">
<i class="angle double left icon"></i> Upstream <i class="angle double left icon"></i> <span class="editorSideMenuText">Upstream</span>
</a> </a>
<a class="item hrpedit_menu_item" cfgpage="vdirs"> <a class="item hrpedit_menu_item" cfgpage="vdirs">
<i class="angle folder icon"></i> Virtual Directory <i class="angle folder icon"></i> <span class="editorSideMenuText">Virtual Directory</span>
</a> </a>
<a class="item hrpedit_menu_item" cfgpage="alias"> <a class="item hrpedit_menu_item" cfgpage="alias">
<i class="at icon"></i> Alias <i class="at icon"></i> <span class="editorSideMenuText">Alias</span>
</a> </a>
<a class="item hrpedit_menu_item" cfgpage="ssl"> <a class="item hrpedit_menu_item" cfgpage="ssl">
<i class="lock icon"></i> TLS / SSL <i class="lock icon"></i> <span class="editorSideMenuText">TLS / SSL</span>
</a> </a>
<a class="item hrpedit_menu_item" cfgpage="headers"> <a class="item hrpedit_menu_item" cfgpage="headers">
<i class="heading icon"></i> Headers <i class="heading icon"></i> <span class="editorSideMenuText">Headers</span>
</a> </a>
<a class="item hrpedit_menu_item" cfgpage="accessrule"> <a class="item hrpedit_menu_item" cfgpage="accessrule">
<i class="star icon"></i> Access Rules <i class="star icon"></i> <span class="editorSideMenuText">Access Rules</span>
</a> </a>
<a class="item hrpedit_menu_item" cfgpage="security"> <a class="item hrpedit_menu_item" cfgpage="security">
<i class="key icon"></i> Security <i class="key icon"></i> <span class="editorSideMenuText">Security</span>
</a> </a>
<a class="item hrpedit_menu_item" cfgpage="tags"> <a class="item hrpedit_menu_item" cfgpage="tags">
<i class="tags icon"></i> Tags <i class="tags icon"></i> <span class="editorSideMenuText">Tags</span>
</a> </a>
</div> </div>
<button class="ui basic fluid button httpProxyEditClosePC" onclick="closeHttpRuleEditor();">Close</button> <button class="ui basic fluid button httpProxyEditClosePC" onclick="closeHttpRuleEditor();">Close</button>
<button class="ui basic icon circular button httpProxyEditCloseMobile" onclick="closeHttpRuleEditor();"><i class="ui times icon"></i></button>
</div> </div>
<div class="twelve wide column"> <div id="httprpEditModalContentWindow" class="twelve wide column">
<div style="height:100%;"> <div style="height:100%;">
<!-- Downstream --> <!-- Downstream -->
<div class="rpconfig_content" rpcfg="downstream"> <div class="rpconfig_content" rpcfg="downstream">
@ -240,11 +268,12 @@
<div class="ui divider"></div> <div class="ui divider"></div>
<div class="downstream_action_list"> <div class="downstream_action_list">
<div class="ui checkbox" style="margin-top: 0.4em;"> <div class="ui checkbox" style="margin-top: 0.4em;">
<input type="checkbox" class="BypassGlobalTLS" ${subd.BypassGlobalTLS?"checked":""}> <input type="checkbox" class="BypassGlobalTLS">
<label>Allow plain HTTP access<br> <label>Allow plain HTTP access<br>
<small>Allow inbound connections without TLS/SSL</small></label> <small>Allow inbound connections without TLS/SSL</small></label>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<!-- Upstream --> <!-- Upstream -->
@ -260,13 +289,7 @@
<label>Monitor Uptime<br> <label>Monitor Uptime<br>
<small>Enable active uptime monitor and auto disable upstreams that are offline</small></label> <small>Enable active uptime monitor and auto disable upstreams that are offline</small></label>
</div> </div>
<div class="ui basic advance segment" style="padding: 0.4em !important; border-radius: 0.4em;"> <br>
<div class="ui endpointAdvanceConfig accordion" style="padding-right: 0.6em;">
<div class="title">
<i class="dropdown icon"></i>
Advanced Settings
</div>
<div class="content">
<div class="ui checkbox" style="margin-top: 0.4em;"> <div class="ui checkbox" style="margin-top: 0.4em;">
<input type="checkbox" class="UseStickySession"> <input type="checkbox" class="UseStickySession">
<label>Use Sticky Session<br> <label>Use Sticky Session<br>
@ -280,9 +303,6 @@
</div> </div>
</div> </div>
</div> </div>
</div>
</div>
</div>
<!-- Virtual Directories--> <!-- Virtual Directories-->
<div class="rpconfig_content" rpcfg="vdirs"> <div class="rpconfig_content" rpcfg="vdirs">
<div class="ui segment"> <div class="ui segment">
@ -353,7 +373,7 @@
</div> </div>
</div> </div>
<br> <br>
<button class="ui basic compact tiny button editBasicAuthCredentialsBtn" style="margin-left: 0.4em; margin-top: 0.4em;"><i class="ui blue user circle icon"></i> Basic Auth Credentials</button> <button class="ui basic compact small button editBasicAuthCredentialsBtn" style="margin-left: 0.4em; margin-top: 0.4em;"><i class="ui blue user circle icon"></i> Basic Auth Credentials</button>
<div class="ui divider"></div> <div class="ui divider"></div>
<!-- Rate Limits--> <!-- Rate Limits-->
@ -377,7 +397,6 @@
</iframe> </iframe>
</div> </div>
</div> </div>
<button class="ui basic fluid button httpProxyEditCloseMobile" onclick="closeHttpRuleEditor();">Close</button>
<!-- Editor Side Wrapper --> <!-- Editor Side Wrapper -->
<div class="editor_side_wrapper" style="display:none;"> <div class="editor_side_wrapper" style="display:none;">
<a class="editor_back_button"> <a class="editor_back_button">
@ -656,206 +675,9 @@
var row = $('tr[eptuuid="' + uuid + '"]'); var row = $('tr[eptuuid="' + uuid + '"]');
var payload = $(row).attr("payload"); var payload = $(row).attr("payload");
payload = JSON.parse(decodeURIComponent(payload)); payload = JSON.parse(decodeURIComponent(payload));
//Show the HTTP Proxy Rule Editor Modal
initHttpProxyRuleEditorModal(payload); initHttpProxyRuleEditorModal(payload);
return; return;
/*
var columns = row.find('td[data-label]');
console.log(payload);
columns.each(function(index) {
var column = $(this);
var oldValue = column.text().trim();
if ($(this).attr("editable") == "false"){
//This col do not allow edit. Skip
return;
}
// Create an input element based on the column content
var input;
var datatype = $(this).attr("datatype");
if (datatype == "domain"){
let useStickySessionChecked = "";
if (payload.UseStickySession){
useStickySessionChecked = "checked";
}
let enableUptimeMonitor = "";
//Note the config file store the uptime monitor as disable, so we need to reverse the logic
if (!payload.DisableUptimeMonitor){
enableUptimeMonitor = "checked";
}
input = `<button class="ui basic compact tiny button" style="margin-left: 0.4em; margin-top: 1em;" onclick="editUpstreams('${uuid}');"><i class="grey server icon"></i> Edit Upstreams</button>
<div class="ui divider"></div>
<div class="ui checkbox" style="margin-top: 0.4em;">
<input type="checkbox" class="UseStickySession" ${useStickySessionChecked}>
<label>Use Sticky Session<br>
<small>Enable stick session on load balancing</small></label>
</div>
<div class="ui checkbox" style="margin-top: 0.4em;">
<input type="checkbox" class="EnableUptimeMonitor" ${enableUptimeMonitor}>
<label>Monitor Uptime<br>
<small>Enable active uptime monitor</small></label>
</div>
`;
column.append(input);
$(column).find(".upstreamList").addClass("editing");
}else if (datatype == "vdir"){
//Append a quick access button for vdir page
column.append(`<button class="ui basic tiny button" style="margin-left: 0.4em; margin-top: 0.4em;" onclick="quickEditVdir('${uuid}');">
<i class="ui yellow folder icon"></i> Edit Virtual Directories
</button>`);
}else if (datatype == "tags"){
column.append(`
<div class="ui divider"></div>
<button class="ui basic compact fluid tiny button" style="margin-left: 0.4em; margin-top: 0.4em;" onclick="editTags('${uuid}');"><i class="ui purple tag icon"></i> Edit tags</button>
`);
}else if (datatype == "advanced"){
let authProvider = payload.AuthenticationProvider.AuthMethod;
let skipWebSocketOriginCheck = payload.SkipWebSocketOriginCheck;
let wsCheckstate = "";
if (skipWebSocketOriginCheck){
wsCheckstate = "checked";
}
let requireRateLimit = payload.RequireRateLimit;
let rateLimitCheckState = "";
if (requireRateLimit){
rateLimitCheckState = "checked";
}
let rateLimit = payload.RateLimit;
if (rateLimit == 0){
//This value is not set. Make it default to 100
rateLimit = 100;
}
let rateLimitDisableState = "";
if (!payload.RequireRateLimit){
rateLimitDisableState = "disabled";
}
column.empty().append(`
<div class="grouped fields authProviderPicker">
<label><b>Authentication Provider</b></label>
<div class="field">
<div class="ui radio checkbox">
<input type="radio" value="0" name="authProviderType" ${authProvider==0x0?"checked":""}>
<label>None (Anyone can access)</label>
</div>
</div>
<div class="field">
<div class="ui radio checkbox">
<input type="radio" value="1" name="authProviderType" ${authProvider==0x1?"checked":""}>
<label>Basic Auth</label>
</div>
</div>
<div class="field">
<div class="ui radio checkbox">
<input type="radio" value="2" name="authProviderType" ${authProvider==0x2?"checked":""}>
<label>Forward Auth</label>
</div>
</div>
<div class="field">
<div class="ui radio checkbox">
<input type="radio" value="3" name="authProviderType" ${authProvider==0x3?"checked":""}>
<label>OAuth2</label>
</div>
</div>
</div>
<button class="ui basic compact tiny button" style="margin-left: 0.4em; margin-top: 0.4em;" onclick="editBasicAuthCredentials('${uuid}');"><i class="ui blue user circle icon"></i> Edit Credentials</button>
<button class="ui basic compact tiny button" style="margin-left: 0.4em; margin-top: 0.4em;" onclick="editCustomHeaders('${uuid}');"><i class="heading icon"></i> Custom Headers</button>
<div class="ui basic advance segment" style="padding: 0.4em !important; border-radius: 0.4em;">
<div class="ui endpointAdvanceConfig accordion" style="padding-right: 0.6em;">
<div class="title">
<i class="dropdown icon"></i>
Security Options
</div>
<div class="content">
<div class="ui checkbox" style="margin-top: 0.4em;">
<input type="checkbox" onchange="handleToggleRateLimitInput();" class="RequireRateLimit" ${rateLimitCheckState}>
<label>Require Rate Limit<br>
<small>Check this to enable rate limit on this inbound hostname</small></label>
</div><br>
<div class="ui mini right labeled fluid input ${rateLimitDisableState}" style="margin-top: 0.4em;">
<input type="number" class="RateLimit" value="${rateLimit}" min="1" >
<label class="ui basic label">
req / sec / IP
</label>
</div>
</div>
</div>
<div>
`);
$('.authProviderPicker .ui.checkbox').checkbox();
} else if (datatype == "ratelimit"){
column.empty().append(`
<div class="ui checkbox" style="margin-top: 0.4em;">
<input type="checkbox" class="RequireRateLimit" ${checkstate}>
<label>Require Rate Limit</label>
</div>
<div class="ui mini fluid input">
<input type="number" class="RateLimit" value="${rateLimit}" placeholder="100" min="1" max="1000" >
</div>
`);
}else if (datatype == 'action'){
column.empty().append(`
<button title="Save" onclick="saveProxyInlineEdit('${uuid.hexEncode()}');" class="ui basic small icon circular button inlineEditActionBtn"><i class="ui green save icon"></i></button>
<button title="Cancel" onclick="exitProxyInlineEdit();" class="ui basic small icon circular button inlineEditActionBtn"><i class="ui remove icon"></i></button>
`);
}else if (datatype == "inbound"){
let originalContent = $(column).html();
//Check if this host is covered within one of the certificates. If not, show the icon
let enableQuickRequestButton = true;
let domains = [payload.RootOrMatchingDomain]; //Domain for getting certificate if needed
for (var i = 0; i < payload.MatchingDomainAlias.length; i++){
let thisAliasName = payload.MatchingDomainAlias[i];
domains.push(thisAliasName);
}
//Check if the domain or alias contains wildcard, if yes, disabled the get certificate button
if (payload.RootOrMatchingDomain.indexOf("*") > -1){
enableQuickRequestButton = false;
}
if (payload.MatchingDomainAlias != undefined){
for (var i = 0; i < payload.MatchingDomainAlias.length; i++){
if (payload.MatchingDomainAlias[i].indexOf("*") > -1){
enableQuickRequestButton = false;
break;
}
}
}
//encode the domain to DOM
let certificateDomains = encodeURIComponent(JSON.stringify(domains));
column.empty().append(`${originalContent}
<div class="ui divider"></div>
<div class="ui checkbox" style="margin-top: 0.4em;">
<input type="checkbox" class="BypassGlobalTLS" ${payload.BypassGlobalTLS?"checked":""}>
<label>Allow plain HTTP access<br>
<small>Allow inbound connections without TLS/SSL</small></label>
</div><br>
<button class="ui basic compact tiny button" style="margin-left: 0.4em; margin-top: 0.4em;" onclick="editAliasHostnames('${uuid}');"><i class=" blue at icon"></i> Alias</button>
<button class="ui basic compact tiny button" style="margin-left: 0.4em; margin-top: 0.4em;" onclick="editAccessRule('${uuid}');"><i class="ui filter icon"></i> Access Rule</button>
<button class="ui basic compact tiny ${enableQuickRequestButton?"":"disabled"} button" style="margin-left: 0.4em; margin-top: 0.4em;" onclick="requestCertificateForExistingHost('${uuid}', '${certificateDomains}', this);"><i class="green lock icon"></i> Get Certificate</button>
`);
$(".hostAccessRuleSelector").dropdown();
}else{
//Unknown field. Leave it untouched
}
});
$(".endpointAdvanceConfig").accordion();
$("#httpProxyList").find(".editBtn").addClass("disabled");
*/
} }
//handleToggleRateLimitInput will get trigger if the "require rate limit" checkbox //handleToggleRateLimitInput will get trigger if the "require rate limit" checkbox
@ -1127,6 +949,8 @@
}); });
function closeHttpRuleEditor(){ function closeHttpRuleEditor(){
// Fixing a bug in semantic ui that when an element fade in/out on top of checkbox
// the checkbox suddently flash on top of the fading element
$(".ui.toggle.tiny.fitted.checkbox").css("z-index", 0); $(".ui.toggle.tiny.fitted.checkbox").css("z-index", 0);
$("#httprpEditModalWrapper").fadeOut("fast", function(){ $("#httprpEditModalWrapper").fadeOut("fast", function(){
$(".ui.toggle.tiny.fitted.checkbox").css("z-index", "auto"); $(".ui.toggle.tiny.fitted.checkbox").css("z-index", "auto");
@ -1316,6 +1140,10 @@
break; break;
} }
editor.find(".authProviderPicker input[type='radio']").off("change").on("change", function() {
saveProxyInlineEdit(uuid);
});
editor.find(".editBasicAuthCredentialsBtn").off("click").on("click", function(){ editor.find(".editBasicAuthCredentialsBtn").off("click").on("click", function(){
editBasicAuthCredentials(uuid); editBasicAuthCredentials(uuid);
}); });
@ -1416,7 +1244,10 @@
} }
function hideEditorSideWrapper(){ function hideEditorSideWrapper(){
$("#httprpEditModal .editor_side_wrapper").fadeOut("fast"); $("#httprpEditModal .editor_side_wrapper").fadeOut("fast", function(){
// Reset the side wrapper frame URL to prevent stale content
$("#httprpEditModal .editor_side_wrapper .wrapper_frame").attr('src', 'snippet/placeholder.html');
});
} }

View File

@ -184,7 +184,8 @@ body.darkTheme .ui.input input::placeholder {
body.darkTheme .ui.label, body.darkTheme .ui.label,
body.darkTheme .ui.label .detail, body.darkTheme .ui.label .detail,
body.darkTheme .ui.label .icon { body.darkTheme .ui.label .icon {
color: #ffffff !important; background-color: var(--buttom_toggle_disabled);
color: var(--text_color) !important;
} }
body.darkTheme .advanceoptions .title { body.darkTheme .advanceoptions .title {

View File

@ -25,7 +25,7 @@
<div class="scrolling content ui form"> <div class="scrolling content ui form">
<div id="inlineEditBasicAuthCredentials" class="field"> <div id="inlineEditBasicAuthCredentials" class="field">
<p>Enter alias hostname or wildcard matching keywords for <code class="epname"></code></p> <p>Enter alias hostname or wildcard matching keywords for <code class="epname"></code></p>
<table class="ui very basic compacted unstackable celled table"> <table class="ui basic very compact unstackable celled table">
<thead> <thead>
<tr> <tr>
<th>Alias Hostname</th> <th>Alias Hostname</th>
@ -161,7 +161,7 @@
} }
$("#inlineEditTable").append(`<tr> $("#inlineEditTable").append(`<tr>
<td>${domainLink}</td> <td>${domainLink}</td>
<td><button class="ui basic button" onclick="removeAliasDomain('${aliasDomain}');"><i class="red remove icon"></i> Remove</button></td> <td><button class="ui basic mini circular icon button" onclick="removeAliasDomain('${aliasDomain}');"><i class="red trash icon"></i></button></td>
</tr>`); </tr>`);
}); });

View File

@ -14,18 +14,11 @@
<script src="../script/darktheme.js"></script> <script src="../script/darktheme.js"></script>
<br> <br>
<div class="ui container"> <div class="ui container">
<div class="ui header">
<div class="content">
Basic Auth Settings
<div class="sub header" id="epname"></div>
</div>
</div>
<div class="ui divider"></div>
<h3 class="ui header">Basic Auth Credential</h3> <h3 class="ui header">Basic Auth Credential</h3>
<div class="scrolling content ui form"> <div class="scrolling content ui form">
<div id="inlineEditBasicAuthCredentials" class="field"> <div id="inlineEditBasicAuthCredentials" class="field">
<p>Enter the username and password for allowing them to access this proxy endpoint</p> <p>Enter the username and password for allowing them to access this proxy endpoint</p>
<table class="ui very basic compacted unstackable celled table"> <table class="ui basic very compacted unstackable celled table">
<thead> <thead>
<tr> <tr>
<th>Username</th> <th>Username</th>
@ -56,7 +49,7 @@
<h3 class="ui header">Authentication Exclusion Paths</h3> <h3 class="ui header">Authentication Exclusion Paths</h3>
<div class="scrolling content ui form"> <div class="scrolling content ui form">
<p>Exclude specific directories / paths which contains the following subpath prefix from authentication. Useful if you are hosting services require remote API access.</p> <p>Exclude specific directories / paths which contains the following subpath prefix from authentication. Useful if you are hosting services require remote API access.</p>
<table class="ui very basic compacted unstackable celled table"> <table class="ui basic very compacted unstackable celled table">
<thead> <thead>
<tr> <tr>
<th>Path Prefix</th> <th>Path Prefix</th>
@ -86,10 +79,6 @@
<code>/public/res/far/boo/</code></p> <code>/public/res/far/boo/</code></p>
</div> </div>
</div> </div>
<div class="ui divider"></div>
<div class="field" >
<button class="ui basic button" style="float: right;" onclick="closeThisWrapper();">Close</button>
</div>
</div> </div>
<br><br><br><br> <br><br><br><br>
@ -232,7 +221,7 @@
data.forEach(function(rule){ data.forEach(function(rule){
$("#exclusionPaths").append(` <tr> $("#exclusionPaths").append(` <tr>
<td>${rule.PathPrefix}</td> <td>${rule.PathPrefix}</td>
<td><button class="ui red basic mini icon button" onclick="removeExceptionPath(this);" prefix="${rule.PathPrefix}"><i class="ui red times icon"></i></button></td> <td><button class="ui red basic mini circular icon button" onclick="removeExceptionPath(this);" prefix="${rule.PathPrefix}"><i class="ui red times icon"></i></button></td>
</tr>`); </tr>`);
}) })
} }
@ -261,7 +250,7 @@
var row = '<tr>' + var row = '<tr>' +
'<td>' + username + '</td>' + '<td>' + username + '</td>' +
'<td>' + password + '</td>' + '<td>' + password + '</td>' +
'<td><button class="ui basic button" onclick="removeCredentialFromEditingList(' + i + ');"><i class="red remove icon"></i> Remove</button></td>' + '<td><button class="ui basic tiny circular button" onclick="removeCredentialFromEditingList(' + i + ');"><i class="red remove icon"></i> Remove</button></td>' +
'</tr>'; '</tr>';
tableBody.append(row); tableBody.append(row);

View File

@ -44,7 +44,7 @@
<a class="item narrowpadding" data-tab="security">Security Headers</a> <a class="item narrowpadding" data-tab="security">Security Headers</a>
</div> </div>
<div class="ui tab basic segment active" data-tab="customheaders"> <div class="ui tab basic segment active" data-tab="customheaders">
<table class="ui very basic compacted unstackable celled table"> <table class="ui basic very compacted unstackable celled table">
<thead> <thead>
<tr> <tr>
<th>Key</th> <th>Key</th>

View File

@ -155,6 +155,10 @@
function addSelectedTags() { function addSelectedTags() {
let tags = $('#tagsInput').val().split(',').map(tag => tag.trim()); let tags = $('#tagsInput').val().split(',').map(tag => tag.trim());
if (tags.length == 0 || (tags.length == 1 && tags[0] == "")){
parent.msgbox("Please enter at least one tag", false);
return;
}
tags.forEach(tag => { tags.forEach(tag => {
if (tag && !tagAlreadyExistsInTable(tag)) { if (tag && !tagAlreadyExistsInTable(tag)) {
addTagRow(tag); addTagRow(tag);
@ -201,8 +205,8 @@
const row = `<tr class="tagEntry" value="${tag}"> const row = `<tr class="tagEntry" value="${tag}">
<td><div class="ui circular label tag-color" style="background-color: ${getTagColorByName(tag)};"></div> ${tag}</td> <td><div class="ui circular label tag-color" style="background-color: ${getTagColorByName(tag)};"></div> ${tag}</td>
<td> <td>
<button title="Delete Tag" class="ui circular mini red basic icon button" onclick="removeTag('${tag}')"> <button title="Delete Tag" class="ui circular mini basic button" onclick="removeTag('${tag}')">
<i class="trash icon"></i> <i class="red trash icon"></i> Delete
</button> </button>
</td> </td>
</tr>`; </tr>`;