* Adds beercss (WIP).

* Adds some date formatting.
* Adds favicon.
This commit is contained in:
Stefan Schallerl 2025-02-07 11:25:13 +01:00
parent 1bdce2e9cf
commit 1d68957f96
12 changed files with 71 additions and 26 deletions

View file

@ -1,18 +1,14 @@
plugins { plugins {
// Apply the shared build logic from a convention plugin.
// The shared code is located in `buildSrc/src/main/kotlin/kotlin-jvm.gradle.kts`.
id("buildsrc.convention.kotlin-jvm") id("buildsrc.convention.kotlin-jvm")
alias(libs.plugins.tempolin) alias(libs.plugins.tempolin)
id("app.cash.sqldelight") version "2.0.2" id("app.cash.sqldelight") version "2.0.2"
// Apply the Application plugin to add support for building an executable JVM application.
application application
} }
dependencies { dependencies {
implementation("at.favre.lib:bcrypt:0.10.2")
implementation("org.apache.pdfbox:pdfbox:3.0.4") implementation("org.apache.pdfbox:pdfbox:3.0.4")
implementation("app.cash.sqldelight:sqlite-driver:2.0.2") implementation("app.cash.sqldelight:sqlite-driver:2.0.2")
// implementation("org.xerial:sqlite-jdbc:3.48.0.0")
implementation("com.fasterxml.jackson.core:jackson-databind:2.18.2") implementation("com.fasterxml.jackson.core:jackson-databind:2.18.2")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.18.+") implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.18.+")
implementation(libs.slf4jsimple) implementation(libs.slf4jsimple)
@ -23,8 +19,6 @@ dependencies {
} }
application { application {
// Define the Fully Qualified Name for the application main class
// (Note that Kotlin compiles `App.kt` to a class with FQN `com.example.app.AppKt`.)
mainClass = "net.h34t.filemure.ServerKt" mainClass = "net.h34t.filemure.ServerKt"
} }

View file

@ -58,7 +58,8 @@ class FilemureApp(repository: SqliteRepository) {
modifiers = modifiers, modifiers = modifiers,
title = "unauthorized", title = "unauthorized",
target = "", target = "",
content = Unauthorized() content = Unauthorized(),
back = ""
) )
) )
} }

View file

@ -6,6 +6,7 @@ import io.javalin.http.HttpStatus
import io.javalin.http.UnauthorizedResponse import io.javalin.http.UnauthorizedResponse
import java.io.BufferedOutputStream import java.io.BufferedOutputStream
import java.io.OutputStreamWriter import java.io.OutputStreamWriter
import java.time.LocalDate
import java.time.LocalDateTime import java.time.LocalDateTime
import java.time.Month import java.time.Month
import java.time.format.DateTimeFormatter import java.time.format.DateTimeFormatter
@ -66,3 +67,10 @@ val formDtf = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm")
fun LocalDateTime.formatHuman() = this.format(htmlDtf) fun LocalDateTime.formatHuman() = this.format(htmlDtf)
fun LocalDateTime.formatHtmlForm() = this.format(formDtf) fun LocalDateTime.formatHtmlForm() = this.format(formDtf)
private val dfMonth = DateTimeFormatter.ofPattern("MMMM")
private val dfYearMonth = DateTimeFormatter.ofPattern("MMMM yyyy")
fun LocalDate.formatHumanMonth() = dfMonth.format(this)
fun LocalDate.formatHumanYearMonth() = dfYearMonth.format(this)

View file

@ -28,6 +28,7 @@ class DocumentController(val modifiers: TemplateModifiers, val repository: Sqlit
modifiers = modifiers, modifiers = modifiers,
title = document.title, title = document.title,
target = "document-${document.extId}", target = "document-${document.extId}",
back = "/",
content = Document( content = Document(
modifiers = modifiers, modifiers = modifiers,
extId = document.extId.value, extId = document.extId.value,
@ -83,6 +84,7 @@ class DocumentController(val modifiers: TemplateModifiers, val repository: Sqlit
title = "new document", title = "new document",
// TODO enable target for new documents in form // TODO enable target for new documents in form
target = "", target = "",
back = "/",
content = DocumentCreateForm( content = DocumentCreateForm(
modifiers = modifiers, modifiers = modifiers,
title = title, title = title,
@ -172,6 +174,7 @@ class DocumentController(val modifiers: TemplateModifiers, val repository: Sqlit
modifiers = modifiers, modifiers = modifiers,
title = document.title, title = document.title,
target = "", target = "",
back = "/document/$extId",
content = DocumentEditForm( content = DocumentEditForm(
modifiers = modifiers, modifiers = modifiers,
extId = extId, extId = extId,

View file

@ -18,6 +18,7 @@ class LimboController(val modifiers: TemplateModifiers, val repository: SqliteRe
modifiers = modifiers, modifiers = modifiers,
title = "Filemure Limbo", title = "Filemure Limbo",
target = "limbo", target = "limbo",
back = "/",
content = Limbo( content = Limbo(
modifiers = modifiers, limboFileCount = files.size.toString(), file = { modifiers = modifiers, limboFileCount = files.size.toString(), file = {
files.map { f -> files.map { f ->

View file

@ -14,9 +14,8 @@ class LoginController(val modifiers: TemplateModifiers, val repository: SqliteRe
modifiers = modifiers, modifiers = modifiers,
title = "Hello to Filemure", title = "Hello to Filemure",
target = "", target = "",
content = Login( back = "",
content = Login()
)
) )
) )
} }

View file

@ -6,8 +6,9 @@ import net.h34t.filemure.repository.SqliteRepository
import net.h34t.filemure.tpl.Frame import net.h34t.filemure.tpl.Frame
import net.h34t.filemure.tpl.Overview import net.h34t.filemure.tpl.Overview
import net.h34t.filemure.tpl.OverviewDocuments import net.h34t.filemure.tpl.OverviewDocuments
import java.time.LocalDate
class OverviewController(val modifiers: TemplateModifiers, val repository: SqliteRepository) { class OverviewController(private val modifiers: TemplateModifiers, private val repository: SqliteRepository) {
fun overview(ctx: Context) { fun overview(ctx: Context) {
val session = ctx.requireSession() val session = ctx.requireSession()
@ -23,6 +24,7 @@ class OverviewController(val modifiers: TemplateModifiers, val repository: Sqlit
modifiers = modifiers, modifiers = modifiers,
title = "Overview", title = "Overview",
target = "document", target = "document",
back = "",
content = Overview( content = Overview(
modifiers = modifiers, modifiers = modifiers,
limboFileCount = limboFileCount.toString(), limboFileCount = limboFileCount.toString(),
@ -34,7 +36,8 @@ class OverviewController(val modifiers: TemplateModifiers, val repository: Sqlit
year.value.entries.sortedByDescending { it.key }.map { month -> year.value.entries.sortedByDescending { it.key }.map { month ->
MonthBlock( MonthBlock(
year = year.key.toString(), year = year.key.toString(),
month = (month.key.ordinal + 1).toString(), month = month.key.value.toString(),
monthHuman = LocalDate.of(year.key, month.key, 1).formatHumanMonth(),
count = month.value.size.toString() count = month.value.size.toString()
) )
}.asSequence() }.asSequence()
@ -60,14 +63,15 @@ class OverviewController(val modifiers: TemplateModifiers, val repository: Sqlit
modifiers = modifiers, modifiers = modifiers,
title = "Overview", title = "Overview",
target = "document", target = "document",
back = "/",
content = OverviewDocuments( content = OverviewDocuments(
modifiers = modifiers, modifiers = modifiers,
category = "$year-$month", category = LocalDate.of(year, month, 1).formatHumanYearMonth(),
document = { document = {
documents.sortedBy { it.referenceDate }.map { document -> documents.sortedBy { it.referenceDate }.map { document ->
DocumentBlock( DocumentBlock(
extId = document.extId.value, extId = document.extId.value,
referenceDate = document.referenceDate.formatHtmlForm(), referenceDate = document.referenceDate.formatHuman(),
title = document.title.ifBlank { "untitled" }, title = document.title.ifBlank { "untitled" },
) )
}.asSequence() }.asSequence()

View file

@ -23,6 +23,7 @@ class SearchController(val modifiers: TemplateModifiers, val repository: SqliteR
modifiers = modifiers, modifiers = modifiers,
title = "Overview", title = "Overview",
target = "document", target = "document",
back = "/",
content = Search( content = Search(
modifiers = modifiers, modifiers = modifiers,
search = q ?: "", search = q ?: "",

View file

@ -6,14 +6,47 @@
<title>{*$title} :: Filemure</title> <title>{*$title} :: Filemure</title>
<link rel="stylesheet" type="text/css" href="/filemure.css"> <link rel="stylesheet" type="text/css" href="/filemure.css">
<script src="/filemure.js"></script> <script src="/filemure.js"></script>
<link rel="icon" href="/favicon.png">
<link href="https://cdn.jsdelivr.net/npm/beercss@3.8.0/dist/cdn/beer.min.css" rel="stylesheet"/>
<script type="module" src="https://cdn.jsdelivr.net/npm/beercss@3.8.0/dist/cdn/beer.min.js"></script>
<script type="module"
src="https://cdn.jsdelivr.net/npm/material-dynamic-colors@1.1.2/dist/cdn/material-dynamic-colors.min.js"></script>
</head> </head>
<body> <body>
{if $target} <main class="responsive">
<div class="dropzone" data-target="{*$target}"></div> <header>
<dialog id="upload_progress_dialog"> <nav>
<!--
<button class="circle transparent">
<i>menu</i>
</button>
-->
{if $back}
<a href="{$back}">
<button class="circle transparent">
<i>arrow_back</i>
</button>
</a>
{/if}
<h5 class="max center-align">{*$title}</h5>
<!--
<button class="circle transparent">
<img class="responsive" src="/favicon.png">
</button>
-->
</nav>
</header>
{if $target}
<div class="dropzone" data-target="{*$target}"></div>
<dialog id="upload_progress_dialog">
<progress id="upload_progress" max="100"></progress> <progress id="upload_progress" max="100"></progress>
</dialog> </dialog>
{/if} {/if}
{template $content} {template $content}
</main>
</body> </body>
</html> </html>

View file

@ -1,4 +1,4 @@
<h1>Hello to Filemure</h1> <p>To add a document, just drag &amp; drop the file in here.</p>
<p>Files in <a href="/limbo">limbo: {$limboFileCount}</a>.</p> <p>Files in <a href="/limbo">limbo: {$limboFileCount}</a>.</p>
@ -13,7 +13,7 @@
</tr> </tr>
{for $month} {for $month}
<tr> <tr>
<td>{*$month}</td> <td>{*$monthHuman}</td>
<td><a href="/overview/{*$year}/{*$month}">{*$count} Files</a></td> <td><a href="/overview/{*$year}/{*$month}">{*$count} Files</a></td>
</tr> </tr>
{/for} {/for}

BIN
public/favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 530 B

View file

@ -1,8 +1,9 @@
document.addEventListener("DOMContentLoaded", function () { document.addEventListener("DOMContentLoaded", function () {
const dropzone = document.querySelector('html'); const dropzone = document.querySelector('html');
const targetContainer = document.querySelector('#dropzone') const targetContainer = document.querySelector('.dropzone')
if (targetContainer !== null) { if (targetContainer != null) {
console.log("target ready")
const target = targetContainer.dataset.target; const target = targetContainer.dataset.target;
const progressDialog = document.querySelector('#upload_progress_dialog') const progressDialog = document.querySelector('#upload_progress_dialog')