base structure and typst template
|
@ -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
|
0
admin/attachments/.gitkeep
Normal file
BIN
admin/attachments/ff-admin-account.png
Normal file
After Width: | Height: | Size: 28 KiB |
BIN
admin/attachments/ff-admin-calendar.png
Normal file
After Width: | Height: | Size: 47 KiB |
BIN
admin/attachments/ff-admin-members.png
Normal file
After Width: | Height: | Size: 55 KiB |
BIN
admin/attachments/ff-admin-newsletter.png
Normal file
After Width: | Height: | Size: 47 KiB |
BIN
admin/attachments/ff-admin-permissions.png
Normal file
After Width: | Height: | Size: 90 KiB |
BIN
admin/attachments/ff-admin-protocol.png
Normal file
After Width: | Height: | Size: 48 KiB |
BIN
admin/attachments/ff-admin-template.png
Normal file
After Width: | Height: | Size: 43 KiB |
9
admin/content/1-intro.typ
Normal 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.
|
9
admin/content/2-installation.typ
Normal file
|
@ -0,0 +1,9 @@
|
|||
= Installation
|
||||
|
||||
== Docker
|
||||
|
||||
=== Docker-Compose
|
||||
|
||||
=== Docker-AIO
|
||||
|
||||
== Git
|
13
admin/content/3-concepts.typ
Normal file
|
@ -0,0 +1,13 @@
|
|||
= Konzepte
|
||||
|
||||
// Engines und wie Daten selbst definiert werden können.
|
||||
|
||||
== Stammdaten
|
||||
|
||||
== Engines
|
||||
|
||||
=== Template-Engine
|
||||
|
||||
=== Query-Engine
|
||||
|
||||
=== Scheduling-Engine (bald)
|
19
admin/content/4-modules.typ
Normal file
|
@ -0,0 +1,19 @@
|
|||
= Module
|
||||
|
||||
== Mitgliederverwaltung & Stammdaten
|
||||
|
||||
== Kalender
|
||||
|
||||
== Protokolle
|
||||
|
||||
== Newsletter
|
||||
|
||||
== Backups
|
||||
|
||||
== Query Builder & Query Store
|
||||
|
||||
== Templates & Template Builder
|
||||
|
||||
== Benutzer & Rollenverwaltung
|
||||
|
||||
== Webapi
|
7
admin/content/5-system.typ
Normal file
|
@ -0,0 +1,7 @@
|
|||
= Ökosystem FF Admin
|
||||
|
||||
// Versand der Termine an Webseite
|
||||
|
||||
// Verwendung der Mitglieder durch Queries und co.
|
||||
|
||||
// Verwendung als Login-Service
|
13
admin/content/6-roadmap.typ
Normal 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.
|
||||
|
1
admin/content/abstract.typ
Normal file
|
@ -0,0 +1 @@
|
|||
FF Admin
|
0
admin/images/.gitkeep
Normal file
BIN
admin/logo.png
Normal file
After Width: | Height: | Size: 34 KiB |
BIN
ff-admin.pdf
Normal file
44
ff-admin.typ
Normal 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
38
ff-webpage.typ
Normal 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
|
@ -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
|
||||
|
BIN
typst/fonts/Fira_Sans/FiraSans-Black.ttf
Normal file
BIN
typst/fonts/Fira_Sans/FiraSans-BlackItalic.ttf
Normal file
BIN
typst/fonts/Fira_Sans/FiraSans-Bold.ttf
Normal file
BIN
typst/fonts/Fira_Sans/FiraSans-BoldItalic.ttf
Normal file
BIN
typst/fonts/Fira_Sans/FiraSans-ExtraBold.ttf
Normal file
BIN
typst/fonts/Fira_Sans/FiraSans-ExtraBoldItalic.ttf
Normal file
BIN
typst/fonts/Fira_Sans/FiraSans-ExtraLight.ttf
Normal file
BIN
typst/fonts/Fira_Sans/FiraSans-ExtraLightItalic.ttf
Normal file
BIN
typst/fonts/Fira_Sans/FiraSans-Italic.ttf
Normal file
BIN
typst/fonts/Fira_Sans/FiraSans-Light.ttf
Normal file
BIN
typst/fonts/Fira_Sans/FiraSans-LightItalic.ttf
Normal file
BIN
typst/fonts/Fira_Sans/FiraSans-Medium.ttf
Normal file
BIN
typst/fonts/Fira_Sans/FiraSans-MediumItalic.ttf
Normal file
BIN
typst/fonts/Fira_Sans/FiraSans-Regular.ttf
Normal file
BIN
typst/fonts/Fira_Sans/FiraSans-SemiBold.ttf
Normal file
BIN
typst/fonts/Fira_Sans/FiraSans-SemiBoldItalic.ttf
Normal file
BIN
typst/fonts/Fira_Sans/FiraSans-Thin.ttf
Normal file
BIN
typst/fonts/Fira_Sans/FiraSans-ThinItalic.ttf
Normal file
93
typst/fonts/Fira_Sans/OFL.txt
Normal 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.
|
BIN
typst/fonts/JetBrainsMono/JetBrainsMono-Regular.ttf
Normal file
151
typst/glossary.typ
Normal 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
After Width: | Height: | Size: 169 KiB |
406
typst/template.typ
Normal 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
|
@ -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
0
webpage/attachments/.gitkeep
Normal file
9
webpage/content/1-intro.typ
Normal 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.
|
9
webpage/content/2-installation.typ
Normal file
|
@ -0,0 +1,9 @@
|
|||
= Installation
|
||||
|
||||
== Docker
|
||||
|
||||
=== Docker-Compose
|
||||
|
||||
=== Docker-AIO
|
||||
|
||||
== Git
|
5
webpage/content/3-strapi.typ
Normal file
|
@ -0,0 +1,5 @@
|
|||
= Strapi
|
||||
|
||||
== Einrichtung
|
||||
|
||||
== Verwendung
|
0
webpage/images/.gitkeep
Normal file
BIN
webpage/logo.png
Normal file
After Width: | Height: | Size: 19 KiB |