Merge branch 'feature/33-use-css-for-header' into 'develop/0.2.0'

Resolve "use CSS for header"

Closes #33

See merge request tpeetz/kontor!35
This commit was merged in pull request #79.
This commit is contained in:
2025-08-16 00:40:32 +02:00
18 changed files with 464 additions and 162 deletions
+172
View File
@@ -0,0 +1,172 @@
* {
box-sizing: border-box;
}
body {
font-family: Arial;
padding: 10px;
background: lightgrey;
}
/* Header/Blog Title */
.header {
padding: 30px;
text-align: center;
background-color: lightblue;
}
.header h1 {
font-size: 50px;
}
/* Style the top navigation bar */
.topnav {
overflow: hidden;
background-color: darkgrey;
}
/* Style the topnav links */
.topnav a {
float: left;
display: block;
color: #f2f2f2;
text-align: center;
padding: 14px 16px;
text-decoration: none;
}
/* Change color on hover */
.topnav a:hover {
background-color: #ddd;
color: black;
}
.subnav {
overflow: hidden;
background-color: grey;
}
.subnav a {
float: left;
display: block;
color: #f2f2f2;
text-align: center;
padding: 14px 16px;
text-decoration: none;
}
.form-inline {
display: flex;
flex-flow: row wrap;
align-items: center;
padding-top: 10px;
padding-left: 20px;
}
.form-inline label {
margin: 5px 10px 5px 0;
}
.form-inline input {
padding-right: 50px;
margin-right: 10px;
}
.form-inline button {
padding: 10px 20px;
background-color: dodgerblue;
border: 1px solid #ddd;
color: white;
border-radius: 10px;
}
.form-inline button:hover {
background-color: royalblue;
}
.pill-nav a {
display: inline-block;
color: white;
background-color: dodgerblue;
text-align: center;
padding: 10px;
text-decoration: none;
border-radius: 10px;
margin-left: 20px;
}
/* Change the color of links on mouse-over */
.pill-nav a:hover {
background-color: royalblue;
color: white;
}
/* Add a color to the active/current link */
.pill-nav a.active {
background-color: royalblue;
color: white;
}
.maincolumn {
float:left;
width: 100%;
}
/* Create two unequal columns that floats next to each other */
/* Left column */
.leftcolumn {
float: left;
width: 75%;
}
/* Right column */
.rightcolumn {
float: left;
width: 25%;
background-color: #f1f1f1;
padding-left: 20px;
}
/* Fake image */
.fakeimg {
background-color: #aaa;
width: 100%;
padding: 20px;
}
/* Add a card effect for articles */
.card {
background-color: white;
padding: 20px;
margin-top: 20px;
}
/* Clear floats after the columns */
.row::after {
content: "";
display: table;
clear: both;
}
/* Footer */
.footer {
padding: 20px;
text-align: center;
background: #ddd;
margin-top: 20px;
}
/* Responsive layout - when the screen is less than 800px wide, make the two columns stack on top of each other instead of next to each other */
@media screen and (max-width: 800px) {
.leftcolumn, .rightcolumn {
width: 100%;
padding: 0;
}
}
/* Responsive layout - when the screen is less than 400px wide, make the navigation links stack on top of each other instead of next to each other */
@media screen and (max-width: 400px) {
.topnav a {
float: none;
width: 100%;
}
}
+25 -12
View File
@@ -4,23 +4,39 @@
<title>Comic List</title>
{% endblock %}
{% block header %}
<h1>Comics..</Comics></h1>
{% endblock %}
{% block subnav %}
<div class="subnav">
<a href="/comic/artists/">Artists</a>
<a href="/comic/publishers/">Publishers</a>
<a href="/comic/worktypes">WorkTypes</a>
</div>
{% endblock %}
{% block content %}
{% with msg=msg %}
{% include "components/alerts.html" %}
{% endwith %}
<div class="container">
<div class="row">
<form class="d-flex" action="/comic/comics/">
<input class="form-control me-2" name="query" id="autocomplete" type="search" placeholder="Search" aria-label="Search">
Completed<input type="checkbox" name="completed" {% if request.query_params.get("completed")=="on" %}checked{% endif %} aria-label="Completed">
Order<input type="checkbox" name="order" {% if request.query_params.get("order")=="on" %}checked{% endif %} aria-label="Order">
<button class="btn btn-outline-success" type="submit">Search</button>
<form class="form-inline" action="/comic/comics">
<input type="text" placeholder="Search" name="query">
<label>
<input type="checkbox" name="completed" {% if request.query_params.get("completed")=="on" %}checked{% endif %}>Completed
</label>
<label>
<input type="checkbox" name="order" {% if request.query_params.get("order")=="on" %}checked{% endif %}>Order
</label>
<button type="submit">Search</button>
<div class="pill-nav">
<a href="/comic/comic/add">Add Comic</a>
</div>
</form>
</div>
<div class="row">
<h1 class="display-5">Comics..</h1>
</div>
<div class="row">
<div class="maincolumn">
<table class="table table-hover">
<thead><tr>
<th scope="col">Title</th>
@@ -40,9 +56,6 @@
{% endfor %}
</tbody>
</table>
<div>
<a href="/comic/comic/add" class="btn btn-outline-primary btn-sm active" role="button" aria-pressed="true">Add Comic</a>
</div>
</div>
</div>
{% endblock %}
@@ -0,0 +1 @@
<h2>Footer</h2>
@@ -1,4 +1,12 @@
<nav class="navbar navbar-expand-lg navbar-light bg-light px-5">
<div class="topnav">
<a href="/">Kontor</a>
<a href="/comic/comics">Comics</a>
<a href="/media/files">Media</a>
<a style="float:right" href="/login/">Login</a></li>
</div>
<!-- <nav class="navbar navbar-expand-lg navbar-light bg-light px-5">
<div class="container-fluid">
<a class="navbar-brand" href="#">Kontor</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
@@ -63,4 +71,4 @@
</form>
</div>
</div>
</nav>
</nav> -->
+3
View File
@@ -5,6 +5,9 @@
<title>Kontor</title>
{% endblock %}
{% block header %}
<h1>Kontor</h1>
{% endblock %}
{% block content %}
{% with msg=msg %}
{% include "components/alerts.html" %}
+24 -6
View File
@@ -4,25 +4,41 @@
<title>MediaFiles List</title>
{% endblock %}
{% block header %}
<h1>MediaFiles..</h1>
{% endblock %}
{% block subnav %}
{% include "media/media_nav.html" %}
{% endblock %}
{% block content %}
{% with msg=msg %}
{% include "components/alerts.html" %}
{% endwith %}
<div class="container">
<div class="row">
<form class="d-flex" action="/media/files/">
<input class="form-control me-2" name="query" id="autocomplete" type="search" placeholder="Search" aria-label="Search">
Review<input type="checkbox" name="review" aria-label="Review">
Download<input type="checkbox" name="download" aria-label="Download">
<button class="btn btn-outline-success" type="submit">Search</button>
<form class="form-inline" action="/media/files">
<input type="text" placeholder="Search" name="query">
<label>
<input type="checkbox" name="review" {% if request.query_params.get("review")=="on" %}checked{% endif %}>Review
</label>
<label>
<input type="checkbox" name="download" {% if request.query_params.get("download")=="on" %}checked{% endif %}>Download
</label>
<button type="submit">Search</button>
<div class="pill-nav">
<a href="/media/files/add">Add MediaFile</a>
</div>
</form>
</div>
<div class="row">
<div class="maincolumn">
<table class="table table-hover">
<thead><tr>
<th scope="col">Titel</th>
<th scope="col">Review</th>
<th scope="col">Download</th>
<th colspan="2">Actions</th>
</tr></thead>
<tbody>
{% for mediafile in mediafiles %}
@@ -30,6 +46,8 @@
<th scope="row"><a href="/media/files/{{mediafile.id}}">{{mediafile.title}}</a></th>
<td>{% with check=mediafile.review %}{% include "components/check.html" %}{% endwith %}</td>
<td>{% with check=mediafile.should_download %}{% include "components/check.html" %}{% endwith %}</td>
<td><a href="/media/files/edit/{{mediafile.id}}" class="btn btn-outline-primary btn-sm active" role="button" aria-pressed="true">Edit</a></td>
<td><a href="/media/files/delete/{{mediafile.id}}" class="btn btn-outline-danger btn-sm active" role="button" aria-pressed="true">Delete</a></td>
</tr>
{% endfor %}
</tbody>
@@ -0,0 +1,5 @@
<div class="subnav">
<a href="/media/files/">MediaFiles</a>
<a href="/media/actors/">MediaActors</a>
<a href="/media/videos/">MediaVideos</a>
</div>
+53 -3
View File
@@ -4,22 +4,72 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-BmbxuPwQa2lc/FVzBcNJ7UAyJxM6wuqIj61tLrc4wSX0szH/Ev+nYRRuWlolflfl" crossorigin="anonymous">
<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
<link rel="stylesheet" href="{{ url_for('static', path='style.css') }}">
{% block title %}
{% endblock %}
</head>
<body>
<div class="header">
{% block header %}
{% endblock %}
</div>
{% include "components/navbar.html" %}
{% block subnav %}
{% endblock %}
<!-- <div class="topnav">
<a href="#">Link</a>
<a href="#">Link</a>
<a href="#">Link</a>
<a href="#" style="float:right">Link</a>
</div> -->
{% block content %}
{% endblock %}
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta2/dist/js/bootstrap.bundle.min.js" integrity="sha384-b5kHyXgcpbZJO/tY9Ul7kGkf1S0CWuKcCD38l8YkeH8z8QjE0GmW1gYU5S9FOnJ0" crossorigin="anonymous"></script>
<script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<script src="{{ url_for('static', path='js/autocomplete.js') }}"></script>
{% block scripts %}
{% endblock %}
<!-- <div class="row">
<div class="leftcolumn">
<div class="card">
<h2>TITLE HEADING</h2>
<h5>Title description, Dec 7, 2017</h5>
<div class="fakeimg" style="height:200px;">Image</div>
<p>Some text..</p>
<p>Sunt in culpa qui officia deserunt mollit anim id est laborum consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco.</p>
</div>
<div class="card">
<h2>TITLE HEADING</h2>
<h5>Title description, Sep 2, 2017</h5>
<div class="fakeimg" style="height:200px;">Image</div>
<p>Some text..</p>
<p>Sunt in culpa qui officia deserunt mollit anim id est laborum consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco.</p>
</div>
</div>
<div class="rightcolumn">
<div class="card">
<h2>About Me</h2>
<div class="fakeimg" style="height:100px;">Image</div>
<p>Some text about me in culpa qui officia deserunt mollit anim..</p>
</div>
<div class="card">
<h3>Popular Post</h3>
<div class="fakeimg"><p>Image</p></div>
<div class="fakeimg"><p>Image</p></div>
<div class="fakeimg"><p>Image</p></div>
</div>
<div class="card">
<h3>Follow Me</h3>
<p>Some text..</p>
</div>
</div>
</div> -->
<div class="footer">
{% include "components/footer.html" %}
</div>
</body>
</html>
@@ -29,7 +29,7 @@ def add_worktype(request: Request, db: SessionDep):
return templates.TemplateResponse("comic/worktype_edit.html", {"request": request})
@router.post("/worktype/add")
async def add_worktype(db: SessionDep, request: Request):
async def add_worktype_post(db: SessionDep, request: Request):
form = AddWorktypeForm(request)
await form.load_data()
if form.is_valid():
@@ -47,11 +47,11 @@ async def add_worktype(db: SessionDep, request: Request):
@router.get("/worktype/edit/{worktype_id}")
def edit_worktype(db: SessionDep, request: Request, worktype_id: str):
worktype = db.get(WorkType, worktype_id)
worktype: WorkType | None = db.get(WorkType, worktype_id)
return templates.TemplateResponse("comic/worktype_edit.html", {"request": request, "worktype": worktype.name})
@router.post("/worktype/edit/{worktype_id}")
async def edit_worktype(request: Request, db: SessionDep, worktype_id: str):
async def edit_worktype_post(request: Request, db: SessionDep, worktype_id: str):
form = AddWorktypeForm(request)
await form.load_data()
if form.is_valid():
@@ -0,0 +1,24 @@
from typing import AnyStr
from fastapi import APIRouter, Request
from fastapi.templating import Jinja2Templates
from sqlalchemy import or_
from src.apis.utils import SessionDep
from src.db.models.media import MediaActor
from src.core.log_conf import logger
templates = Jinja2Templates(directory="src/templates")
router = APIRouter(include_in_schema=False, prefix="/media")
@router.get("/actors")
def get_actors(db: SessionDep, request: Request, msg: str | None = None):
actors = db.query(MediaActor).all()
return templates.TemplateResponse("media/actors.html", {"request": request, "msg": msg, "actors": actors})
@router.get("/actors/{actor_id}")
def artist_detail(actor_id: AnyStr, request: Request, db: SessionDep):
actor = db.get(MediaActor, actor_id)
return templates.TemplateResponse("media/actor_detail.html", {"request": request, "actor": actor})
+7 -10
View File
@@ -9,12 +9,13 @@ from src.apis.utils import SessionDep
from src.apis.version1.admin import get_current_user_from_token
from src.db.models.admin import Profile
from src.db.models.media import MediaFile, MediaActor
from src.core.log_conf import logger
templates = Jinja2Templates(directory="src/templates")
router = APIRouter(include_in_schema=False, prefix="/media")
@router.get("/files")
def get_mediafiles(db: SessionDep, request: Request, msg: str = None):
def get_mediafiles(db: SessionDep, request: Request, msg: str | None = None):
params = request.query_params
query = params.get("query")
filter = {}
@@ -40,6 +41,7 @@ def get_mediafiles(db: SessionDep, request: Request, msg: str = None):
return templates.TemplateResponse("media/files.html", {"request": request, "msg": msg, "mediafiles": mediafiles})
except Exception as e:
print(e)
logger.info("User is not authorized, no data shown")
msg = "Nicht berechtigt!!"
return templates.TemplateResponse("media/files.html", {"request": request, "msg": msg, "mediafiles": []})
@@ -48,13 +50,8 @@ def file_details(file_id: AnyStr, request: Request, db: SessionDep):
mediafile = db.get(MediaFile, file_id)
return templates.TemplateResponse("media/file_detail.html", {"request": request, "mediafile":mediafile})
@router.get("/actors")
def get_actors(db: SessionDep, request: Request, msg: str = None):
actors = db.query(MediaActor).all()
return templates.TemplateResponse("media/actors.html", {"request": request, "msg": msg, "actors": actors})
@router.get("/actors/{actor_id}")
def artist_detail(actor_id: AnyStr, request: Request, db: SessionDep):
actor = db.get(MediaActor, actor_id)
return templates.TemplateResponse("media/actor_detail.html", {"request": request, "actor": actor})
@router.get("files/edit/{file_id}")
def edit_file(db: SessionDep, request: Request, file_id: str):
media_file = db.get(MediaFile, file_id)
return templates.TemplateResponse("media/file_detail.html", {"request": request, "mediafile":media_file})
+5 -5
View File
@@ -1,10 +1,10 @@
<!DOCTYPE html>
<!doctype html>
<html lang="">
<head>
<meta charset="UTF-8">
<link rel="icon" href="/favicon.ico">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vite App</title>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Kontor</title>
</head>
<body>
<div id="app"></div>
+4 -46
View File
@@ -1,62 +1,20 @@
<script setup lang="ts">
import { RouterLink, RouterView } from 'vue-router'
import { RouterView } from 'vue-router'
import MainNavbar from '@/components/MainNavbar.vue'
</script>
<template>
<header>
<img alt="Vue logo" class="logo" src="@/assets/logo.svg" width="125" height="125" />
<div class="wrapper">
<nav>
<RouterLink to="/">Home</RouterLink>
<RouterLink to="/about">About</RouterLink>
</nav>
</div>
<MainNavbar />
</header>
<RouterView />
</template>
<style scoped>
header {
line-height: 1.5;
max-height: 100vh;
}
.logo {
display: block;
margin: 0 auto 2rem;
}
nav {
width: 100%;
font-size: 12px;
text-align: center;
margin-top: 2rem;
}
nav a.router-link-exact-active {
color: var(--color-text);
}
nav a.router-link-exact-active:hover {
background-color: transparent;
}
nav a {
display: inline-block;
padding: 0 1rem;
border-left: 1px solid var(--color-border);
}
nav a:first-of-type {
border: 0;
}
@media (min-width: 1024px) {
header {
display: flex;
display: inline;
place-items: center;
padding-right: calc(var(--section-gap) / 2);
}
+4 -39
View File
@@ -1,5 +1,5 @@
/* color palette from <https://github.com/vuejs/theme> */
:root {
/* :root {
--vt-c-white: #ffffff;
--vt-c-white-soft: #f8f8f8;
--vt-c-white-mute: #f2f2f2;
@@ -19,7 +19,7 @@
--vt-c-text-light-2: rgba(60, 60, 60, 0.66);
--vt-c-text-dark-1: var(--vt-c-white);
--vt-c-text-dark-2: rgba(235, 235, 235, 0.64);
}
} */
/* semantic color variables for this project */
:root {
@@ -36,7 +36,7 @@
--section-gap: 160px;
}
@media (prefers-color-scheme: dark) {
/* @media (prefers-color-scheme: dark) {
:root {
--color-background: var(--vt-c-black);
--color-background-soft: var(--vt-c-black-soft);
@@ -48,39 +48,4 @@
--color-heading: var(--vt-c-text-dark-1);
--color-text: var(--vt-c-text-dark-2);
}
}
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
font-weight: normal;
}
body {
min-height: 100vh;
color: var(--color-text);
background: var(--color-background);
transition:
color 0.5s,
background-color 0.5s;
line-height: 1.6;
font-family:
Inter,
-apple-system,
BlinkMacSystemFont,
'Segoe UI',
Roboto,
Oxygen,
Ubuntu,
Cantarell,
'Fira Sans',
'Droid Sans',
'Helvetica Neue',
sans-serif;
font-size: 15px;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
} */
+9 -8
View File
@@ -1,27 +1,27 @@
@import './base.css';
#app {
/* #app {
max-width: 1280px;
margin: 0 auto;
padding: 2rem;
font-weight: normal;
}
} */
a,
/* a,
.green {
text-decoration: none;
color: hsla(160, 100%, 37%, 1);
transition: 0.4s;
padding: 3px;
}
} */
@media (hover: hover) {
/* @media (hover: hover) {
a:hover {
background-color: hsla(160, 100%, 37%, 0.2);
}
}
} */
@media (min-width: 1024px) {
/* @media (min-width: 1024px) {
body {
display: flex;
place-items: center;
@@ -32,4 +32,5 @@ a,
grid-template-columns: 1fr 1fr;
padding: 0 2rem;
}
}
} */
+75
View File
@@ -0,0 +1,75 @@
<script setup lang="ts">
import { RouterLink } from 'vue-router'
</script>
<template>
<ul>
<li><RouterLink to="/">Home</RouterLink></li>
<li><RouterLink to="/comics">Comics</RouterLink></li>
<li><RouterLink to="/tysc">TYSC</RouterLink></li>
<li><RouterLink to="/mediq">Media</RouterLink></li>
<li style="float: right"><RouterLink to="/about">About</RouterLink></li>
</ul>
</template>
<style lang="css" scoped>
nav {
width: 100%;
font-size: 12px;
text-align: left;
margin-top: 2rem;
}
nav a.router-link-exact-active {
color: var(--color-text);
}
nav a.router-link-exact-active:hover {
background-color: transparent;
}
nav a {
display: inline-block;
padding: 0 1rem;
border-left: 1px solid var(--color-border);
}
nav a:first-of-type {
border: 0;
}
ul {
position: sticky;
width: 90%;
align-items: center;
top: 0;
list-style-type: none;
margin: 1em;
padding: 1em;
overflow: hidden;
background-color: lightgray;
border-radius: 10px;
}
li {
float: left;
border-right: 1px solid #bbb;
}
li:last-child {
border-right: none;
}
li a {
display: block;
color: darkblue;
text-align: center;
padding: 14px 16px;
text-decoration: none;
}
li a:hover:not(.active) {
background-color: gray;
}
.active {
background-color: #04aa6d;
}
</style>
+6
View File
@@ -1,5 +1,6 @@
import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'
import ComicsView from '@/views/ComicsView.vue'
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
@@ -9,6 +10,11 @@ const router = createRouter({
name: 'home',
component: HomeView,
},
{
path: '/comics',
name: 'comics',
component: ComicsView,
},
{
path: '/about',
name: 'about',
+6
View File
@@ -0,0 +1,6 @@
<script setup lang="ts"></script>
<template>
<div>
<h2>Comics</h2>
</div>
</template>