base structure and typst template

This commit is contained in:
Julian Krauser 2025-02-02 11:09:32 +01:00
parent 9a30b108d5
commit 497a850fc3
54 changed files with 3280 additions and 1 deletions

View file

@ -1,3 +1,9 @@
# ff-handbook
Anleitungen zu ff-Admin Software
Anleitungen zu ff-Admin Software
Hier lassen sich Anleitungen zu den Versionen der FF-Admin Software und weiterer verbundener Software finden.
Das Repository beinhaltet Anleitungen für:
- FF Admin
- FF Webpage

View file

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

View file

@ -0,0 +1,9 @@
= Einleitung
*FF Admin - Die zentrale Verwaltungssoftware für Feuerwehren und Vereine*
\
\
FF Admin ist eine vielseitige Mitgliederverwaltungssoftware, die als Herzstück eines wachsenden Ökosystems dient. Neben der Mitgliederverwaltung ermöglicht das Programm die Organisation von Terminkalendern, die Erstellung von Newslettern und Protokollen sowie - in Zukunft - die Verwaltung von Gerätschaften und Prüfplänen.
\
\
Obwohl FF Admin in erster Linie für Feuerwehren konzipiert ist, kann es dank seines modularen Aufbaus auch für andere Organisationen angepasst werden. Die frei definierbaren Stammdaten ermöglichen einen flexiblen Einsatz, so dass die Software optimal an die individuellen Bedürfnisse angepasst werden kann.

View file

@ -0,0 +1,9 @@
= Installation
== Docker
=== Docker-Compose
=== Docker-AIO
== Git

View file

@ -0,0 +1,13 @@
= Konzepte
// Engines und wie Daten selbst definiert werden können.
== Stammdaten
== Engines
=== Template-Engine
=== Query-Engine
=== Scheduling-Engine (bald)

View file

@ -0,0 +1,19 @@
= Module
== Mitgliederverwaltung & Stammdaten
== Kalender
== Protokolle
== Newsletter
== Backups
== Query Builder & Query Store
== Templates & Template Builder
== Benutzer & Rollenverwaltung
== Webapi

View file

@ -0,0 +1,7 @@
= Ökosystem FF Admin
// Versand der Termine an Webseite
// Verwendung der Mitglieder durch Queries und co.
// Verwendung als Login-Service

View file

@ -0,0 +1,13 @@
= Roadmap
Folgende Funktionalitäten sind in Planung (Auszug):
- *Calendar Link Dictionary:* Speicherung von Kalender-Link-Configs mit Aliase wie eine Name.
- *Reihenfolge von Protokoll-Inhalten:* Änderung der Reihenfolge von Abstimmungen, Beschlüssen und TOPs.
- *Mitglieder Ausdruck:* Druck der Daten eines Mitglieds anhand eines Templates.
- *Listen Ausdruck:* Druck von Listen mit Daten eines Queries anhand eines Templates.
- *Verbesserung der Template-Erstellung:* Verbesserung oder Änderung des Prozesses und Interfaces zu Erstellung eigener Templates.
- *Query Builder Erweiterung:* Erweiterung der Abfrage-Funktionalitäten des Query Builders im Bereich der Sortierung und Daten-Verbindung.
- *Kalendereinträge und Webpage:* Versand der Eingetragenen Termine des Kalenders an die Webseite mit der Möglichkeit von Änderungen eines Termins.
- *Geräteverwaltung & Prüfpläne:* Erfassung von Gerätschaften mittels Barcode und Erstellung von Prüfplänen.
- *Erinnerungen:* Versand von Erinnerungen zu anstehenden Prüfungen oder Wartungen.

View file

@ -0,0 +1 @@
FF Admin

0
admin/images/.gitkeep Normal file
View file

BIN
admin/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

BIN
ff-admin.pdf Normal file

Binary file not shown.

44
ff-admin.typ Normal file
View file

@ -0,0 +1,44 @@
#import "typst/template.typ": template
#import "typst/utils.typ": *
#show: template.with(
logo: "../admin/logo.png",
title: "Anleitung",
alttitle: "Anleitung zu FF Admin bis v1.3.x",
subtitle: "bis v1.3.x",
details: (
"Open Source Software",
"entwickelt durch JK Effects",
"von Julian Krauser",
),
authors: (
(name: "Krauser Julian"),
),
// abstract: [
// #include "admin/content/abstract.typ"
// ],
glossary: (
// (key: "key", short: "short", long: "long", desc: [description]),
),
attachments: (
// (title: "attach", file: "../admin/attachments/filename.type", ref: "ref", description:"description"),
),
keywords: (
"FF Admin",
"Mitgliederverwaltung",
"Vereinsverwaltung",
"Feuerwehr"
),
)
#include "admin/content/1-intro.typ"
#include "admin/content/2-installation.typ"
#include "admin/content/3-concepts.typ"
#include "admin/content/4-modules.typ"
#include "admin/content/5-system.typ"
#include "admin/content/6-roadmap.typ"

BIN
ff-webpage.pdf Normal file

Binary file not shown.

38
ff-webpage.typ Normal file
View file

@ -0,0 +1,38 @@
#import "typst/template.typ": template
#import "typst/utils.typ": *
#show: template.with(
logo: "../webpage/logo.png",
title: "Anleitung",
alttitle: "Anleitung zu FF Webpage bis v1.1.x",
subtitle: "bis v1.1.x",
details: (
"Open Source Software",
"entwickelt durch JK Effects",
"von Julian Krauser",
),
authors: (
(name: "Krauser Julian"),
),
// abstract: [
// #include "webpage/content/abstract.typ"
// ],
glossary: (
// (key: "key", short: "short", long: "long", desc: [description]),
),
attachments: (
// (title: "attach", file: "../webpage/attachments/filename.type", ref: "ref", description:"description"),
),
keywords: (
"FF Webpage",
"Webseitebaukasten",
"Strapi",
"Feuerwehr",
),
)
#include "webpage/content/1-intro.typ"
#include "webpage/content/2-installation.typ"
#include "webpage/content/3-strapi.typ"

33
typst/constants.typ Normal file
View file

@ -0,0 +1,33 @@
#let accent_color = rgb("#243a5a")
#let draft_color = rgb("EDEDED")
#let code_background_color = luma(240)
#let localized_months = ("Januar", "Februar", "März", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Dezember")
#let draft = "Entwurf"
#let disclaimer = "Eigenständigkeitserklärung"
#let date_place = "Ort, Datum"
#let acknowledgment = "Danksagung"
#let abstract = "Zusammenfassung"
#let contents = "Inhaltsverzeichnis"
#let figure_list = "Abbildungsverzeichnis"
#let table_list = "Tabellenverzeichnis"
#let code_list = "Quellcodeverzeichnis"
#let glossary = "Abkürzungsverzeichnis / Glossar"
#let references = "Literaturverzeichnis"
#let attachments = "Anhang"
#let example = "Beispiel"
#let source = "Quelle"
#let own-figure = "Eigene Darstellung"
#let textfont = ("Fira Sans", "Libertinus Serif")
#let mathfont = ("New Computer Modern Math")
#let codefont = ("JetBrains Mono", "Fira Mono")
#let font_size = 10.5pt
#let math_font_size = 13pt
#let code_font_size = 8pt
#let bibliography_style = "ieee"
#let outline_other_headings = true

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,93 @@
Copyright (c) 2012-2015, The Mozilla Foundation and Telefonica S.A.
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
https://openfontlicense.org
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

Binary file not shown.

151
typst/glossary.typ Normal file
View file

@ -0,0 +1,151 @@
// glossary figure kind
#let __glossary_figure = "glossary_entry"
// prefix of label for references query
#let __glossary_label_prefix = "glossary:"
// global state containing the glossary entry and their location
#let __glossary_entries = state("__glossary_entries", (:))
#let __query_labels_with_key(key, before: false) = {
if before {
query(selector(label(__glossary_label_prefix + key)).before(here(), inclusive: false))
} else {
query(selector(label(__glossary_label_prefix + key)))
}
}
// Reference a term
#let gls(key, suffix: none, long: none, display: none) = {
context {
let __glossary_entries = __glossary_entries.final()
if key in __glossary_entries {
let entry = __glossary_entries.at(key)
let gloss = __query_labels_with_key(key, before: true)
let is_first = gloss == ()
let entlong = entry.at("long", default: "")
let textLink = if display != none {
[#display]
} else if (is_first or long == true) and entlong != [] and entlong != "" and long != false {
[#entlong#suffix (#entry.short#suffix)]
} else {
[#entry.short#suffix]
}
[#link(label(entry.key), textLink)#label(__glossary_label_prefix + entry.key)]
} else {
text(fill: red, "Glossary entry not found: " + key)
}
}
}
// reference to term with pluralisation
#let glspl(key) = gls(key, suffix: "s")
// show rule to make the references for glossary
#let make-glossary(body) = {
show ref: r => {
if r.element != none and r.element.func() == figure and r.element.kind == __glossary_figure {
// call to the general citing function
gls(str(r.target), suffix: r.citation.supplement)
} else {
r
}
}
body
}
#let __normalize-entry-list(entry_list) = {
let new-list = ()
for entry in entry_list {
new-list.push(
(
key: entry.key,
short: entry.short,
long: entry.at("long", default: ""),
desc: entry.at("desc", default: ""),
group: entry.at("group", default: ""),
),
)
}
return new-list
}
#let print-glossary(entry_list, show-all: false, disable-back-references: false, enable-group-pagebreak: false) = {
let entries = __normalize-entry-list(entry_list)
__glossary_entries.update(x => {
for entry in entry_list {
x.insert(
entry.key,
entry,
)
}
x
})
let groups = entries.map(x => x.at("group", default: "")).dedup()
// move no group to the front
groups.insert(0, "")
groups.pop()
for group in groups.sorted() {
if group != "" [#heading(group, level: 2) ]
for entry in entries.sorted(key: x => x.short) {
if entry.group == group {
[
#show figure.where(kind: __glossary_figure): it => it.caption
#par(hanging-indent: 1em, first-line-indent: 0em)[
#figure(
supplement: "",
kind: __glossary_figure,
numbering: none,
caption: {
context {
set align(left)
set par(justify: true)
let term_references = __query_labels_with_key(entry.key)
if term_references.len() != 0 or show-all {
let desc = entry.at("desc", default: "")
let long = entry.at("long", default: "")
let hasLong = long != "" and long != []
let hasDesc = desc != "" and desc != []
{
set text(weight: 600)
if hasLong {
emph(entry.short) + [ -- ] + entry.long
} else {
emph(entry.short)
}
}
if hasDesc [: #desc ] else [ ]
if disable-back-references != true {
set text(weight: "bold")
box(width: 1fr, repeat[.])
[ ]
term_references.map(x => x.location()).sorted(key: x => x.page()).fold(
(values: (), pages: ()),
((values, pages), x) => if pages.contains(x.page()) {
(values: values, pages: pages)
} else {
values.push(x)
pages.push(x.page())
(values: values, pages: pages)
},
).values.map(x => link(x)[#numbering(x.page-numbering(), ..counter(page).at(x))]).join(", ")
} else {
h(1fr)
}
}
}
},
)[] #label(entry.key)
]
#parbreak()
]
}
}
if enable-group-pagebreak {pagebreak(weak: true)}
}
};

2251
typst/jkeffects.svg Normal file

File diff suppressed because it is too large Load diff

After

Width:  |  Height:  |  Size: 169 KiB

406
typst/template.typ Normal file
View file

@ -0,0 +1,406 @@
#import "@preview/codly:1.2.0": *
#import "@preview/codly-languages:0.1.2": *
#import "constants.typ"
#import "glossary.typ": make-glossary, print-glossary, gls, glspl
#import "utils.typ": draft, inwriting, showNumberedLines
#let localized_month(month) = {
month = calc.clamp(month, 1, 12)
return constants.localized_months.at(month - 1)
}
#let titlepage(
logo,
authors,
collaboration,
title,
subtitle,
details,
date
) = {
set page(margin: (left: 2.5cm, top: 3cm, bottom: 4cm, rest: 2.5cm))
page(footer: [])[
#if logo != none {
place(top + center, float: true, dx: 0cm, dy:2.5cm, image(logo, height:2.5cm))
place(bottom + left, float: true, dx: 0cm, dy:-2.3cm, image("./jkeffects.svg", height: 2cm))
}
// Information on the center
#set align(center)
// #text(constants.font_size * 1.5, authors.map(author => [
// #author.name#if author.at("enrollment_number", default: none) != none [, #author.enrollment_number]
// ]).join("\n"))
// #v(1cm, weak: true)
#v(3cm)
#set par(leading: 0.4em)
#text(
constants.font_size * 2.5,
weight: "medium",
fill: constants.accent_color,
title//smallcaps(title)
)
#if subtitle != none {
v(0.9cm, weak: true)
text(
constants.font_size * 1.5,
fill: constants.accent_color,
subtitle
)
}
#v(1fr)
// Information on the right
#set align(right)
// Information on the bottom right
#set par(leading: 0.65em)
#for line in details {
if line != "" [
#line\
] else {
v(0em)
}
}
#v(0em)
#date.display("[day].") #localized_month(date.month()) #date.year()
]
}
#let acknowledgmentpage(
acknowledgment
) = {
if acknowledgment != none {
v(1fr)
set par(first-line-indent: 0em, justify: true)
heading(constants.acknowledgment, numbering: none)
v(1.5em, weak: true)
acknowledgment
v(1fr)
pagebreak()
}
}
#let abstractpage(
abstract
) = {
if abstract != none {
v(1fr)
set par(first-line-indent: 0em, justify: true)
heading(constants.abstract, numbering: none)
v(1.5em, weak: true)
abstract
v(1fr)
pagebreak()
}
}
#let outlinepages() = {
show ref: none
{
show outline.entry.where(
level: 1
): it => {
v(constants.font_size * 1.25, weak: true)
if it.body.fields().at("children", default: none) != none {
strong(it)
} else {
it
}
}
// Contents
heading(constants.contents)
outline(
title: none,
target: heading.where(outlined: true),
indent: auto
)
}
}
#let listspages() = {
context {
// Figure list
if query(figure.where(kind: image)).len() > 2 {
pagebreak()
heading(constants.figure_list)
outline(
title: none,
target: figure.where(kind: image),
indent: auto
)
}
// Table list
if query(figure.where(kind: table)).len() > 2 {
pagebreak()
heading(constants.table_list)
outline(
title: none,
target: figure.where(kind: table),
indent: auto
)
}
// Code list
if query(figure.where(kind: "code")).len() > 2 {
pagebreak()
heading(constants.code_list)
outline(
title: none,
target: figure.where(kind: "code"),
indent: auto
)
}
}
}
#let glossarypage(glossary) = {
if glossary != none and glossary.len() > 0 {
pagebreak()
heading(constants.glossary)
v(1.5em, weak: true)
print-glossary(glossary, disable-back-references: true)
}
}
#let attachmentpages(attachments, references) = {
if attachments != none and attachments.len() > 0 {
if references != none {
pagebreak()
}
heading(constants.attachments, outlined: false)
v(1.5em, weak: true)
// Reset heading counter
counter(heading).update(0)
// Display heading count as: "Attachment X:"
set heading(
numbering: (..nums) => constants.attachments + " " + nums
.pos()
.slice(1) // All headings are level 2 headings
.map(str)
.join("."),
supplement: none,
outlined: constants.outline_other_headings
)
for (index, attachment) in attachments.enumerate() {
if index != 0 {
pagebreak()
}
if attachment.at("ref", default: none) != none [
#heading(depth: 2, "- " + attachment.title)
#label(attachment.ref)
] else {
heading(depth: 2, "- " + attachment.title)
}
if attachment.at("description", default: none) != none [
#set par(justify: true)
#text(attachment.description)
]
v(1em)
set align(horizon + center)
context{
let (width, height) = measure(image(attachment.file))
if width < (100% - 1pt).length and height < (80% - 1pt).length {
image(attachment.file, fit: "contain")
} else if height < width {
image(attachment.file, width: 100%, fit: "contain")
} else {
image(attachment.file, width: 100%, height: 80%, fit: "contain")
}
}
if attachment.at("note", default: none) != none {
v(1em, weak: true)
text(attachment.note)
}
}
}
}
#let referenceattachmentpages(references, attachments) = {
if references != none or (attachments != none and attachments.len() > 0) {
pagebreak()
set heading(numbering: none, outlined: constants.outline_other_headings)
set page(numbering: "I", number-align: right, header: none)
context {
counter(page).update(counter(page).at(<end-intro>))
counter(page).step()
}
if references != none {
heading(constants.references)
v(1.5em, weak: true)
bibliography("../" + references, title: none, style: constants.bibliography_style)
}
listspages()
attachmentpages(attachments, references)
}
}
#let main(title, alttitle, authors, body) = {
set page(numbering: "1", number-align: right)
set heading(numbering: "1.1", outlined: true)
show heading.where(level: 2): it => {
v(.75em)
it
v(.75em)
}
show heading.where(level: 3): it => {
v(.5em)
it
v(.5em)
}
// set par(justify: true, first-line-indent: 1em)
set par.line(numbering: "1") if showNumberedLines
show figure: set block(above: 3em, below: 2.5em)
show figure.caption: set text(size: .95em)
show figure.caption.where(kind: "source"): set text(size: .8em)
show table: set par(justify: false)
set page(
header: {
//smallcaps(
if alttitle != none {
alttitle
} else {
title
}
//)
context {
let headings_before = query(
heading.where(level: 1, outlined: true, numbering: "1.1").before(here())
)
let lastheadbefore = headings_before.at(-1, default: none)
let headings_after = query(
heading.where(level: 1, outlined: true, numbering: "1.1").after(here())
)
let firstheadafter = headings_after.at(0, default: none)
let currentpage = here().page()
if lastheadbefore != none {
text(" ")
if firstheadafter == none {
sym.dash.en
text(" ")
lastheadbefore.body//smallcaps(lastheadbefore.body)
} else if firstheadafter.location().page() != currentpage {
sym.dash.en
text(" ")
lastheadbefore.body//smallcaps(lastheadbefore.body)
}
}
}
//h(1fr)
//authors.map(a => a.name).join(", ")
}
)
counter(page).update(1)
show: codly-init.with()
codly(languages: codly-languages)
set par(justify: true)
body
}
#let template(
// The title of the report
title: [Report title],
// An optional alternative title as page heading
alttitle: none,
// An optional subtitle of the report
subtitle: none,
// Details displayed on the cover page
details: (),
// Authors of the report:
authors: (),
// optional collaboration with
collaboration: none,
// Logo for the cover page. Must be in the image folder. Can be omitted.
logo: "logo.svg",
// Date displayed on the cover page
date: datetime.today(),
// The report's abstract. Can be omitted.
abstract: none,
// The report's disclaimer. Can be omitted.
disclaimer: none,
// The report's acknowledments. Can be omitted.
acknowledgment: none,
// Glossary for the report. Can be omitted.
glossary: none,
// The result of a call to the `bibliography` function. Can be omitted.
references: none,
// Attachments of the report. Can be omitted.
attachments: none,
// Keywords of the report.
keywords: (),
// Language of the report
lang: "de",
// The report's content
body
) = {
// Set the document's basic properties.
set document(author: authors.map(a => a.name), title: title, keywords: keywords, date: date)
let draft_background = none
if draft {
draft_background = rotate(
45deg,
rect(stroke: 2pt + constants.draft_color, radius: 15pt, inset: 15pt, text(100pt, fill: constants.draft_color)[*#constants.draft*])
)
}
set page(paper: "a4", margin: (right: 2.5cm, bottom: 3.5cm, rest: 4cm), numbering: "I", number-align: right, background: draft_background)
set par(leading: .9em, spacing: .9em)
set text(size: constants.font_size, font: constants.textfont, stretch: 120%, lang: lang, region: lang)
set heading(numbering: none, outlined: constants.outline_other_headings)
show heading.where(level: 1): set text(size: constants.font_size * 2)
show heading.where(level: 2): set text(size: constants.font_size * 1.5)
show heading.where(level: 3): set text(size: constants.font_size * 5/4)
show link: set text(fill: constants.accent_color) //if inwriting
show ref: set text(fill: constants.accent_color) //if inwriting
show: make-glossary
show bibliography: set text(constants.font_size)
show math.equation: set text(size: constants.math_font_size, font: constants.mathfont)
// Raw block format
show raw: set text(size: constants.code_font_size, font: constants.codefont)
titlepage(logo, authors, collaboration, title, subtitle, details, date)
acknowledgmentpage(acknowledgment)
abstractpage(abstract)
show heading.where(level: 1): it => {
pagebreak(weak: true)
v(0cm)
it
v(1em)
}
outlinepages()
glossarypage(glossary)
// Main body
[
~ <end-intro>
#pagebreak()
]
main(title, alttitle, authors, body)
referenceattachmentpages(references, attachments)
}

163
typst/utils.typ Normal file
View file

@ -0,0 +1,163 @@
#import "@preview/codly:1.2.0": *
#import "@preview/cetz:0.3.1"
#import "@preview/cetz-plot:0.1.0": chart
#import "constants.typ"
#let inwriting = false
#let draft = false
#let showNumberedLines = false
#assert(not(inwriting and not(draft)), message: "If inwriting is true, draft should be true as well.")
#let todo(it) = [
#if inwriting {
text([#emoji.pencil #it], fill: red, weight: 600)
} else if not draft {
panic("There is still a todo in the final version.")
}
]
#let image-with-source(
str,
width: auto,
height: auto,
caption: str,
source: constants.own-figure
) = {
figure(
figure(
pad(y: 1em, image("../images/" + str, width: width, height: height)),
kind: "source",
numbering: none,
caption: [
#constants.source: #source
]
),
caption: figure.caption(
position: top,
caption
)
)
}
#let code(
header: none,
placement: none,
caption: none,
caption-pos: bottom,
numbering: "1",
outlined: true,
breakable: false,
range: none,
ranges: none,
content
) = {
codly(
zebra-fill: none, //luma(250),
smart-indent: false,
range: range,
ranges: ranges,
breakable: breakable
)
if header != none {
codly(
header: if type(header) == str { [#header] } else { header },
header-cell-args: (align: center),
)
}
figure(
placement: placement,
caption: if caption == none {
none
} else {
figure.caption(
position: caption-pos,
caption
)
},
kind: "code",
supplement: "Code",
numbering: numbering,
outlined: outlined,
content
)
}
#let code_file(
path: "",
header: none,
placement: none,
caption: none,
caption-pos: bottom,
numbering: "1",
outlined: true,
breakable: false,
range: none,
ranges: none
) = {
code(
header: header,
placement: placement,
caption: caption,
caption-pos: caption-pos,
numbering: numbering,
range: range,
ranges: ranges,
outlined: outlined,
breakable:breakable
)[
#raw(
read("../code/" + path),
block: true,
lang: path.split(".").at(-1)
)
]
}
#let example(title: "", content) = {
let lower_name = lower(constants.example)
counter(lower_name).step()
figure(
kind: lower_name,
supplement: constants.example,
block(
inset: 8pt,
radius: 4pt,
stroke: constants.accent_color,
width: 100%,
breakable: true,
align(
left,
[
#text(weight: "bold", fill: constants.accent_color, [Beispiel #context counter(lower_name).display()])
#if repr(title).len() > 2 [
(#text(weight: "bold", fill: constants.accent_color, title))
]
#content
],
)
)
)
}
#let colors = gradient.linear(red, blue, green)
#let draw_chart(data) = {
align(center,
cetz.canvas({
chart.piechart(
data,
value-key: 1,
label-key: 0,
radius: 2.5,
slice-style: colors,
inner-radius: 0.75,
inner-label: (content: (value, label) => [#text(white, str(value / data.map(x => x.at(1)).sum() * 100) + "%")], radius: 110%),
//inner-label: (content: "%", radius: 110%),
outer-label: (content: (value, label) => [#text(label)], radius: 140%),
legend: (label: "LABEL" )
)
})
)
}

0
versioned/.gitkeep Normal file
View file

View file

View file

@ -0,0 +1,9 @@
= Einleitung
*FF Webpage - Der flexible Webseitenbaukasten für Feuerwehren und Vereine*
\
\
FF Webpage ist ein modularer Webseitenbaukasten, bestehend aus einem Nuxt-Frontend und Strapi als CMS. Struktur und Navigation der Seite können über das Strapi CMS individuell konfiguriert werden, was eine flexible Gestaltung ermöglicht.
\
\
Speziell entwickelte Module erleichtern die Darstellung von Listen, wobei vordefinierte Listen für Einsätze, Termine und Artikel zur Verfügung stehen. Ein besonderes Feature ist die direkte Übernahme von Terminen aus FF Admin in die Website, wodurch eine nahtlose Integration in das FF-Ökosystem gewährleistet ist.

View file

@ -0,0 +1,9 @@
= Installation
== Docker
=== Docker-Compose
=== Docker-AIO
== Git

View file

@ -0,0 +1,5 @@
= Strapi
== Einrichtung
== Verwendung

0
webpage/images/.gitkeep Normal file
View file

BIN
webpage/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB