Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions app/components/BlogTeaser.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<script setup lang="ts">
const { data: latestPost } = useAsyncData('blog-teaser', () =>
queryCollection('blog').order('date', 'DESC').first()
)
</script>

<template>
<UPageSection v-if="latestPost">
<template #title>Blog</template>
<template #description>
Neuigkeiten und Artikel rund um FeuerSoftware Produkte.
</template>

<UBlogPost
orientation="horizontal"
:to="latestPost.path"
:title="latestPost.title"
:description="latestPost.description"
:image="latestPost.image"
:date="latestPost.date"
:author="latestPost.author"
:badge="latestPost.minRead ? { label: `${latestPost.minRead} Min. Lesezeit`, color: 'neutral' } : undefined"
:ui="{
root: 'md:grid md:grid-cols-[200px_1fr] md:gap-6 md:items-start',
image: 'md:h-full md:max-h-40 object-cover rounded-lg'
}"
/>

<div class="mt-6">
<UButton
to="/blog"
color="neutral"
variant="outline"
trailing-icon="i-lucide-arrow-right"
>
Alle Beiträge
</UButton>
</div>
</UPageSection>
</template>
37 changes: 37 additions & 0 deletions app/pages/blog/[slug].vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<script setup lang="ts">
const route = useRoute()
const slug = computed(() =>
Array.isArray(route.params.slug) ? route.params.slug[0] : route.params.slug
)

const { data: post } = await useAsyncData(`blog-${slug.value}`, () =>
queryCollection('blog').path(`/blog/${slug.value}`).first()
)

if (!post.value) {
throw createError({
statusCode: 404,
statusMessage: 'Artikel nicht gefunden',
fatal: true
})
}

useSeoMeta({
title: post.value?.seo?.title || post.value?.title,
ogTitle: post.value?.seo?.title || post.value?.title,
description: post.value?.seo?.description || post.value?.description,
ogDescription: post.value?.seo?.description || post.value?.description
})
</script>

<template>
<UPage v-if="post">
<UPageHeader
:title="post.title"
:description="post.description"
/>
<UPageBody>
<ContentRenderer :value="post" />
</UPageBody>
</UPage>
</template>
47 changes: 47 additions & 0 deletions app/pages/blog/index.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<script setup lang="ts">
const { data: posts } = await useAsyncData('blog-posts', () =>
queryCollection('blog').order('date', 'DESC').all()
)

useSeoMeta({
title: 'Blog – FeuerSoftware',
ogTitle: 'Blog – FeuerSoftware',
description: 'Neuigkeiten und Artikel rund um FeuerSoftware Produkte.',
ogDescription: 'Neuigkeiten und Artikel rund um FeuerSoftware Produkte.'
})
</script>

<template>
<UPage>
<UPageHero
title="Blog"
description="Neuigkeiten und Artikel rund um FeuerSoftware Produkte."
:ui="{
title: '!mx-0 text-left',
description: '!mx-0 text-left',
links: 'justify-start'
}"
/>
<UPageSection
:ui="{
container: '!pt-0'
}"
>
<UBlogPosts orientation="vertical">
<UBlogPost
v-for="post in posts"
:key="post.path"
variant="naked"
orientation="horizontal"
:to="post.path"
:title="post.title"
:description="post.description"
:image="post.image"
:date="post.date"
:author="post.author"
:badge="post.minRead ? { label: `${post.minRead} Min. Lesezeit`, color: 'neutral' } : undefined"
/>
</UBlogPosts>
</UPageSection>
</UPage>
</template>
23 changes: 23 additions & 0 deletions content.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { defineCollection, defineContentConfig, z } from '@nuxt/content'

export default defineContentConfig({
collections: {
blog: defineCollection({
type: 'page',
source: 'blog/*.md',
schema: z.object({
date: z.string(),
image: z.string().optional(),
minRead: z.number().optional(),
author: z.object({
name: z.string(),
description: z.string().optional(),
avatar: z.object({
src: z.string(),
alt: z.string()
}).optional()
}).optional()
})
})
}
})
74 changes: 74 additions & 0 deletions content/blog/neue-admin-oberflaeche-tetracontrol2connect.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
---
title: Neue Admin-Oberfläche für TetraControl2Connect
description: Mit Version 3.0 von TetraControl2Connect wurde die Konfiguration vollständig in eine moderne, webbasierte Admin-Oberfläche überführt. Wir erklären, was sich geändert hat und wie der Umstieg gelingt.
date: "2025-03-25"
image: /images/tools/tetracontrol2connect/dashboard.png
minRead: 5
author:
name: FeuerSoftware Team
description: Das FeuerSoftware Entwicklungsteam
navigation: false
---

TetraControl2Connect verbindet TetraControl-Instanzen mit mehreren Connect-Standorten und ist ein wichtiges Werkzeug für viele Feuerwehren und Rettungsdienste. Mit der neuen Version 3.0 haben wir die Anwendung grundlegend überarbeitet: Die bisherige Konfiguration über eine `appsettings.json` ist Geschichte – willkommen zur webbasierten Admin-Oberfläche.

## Was hat sich geändert?

Bis Version 2.x wurde TetraControl2Connect ausschließlich über eine `appsettings.json`-Datei konfiguriert. Das hatte einige Nachteile:

- Bearbeitung nur mit einem Texteditor
- Fehler in der JSON-Struktur führten zum Absturz der Anwendung
- Keine Echtzeitüberwachung des Verbindungsstatus
- Keine Möglichkeit, Konfigurationsänderungen ohne Neustart zu übernehmen

## Die neue Admin-Oberfläche

Mit Version 3.0 wird die gesamte Konfiguration über eine moderne, webbasierte Oberfläche verwaltet, die beim Start der Anwendung automatisch unter `http://localhost:5050` erreichbar ist.

![TetraControl2Connect Admin-Dashboard](/images/tools/tetracontrol2connect/dashboard.png)

### Einrichtungsassistent

Beim ersten Start führt ein interaktiver Einrichtungsassistent durch die Grundkonfiguration:

1. **TetraControl-Verbindung** – Hostname, Port und Zugangsdaten
2. **Connect-Standorte** – API-Schlüssel für jeden anzubindenden Standort
3. **Programmoptionen** – Auswahl der zu übertragenden Datentypen
4. **Status-Zuordnungen** – Mapping von TETRA-Statuscodes zu Connect-Verfügbarkeitsstatus

### SQLite-Datenbank statt JSON

Alle Einstellungen werden jetzt in einer SQLite-Datenbank (`settings.db`) gespeichert. Das bietet mehrere Vorteile:

- **Datenkonsistenz**: Die Datenbank verhindert ungültige Konfigurationseinträge
- **Automatische Backups**: Tägliche Sicherungen im `backups/`-Unterordner
- **Manuelle Backups**: Über die Admin-Oberfläche unter `/backups` jederzeit möglich

### Live-Ansicht und Monitoring

Neu ist auch die Live-Ansicht unter `/live`, die alle eingehenden Nachrichten von TetraControl in Echtzeit anzeigt. Das erleichtert die Fehlersuche erheblich.

![TetraControl2Connect Live-Ansicht](/images/tools/tetracontrol2connect/live.png)

## Migration von Version 2.x

Der Umstieg ist unkompliziert: Beim ersten Start von Version 3.0 erkennt die Anwendung eine vorhandene `appsettings.json` und importiert die Einstellungen automatisch in die neue Datenbank.

::note
Ausführliche Informationen zur Migration finden Sie in der [Migrationsdokumentation](/tools/tetracontrol2connect/migration).
::

## Fazit

Die neue Admin-Oberfläche macht TetraControl2Connect deutlich benutzerfreundlicher. Konfigurationsänderungen sind jetzt ohne Texteditor möglich, und die Live-Ansicht erleichtert die Fehlerdiagnose erheblich. Wir empfehlen allen Nutzern das Update auf Version 3.0.

::u-button
---
color: primary
icon: i-lucide-download
size: lg
target: _blank
to: https://github.com/feuersoftware/tetracontrol-to-connect/releases/latest
---
Jetzt aktualisieren
::
2 changes: 2 additions & 0 deletions content/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,3 +138,5 @@ Unsere Produkte
Weitere Hilfswerkzeuge und Utilities zur Integration verschiedenster Alarmierungswege.
:::
::

:blog-teaser
Loading