summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--HyperBooru.cs3
-rw-r--r--Pages/Shared/_Layout.cshtml2
-rw-r--r--Pages/TagDefinitions.cshtml104
-rw-r--r--Pages/TagDefinitions.cshtml.cs16
-rw-r--r--Pages/TagDefinitions.cshtml.css16
-rw-r--r--Pages/ViewMedia.cshtml30
-rw-r--r--Pages/ViewMedia.cshtml.css5
-rw-r--r--Server.csproj10
-rw-r--r--TagController.cs2
-rw-r--r--wwwroot/styles/global.css75
10 files changed, 233 insertions, 30 deletions
diff --git a/HyperBooru.cs b/HyperBooru.cs
index 9ccd323..e0e1b2d 100644
--- a/HyperBooru.cs
+++ b/HyperBooru.cs
@@ -1,3 +1,4 @@
+using Microsoft.EntityFrameworkCore;
using System.Text.Json.Serialization;
namespace HyperBooru;
@@ -20,7 +21,7 @@ public class HyperBooru {
using var scope = app.Services.CreateScope();
using var db = scope.ServiceProvider.GetRequiredService<HyperBooruDbContext>();
- db.Database.EnsureCreated();
+ db.Database.Migrate();
#if DEBUG
app.UseSwagger();
diff --git a/Pages/Shared/_Layout.cshtml b/Pages/Shared/_Layout.cshtml
index be445cc..9cdb952 100644
--- a/Pages/Shared/_Layout.cshtml
+++ b/Pages/Shared/_Layout.cshtml
@@ -16,7 +16,7 @@
<body>
<div id="navbar">
<a href="/">Home</a>
- <a href="/tags">Tags</a>
+ <a href="/TagDefinitions">Tags</a>
</div>
<div id="content" style="overflow:@(ViewBag.ContentScroll ? "auto" : "hidden");margin:@(ViewBag.ContentMargin ?? "0");">
@RenderBody()
diff --git a/Pages/TagDefinitions.cshtml b/Pages/TagDefinitions.cshtml
new file mode 100644
index 0000000..a05f5d5
--- /dev/null
+++ b/Pages/TagDefinitions.cshtml
@@ -0,0 +1,104 @@
+@page
+@model HyperBooru.Pages.TagDefinitionsModel
+@{
+ ViewBag.Title = "Tag Definitions";
+}
+
+<script type="text/javascript">
+ async function createDefinition(e) {
+ var form = new FormData();
+
+ form.append('name', e.querySelector('#name').value);
+ form.append('namespace', e.querySelector('#namespace').value);
+
+ var resp = await fetch('/api/tag/def', {
+ method: 'post',
+ body: form
+ });
+
+ if(!resp.ok) {
+ alert('Error creating tag definition!');
+ showCreateDialog(false);
+ } else {
+ window.location.reload();
+ }
+ }
+
+ async function deleteTagDefinition() {
+ var dialog = document.getElementById('delete-dialog');
+
+ var resp = await fetch(`/api/tag/def/${dialog.dataset.guid}`, {
+ method: 'delete'
+ });
+
+ if(!resp.ok) {
+ alert('Error deleting tag definition!');
+ showDeleteDialog(false);
+ } else {
+ window.location.reload()
+ }
+ }
+
+ function showCreateDialog(visible) {
+ document.getElementById('create-dialog').classList.toggle('visible', visible);
+ }
+
+ function showDeleteDialog(visible) {
+ var dialog = document.getElementById('delete-dialog');
+ if(visible == false) {
+ dialog.classList.toggle('visible', false);
+ } else {
+ dialog.classList.toggle('visible', true);
+ dialog.dataset.guid = visible;
+ }
+ }
+</script>
+
+<link rel="stylesheet" type="text/css" href="@(nameof(HyperBooru)).styles.css"/>
+
+<table id="tag-definitions" class="data-table">
+ <tr>
+ <th>Guid</th>
+ <th>Source</th>
+ <th>Namespace</th>
+ <th>Name</th>
+ <th></th>
+ </tr>
+ @foreach(var tagDef in Model.TagDefinitions) {
+ <tr>
+ <td>@tagDef.Guid</td>
+ <td>@tagDef.Source</td>
+ <td>@tagDef.Namespace</td>
+ <td>@tagDef.Name</td>
+ <td><button onclick="showDeleteDialog('@tagDef.Guid')">Delete</button></td>
+ </tr>
+ }
+</table>
+
+<div class="button-container">
+ <button onclick="showCreateDialog(true)">Create</button>
+</div>
+
+<div id="create-dialog" class="dialog">
+ <p>Create a new tag definition</p>
+ <hr/>
+ <form onsubmit="createDefinition(this)">
+ <label>Name</label>
+ <input id="name" type="text" required/>
+ <label>Namespace</label>
+ <input id="namespace" type="text"/>
+ <div class="button-container">
+ <button class="secondary" onclick="showCreateDialog(false)">Cancel</button>
+ <button type="submit">Create</button>
+ </div>
+ </form>
+</div>
+
+<div id="delete-dialog" class="dialog">
+ <p>Are you sure you want to delete this tag definition?</p>
+ <hr/>
+ <div class="button-container">
+ <button onclick="showDeleteDialog(false)" class="secondary">Cancel</button>
+ <button onclick="deleteTagDefinition()">Confirm</button>
+ </div>
+</div>
diff --git a/Pages/TagDefinitions.cshtml.cs b/Pages/TagDefinitions.cshtml.cs
new file mode 100644
index 0000000..e2253d6
--- /dev/null
+++ b/Pages/TagDefinitions.cshtml.cs
@@ -0,0 +1,16 @@
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.RazorPages;
+
+namespace HyperBooru.Pages;
+
+public class TagDefinitionsModel : PageModel {
+ public IEnumerable<DbTagDefinition> TagDefinitions =>
+ db.TagDefinitions;
+
+ private HyperBooruDbContext db;
+
+ public TagDefinitionsModel(HyperBooruDbContext db) =>
+ this.db = db;
+
+ public void OnGet() {}
+}
diff --git a/Pages/TagDefinitions.cshtml.css b/Pages/TagDefinitions.cshtml.css
new file mode 100644
index 0000000..0a9e226
--- /dev/null
+++ b/Pages/TagDefinitions.cshtml.css
@@ -0,0 +1,16 @@
+form > input {
+ width: 100%;
+}
+
+div.button-container {
+ display: flex;
+ justify-content: flex-end;
+}
+
+table#tag-definitions td:first-child {
+ font-family: 'Lucida Console';
+}
+
+table#tag-definitions td > button {
+ margin-top: 0;
+} \ No newline at end of file
diff --git a/Pages/ViewMedia.cshtml b/Pages/ViewMedia.cshtml
index 967759b..3d0ce4a 100644
--- a/Pages/ViewMedia.cshtml
+++ b/Pages/ViewMedia.cshtml
@@ -17,6 +17,10 @@
}
}
+ function showDeleteDialog(visible) {
+ document.getElementById('delete-dialog').classList.toggle('visible', visible);
+ }
+
function selectPane(tab) {
var tabs = Array.from(document.querySelectorAll('div#metadata-header > a'));
@@ -44,8 +48,8 @@
<img src="/media/@(Model.Media.Guid)"/>
<div id="metadata">
<div id="metadata-header">
- <a href="javascript:;" onclick="selectPane(this);" data-pane="metadata-fileinfo">File Info</a>
- <a href="javascript:;" onclick="selectPane(this);" data-pane="metadata-tags" class="selected">Tags</a>
+ <a href="javascript:;" onclick="selectPane(this);" data-pane="metadata-fileinfo" class="selected">File Info</a>
+ <a href="javascript:;" onclick="selectPane(this);" data-pane="metadata-tags">Tags</a>
</div>
@* <form method="post">
<label for="shortDescription">Short Description</label>
@@ -54,7 +58,7 @@
<input type="text" name="longDescription" placeholder="@Model.Media.LongDescription"/>
<input type="submit" value="Update"/>
</form>*@
- <div id="metadata-fileinfo">
+ <div id="metadata-fileinfo" class="selected">
<p>Upload history</p>
<hr />
<table class="data-table">
@@ -75,14 +79,28 @@
</tr>
}
</table>
+ <div class="button-container">
+ <button onclick="showDeleteDialog(true)">Delete</button>
+ </div>
</div>
- <div id="metadata-tags" class="selected">
+ <div id="metadata-tags">
<table class="data-table">
<tr>
<th>Tag Name</th>
</tr>
</table>
- <button>Add Tag</button>
+ <div class="button-container">
+ <button>Add Tag</button>
+ </div>
</div>
</div>
-</div> \ No newline at end of file
+</div>
+
+<div id="delete-dialog" class="dialog">
+ <p>Delete this media?</p>
+ <hr/>
+ <div class="button-container">
+ <button class="secondary" onclick="showDeleteDialog(false)">Cancel</button>
+ <button onclick="deleteMedia()">Confirm</button>
+ </div>
+</div>
diff --git a/Pages/ViewMedia.cshtml.css b/Pages/ViewMedia.cshtml.css
index ff8a1cd..4041fa8 100644
--- a/Pages/ViewMedia.cshtml.css
+++ b/Pages/ViewMedia.cshtml.css
@@ -49,4 +49,9 @@ div#metadata-fileinfo > table th {
div#metadata-fileinfo > table td {
font-family: 'Lucida Console';
font-size: 8pt;
+}
+
+div.button-container {
+ display: flex;
+ justify-content: flex-end;
} \ No newline at end of file
diff --git a/Server.csproj b/Server.csproj
index 7917674..f7bf989 100644
--- a/Server.csproj
+++ b/Server.csproj
@@ -10,9 +10,13 @@
<ItemGroup>
<PackageReference Include="Magick.NET-Q16-AnyCPU" Version="13.5.0" />
- <PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.7" />
- <PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="7.0.7" />
- <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.7" />
+ <PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.10" />
+ <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.10">
+ <PrivateAssets>all</PrivateAssets>
+ <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
+ </PackageReference>
+ <PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="7.0.10" />
+ <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.10" />
<PackageReference Include="Mime-Detective" Version="23.6.1" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
</ItemGroup>
diff --git a/TagController.cs b/TagController.cs
index b97b066..5c9fa39 100644
--- a/TagController.cs
+++ b/TagController.cs
@@ -3,7 +3,7 @@
namespace HyperBooru;
[ApiController]
-[Route("tag")]
+[Route("/api/tag")]
public class TagController : Controller {
private HyperBooruDbContext db;
diff --git a/wwwroot/styles/global.css b/wwwroot/styles/global.css
index 4a76f87..2b007f8 100644
--- a/wwwroot/styles/global.css
+++ b/wwwroot/styles/global.css
@@ -1,6 +1,10 @@
:root {
- --col-bg: #222;
- --col-navbar-bg: magenta;
+ --col-accent-pri: #0aa;
+ --col-bg: #222;
+ --col-dialog-bg: #333;
+ --col-navbar-bg: var(--col-accent-pri);
+ --col-button-pri: var(--col-accent-pri);
+ --col-button-sec: #555;
}
body {
@@ -15,29 +19,42 @@ a {
text-decoration: none;
}
-input {
+button, input[type=submit] {
color: white;
- box-sizing: border-box;
- border: 1px solid #aaa;
- background: rgba(0, 0, 0, 0.3);
- border-radius: 5px;
-}
-
-button {
- background: #f0f;
+ background: var(--col-button-pri);
border-radius: 10px;
border: none;
box-sizing: border-box;
height: 30px;
- padding: 0 7px 0 7px;
+ margin: 10px 5px 0 5px;
+ padding: 0 9px 0 9px;
+}
+
+button.secondary {
+ background: var(--col-button-sec);
+}
+
+button:hover, input[type=submit]:hover {
+ filter: grayscale(0.9) brightness(2.5);
+}
+
+button:active, input[type=submit]:active {
+ filter: grayscale(0.9) brightness(3.5);
}
-button:hover {
- background: #f8f;
+input {
+ background: rgba(0, 0, 0, 0);
+ border-radius: 5px;
+ border: 1px solid #aaa;
+ box-sizing: border-box;
+ color: white;
+ height: 25px !important;
+ margin-bottom: 10px;
}
-button:active {
- background: #fff;
+/* necessary for use inside flex containers */
+hr {
+ width: 100%;
}
table.data-table {
@@ -54,10 +71,32 @@ table.data-table > tbody > tr > td {
padding: 4px;
}
-table.data-table > tbody > tr:nth-child(2n):not(:last-child) {
+table.data-table > tbody > tr:nth-child(2n) {
background: rgba(255, 255, 255, 0.2);
}
table.data-table > tbody > tr > td:not(:last-child) {
border-right: 1px solid white;
-} \ No newline at end of file
+}
+
+div.dialog {
+ background: var(--col-dialog-bg);
+ border-radius: 20px;
+ box-shadow: 0px 5px 10px 10px rgb(0 0 0 / 25%);
+ display: flex;
+ flex-direction: column;
+ left: 50%;
+ opacity: 0;
+ padding: 20px;
+ position: absolute;
+ top: 50%;
+ transform: translate(-50%, -50%);
+ transition: visibility 0.1s, opacity 0.1s linear;
+ visibility: hidden;
+ width: 450px;
+}
+
+div.dialog.visible {
+ opacity: 1;
+ visibility: visible;
+}