diff options
Diffstat (limited to 'clams-server/src')
10 files changed, 116 insertions, 77 deletions
diff --git a/clams-server/src/main/java/no/eliashaugsbakk/clams/server/config/AppRoutes.java b/clams-server/src/main/java/no/eliashaugsbakk/clams/server/config/AppRoutes.java index 3df4d6d..9deef6b 100644 --- a/clams-server/src/main/java/no/eliashaugsbakk/clams/server/config/AppRoutes.java +++ b/clams-server/src/main/java/no/eliashaugsbakk/clams/server/config/AppRoutes.java @@ -26,7 +26,7 @@ public class AppRoutes implements EndpointGroup { Map.of("page_title", "Projects - Elias Haugsbakk", "page_css", "projects"))); path("articles", () -> { - get(appContext.getArticlesController()::handleArticlesRequest); + get(appContext.getArticlesController()::handleGetArticles); get("{slug}", appContext.getArticlesController()::handleGetArticle); }); @@ -50,13 +50,13 @@ public class AppRoutes implements EndpointGroup { } }); - post("articles", appContext.getArticleController()::handleArticleArticle); + post("articles", appContext.getArticleController()::handlePostArticle); put("articles/{slug}", appContext.getArticleController()::handlePutArticle); delete("articles/{slug}", appContext.getArticleController()::handleDeleteArticle); get("media", appContext.getMediaController()::handleGetMediaIndex); get("media/{uuid}", appContext.getMediaController()::handleGetMedia); - post("media", appContext.getMediaController()::handleArticleMedia); + post("media", appContext.getMediaController()::handlePostMedia); delete("media/{uuid}", appContext.getMediaController()::handleDeleteMedia); }); } diff --git a/clams-server/src/main/java/no/eliashaugsbakk/clams/server/controller/ArticleController.java b/clams-server/src/main/java/no/eliashaugsbakk/clams/server/controller/ArticleController.java index a57414f..8759b1a 100644 --- a/clams-server/src/main/java/no/eliashaugsbakk/clams/server/controller/ArticleController.java +++ b/clams-server/src/main/java/no/eliashaugsbakk/clams/server/controller/ArticleController.java @@ -15,7 +15,7 @@ public class ArticleController { this.slugService = slugService; } - public void handleArticleArticle(Context ctx) { + public void handlePostArticle(Context ctx) { ArticleDTO newArticle = ctx.bodyAsClass(ArticleDTO.class); articlesRepo.addArticle(new Article(newArticle, slugService.toSlug(newArticle.title()))); } diff --git a/clams-server/src/main/java/no/eliashaugsbakk/clams/server/controller/ArticlesController.java b/clams-server/src/main/java/no/eliashaugsbakk/clams/server/controller/ArticlesController.java index f013419..80a1cb5 100644 --- a/clams-server/src/main/java/no/eliashaugsbakk/clams/server/controller/ArticlesController.java +++ b/clams-server/src/main/java/no/eliashaugsbakk/clams/server/controller/ArticlesController.java @@ -24,7 +24,7 @@ public class ArticlesController { private final ArticlesRepo articlesRepo; private final ArticlesSearchService articlesSearchService; - public record SearchResultItem(String title, String slug, String summary, String formattedDate) {} + public record ArticleItem(String title, String slug, String summary, String formattedDate, int year) {} public ArticlesController(SqliteManager sqliteManager) { this.articlesRepo = new ArticlesRepoSqlite(sqliteManager); @@ -58,7 +58,7 @@ public class ArticlesController { )); } - public void handleArticlesRequest(Context ctx) { + public void handleGetArticles(Context ctx) { String searchTerm = ctx.queryParam("search"); if (searchTerm != null) { handleSearch(ctx, searchTerm); @@ -71,23 +71,28 @@ public class ArticlesController { List<ArticleMetaData> allArticles = articlesRepo.listArticlesMetaData().stream() .filter(ArticleMetaData::isPublished) .toList(); - Map<Integer, List<ArticleMetaData>> articlesByYear = groupArticlesByYear(allArticles); - var featuredSlugs = List.of( - "" - ); + Map<Integer, List<ArticleMetaData>> articlesByYear = groupArticlesByYear(allArticles); - List<Article> featuredArticles = new ArrayList<>(); - for (String slug : featuredSlugs) { - articlesRepo.getArticle(slug).ifPresent(featuredArticles::add); - } + Map<Integer, List<ArticleItem>> articleItemsByYear = articlesByYear.entrySet().stream() + .collect(Collectors.toMap( + Map.Entry::getKey, + entry -> entry.getValue().stream() + .map(article -> new ArticleItem( + article.title(), + article.slug(), + article.summary() != null ? article.summary() : "", + article.timePublished().atZone(OSLO_ZONE).format(DATE_FORMATTER), + article.timePublished().atZone(OSLO_ZONE).getYear() + )) + .toList() + )); ctx.render("templates/articles.html", Map.of( "page_title", "My articles", "page_css", "articles", - "articles_by_year", articlesByYear, - "featured_articles", featuredArticles, + "articles_by_year", articleItemsByYear, "search_value", "" )); } @@ -96,13 +101,14 @@ public class ArticlesController { String query = searchTerm.trim(); List<ArticleMetaData> results = query.isEmpty() ? List.of() : articlesSearchService.searchArticles(query); - List<SearchResultItem> resultItems = results.stream() + List<ArticleItem> resultItems = results.stream() .filter(ArticleMetaData::isPublished) - .map(article -> new SearchResultItem( + .map(article -> new ArticleItem( article.title(), article.slug(), article.summary() != null ? article.summary() : "", - article.timePublished().atZone(OSLO_ZONE).format(DATE_FORMATTER) + article.timePublished().atZone(OSLO_ZONE).format(DATE_FORMATTER), + article.timePublished().atZone(OSLO_ZONE).getYear() )) .toList(); diff --git a/clams-server/src/main/java/no/eliashaugsbakk/clams/server/controller/MediaController.java b/clams-server/src/main/java/no/eliashaugsbakk/clams/server/controller/MediaController.java index d4e40b9..8855b7b 100644 --- a/clams-server/src/main/java/no/eliashaugsbakk/clams/server/controller/MediaController.java +++ b/clams-server/src/main/java/no/eliashaugsbakk/clams/server/controller/MediaController.java @@ -28,7 +28,7 @@ public class MediaController { this.appConfig = appConfig; } - public void handleArticleMedia(Context ctx) { + public void handlePostMedia(Context ctx) { UploadedFile file = ctx.uploadedFile("image"); if (file == null) { diff --git a/clams-server/src/main/resources/public/css/articles.css b/clams-server/src/main/resources/public/css/articles.css index a3050b0..3ff7648 100644 --- a/clams-server/src/main/resources/public/css/articles.css +++ b/clams-server/src/main/resources/public/css/articles.css @@ -25,19 +25,7 @@ } .search-form button { - font-family: var(--font-serif), serif; - padding: 0.4rem 0.7rem; - background: #e8e8e4; - color: var(--text-color); - border: 1px solid var(--border-color); - border-radius: 4px; - cursor: pointer; - font-weight: bold; - font-size: 0.8rem; -} - -.search-form button:hover { - background: #ddd; + margin-left: 0.2rem; } .year-accordion { @@ -50,6 +38,7 @@ font-weight: bold; list-style: none; margin-bottom: 0.5rem; + transition: color 0.2s ease; } .year-toggle::-webkit-details-marker { @@ -57,7 +46,7 @@ } .year-toggle:hover { - opacity: 0.7; + color: var(--accent-color) } .article-list { @@ -66,15 +55,28 @@ border-left: 2px solid var(--border-color); } -.article-list li { +.article-list-item, +.search-result-item { margin-bottom: 0.8rem; + font-size: 1rem; + line-height: 1.6; } -.article-list a { +.article-list a, +.search-result-item a { font-size: 1rem; font-weight: normal; } +.article-date { + display: inline-block; + font-size: 0.9rem; + color: var(--text-color); + opacity: 0.65; + margin-right: 0.9rem; + min-width: 6.8rem; +} + .featured-desc { font-weight: normal; color: var(--text-color); @@ -82,29 +84,15 @@ } .search-results-container { - max-width: 40ch; - margin: 1.8rem auto 0 auto; + max-width: 100%; + margin: 1.8rem 0 0 0; } .search-results-list { list-style-type: none; - padding: 0; margin: 0; -} - -.search-result-item { - margin-bottom: 1.25rem; - font-size: 1rem; - line-height: 1.6; -} - -.search-result-date { - display: inline-block; - font-size: 0.9rem; - color: var(--text-color); - opacity: 0.65; - margin-right: 0.9rem; - min-width: 6.8rem; + padding: 0 0 0 2.2rem !important; + border-left: 2px solid var(--border-color); } .search-result-item a { diff --git a/clams-server/src/main/resources/public/css/common.css b/clams-server/src/main/resources/public/css/common.css index 89748f0..dc81927 100644 --- a/clams-server/src/main/resources/public/css/common.css +++ b/clams-server/src/main/resources/public/css/common.css @@ -5,6 +5,8 @@ --text-color: #363737; --border-color: #ccc; --accent-color: #8fae8f; + --surface-color: #e8e8e4; + --surface-hover-color: #ddd; --font-serif: "Spectral", Georgia, serif; --font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; --font-size-body: 1.2rem; @@ -93,6 +95,32 @@ a:hover { color: var(--accent-color); } +.pill-button { + font-family: var(--font-serif), serif; + padding: 0.4rem 0.7rem; + background: var(--surface-color); + color: var(--text-color); + border: 1px solid var(--border-color); + border-radius: 4px; + cursor: pointer; + font-weight: bold; + font-size: 0.8rem; + text-decoration: none; + display: inline-block; + transition: background-color 0.2s ease, color 0.2s ease; +} + +.pill-button:hover, +.pill-button:focus-visible { + background: var(--surface-hover-color); + color: var(--text-color); +} + +.pill-button:focus-visible { + outline: 1px solid var(--accent-color); + outline-offset: 2px; +} + footer { padding: 1.875rem 1.25rem; border-top: 1px solid var(--border-color); @@ -100,6 +128,11 @@ footer { font-size: var(--font-size-small); } +footer a { + font-weight: normal; + font-size: 1rem; +} + footer p { margin: 0; display: flex; @@ -172,14 +205,26 @@ footer p { font-size: var(--font-size-caption); text-decoration: none; font-weight: bold; + color: var(--text-color); opacity: 0.7; - transition: color 0.2s ease; + transition: color 0.2s ease, opacity 0.2s ease; + display: inline-flex; + align-items: center; + gap: 0.2rem; } -.back-link-top:hover { +.back-link-top:hover, +.back-link-top:focus-visible { + color: var(--accent-color); opacity: 1; } +.back-link-top:focus-visible { + outline: 1px solid var(--accent-color); + outline-offset: 2px; + border-radius: 2px; +} + @media (max-width: 768px) { .back-link-top { position: static; diff --git a/clams-server/src/main/resources/public/css/projects.css b/clams-server/src/main/resources/public/css/projects.css index f56955b..d953411 100644 --- a/clams-server/src/main/resources/public/css/projects.css +++ b/clams-server/src/main/resources/public/css/projects.css @@ -44,15 +44,7 @@ .project-actions a { font-size: 0.85rem; - text-decoration: none; - border: 1px solid var(--border-color); padding: 0.2rem 0.6rem; - border-radius: 4px; - background: #e8e8e4; -} - -.project-actions a:hover { - background: #ddd; } @media (max-width: 768px) { diff --git a/clams-server/src/main/resources/templates/articles-search.html b/clams-server/src/main/resources/templates/articles-search.html index dd435e5..54eb609 100644 --- a/clams-server/src/main/resources/templates/articles-search.html +++ b/clams-server/src/main/resources/templates/articles-search.html @@ -9,7 +9,7 @@ <form action="/articles" method="GET" class="search-form"> <input type="text" name="search" value="{{ search_term }}" placeholder="Search articles..." aria-label="Search articles"> - <button type="submit">Search</button> + <button type="submit" class="pill-button">Search</button> </form> </header> @@ -27,9 +27,10 @@ <ul class="search-results-list"> {% for article in results %} <li class="search-result-item"> - <span class="search-result-date">{{ article.formattedDate }}</span> + <span class="article-date">{{ article.formattedDate }}</span> <a href="/articles/{{ article.slug }}">{{ article.title }}</a> {% if article.summary is not empty %} + <br> <span class="search-result-summary">-- {{ article.summary }}</span> {% endif %} </li> diff --git a/clams-server/src/main/resources/templates/articles.html b/clams-server/src/main/resources/templates/articles.html index 905ed52..5feddae 100644 --- a/clams-server/src/main/resources/templates/articles.html +++ b/clams-server/src/main/resources/templates/articles.html @@ -10,7 +10,7 @@ <form action="/articles" method="GET" class="search-form"> <input type="text" name="search" value="{{ search_value }}" placeholder="Search articles..." aria-label="Search articles"> - <button type="submit">Search</button> + <button type="submit" class="pill-button">Search</button> </form> </header> @@ -21,7 +21,14 @@ <summary class="year-toggle">{{ group.key }}</summary> <ul class="article-list"> {% for article in group.value %} - <li><a href="/articles/{{ article.slug }}">{{ article.title }}</a></li> + <li class="article-list-item"> + <span class="article-date">{{ article.formattedDate }}</span> + <a href="/articles/{{ article.slug }}">{{ article.title }}</a> + {% if article.summary is not empty %} + <br> + <span class="search-result-summary">-- {{ article.summary }}</span> + {% endif %} + </li> {% endfor %} </ul> </details> diff --git a/clams-server/src/main/resources/templates/projects.html b/clams-server/src/main/resources/templates/projects.html index 7a1f440..8d476cd 100644 --- a/clams-server/src/main/resources/templates/projects.html +++ b/clams-server/src/main/resources/templates/projects.html @@ -15,9 +15,9 @@ <p>Short project description / introduction.</p> </div> <div class="project-actions"> - <a href="#">Read More</a> - <a href="#">Self-Hosted Git</a> - <a href="#">GitHub</a> + <a href="#" class="pill-button">Read More</a> + <a href="#" class="pill-button">Self-Hosted Git</a> + <a href="#" class="pill-button">GitHub</a> </div> </li> @@ -27,9 +27,9 @@ <p>Short project description / introduction.</p> </div> <div class="project-actions"> - <a href="#">Read More</a> - <a href="#">Self-Hosted Git</a> - <a href="#">GitHub</a> + <a href="#" class="pill-button">Read More</a> + <a href="#" class="pill-button">Self-Hosted Git</a> + <a href="#" class="pill-button">GitHub</a> </div> </li> @@ -39,9 +39,9 @@ <p>A short one-line description.</p> </div> <div class="project-actions"> - <a href="#">Read More</a> - <a href="#">Self-Hosted Git</a> - <a href="#">GitHub</a> + <a href="#" class="pill-button">Read More</a> + <a href="#" class="pill-button">Self-Hosted Git</a> + <a href="#" class="pill-button">GitHub</a> </div> </li> </ul> |
