Skip to content
Snippets Groups Projects
Commit 9fd168ef authored by Andy Leuchtturm's avatar Andy Leuchtturm
Browse files

add separate page for it-standards

parent d6cdf911
No related branches found
No related tags found
1 merge request!86planning#1203: Standards
---
contactInformation:
mail: test@mail.de
name: Nortal
developer:
name: Nortal
url: https://xbezahldienste.de/
docsUrl: https://docs.fitko.de/xbezahldienste/
name: XBezahldienste
sourceCodeUrl: https://docs.fitko.de/xbezahldienste/
tags:
- label:external
- type:api
- status:Umsetzung
---
!XBezahldienste ist eine standardisierte Programmierschnittstelle (engl.: „Application Programming Interface“, API), welche zentrale EfA-Onlinedienste mit dezentralen Bezahldiensten verbindet. Die Standardschnittstelle ist unabhängig von der beteiligten Behörde und deren technischen Voraussetzungen. Durch die Schnittstelle XBezahldienste soll ermöglicht werden, bestehende Prozesse im Haushalts-, Kassen- und Rechnungswesen nachzunutzen und somit Interoperabilität zu gewährleisten.
Durch diese Vereinheitlichung wird Bürger*innen sowie Unternehmen das Bezahlen von jeglichen Verwaltungsleistungen erleichtert.
......@@ -67,5 +67,99 @@
}
]
}
],
"filter-it-standards": [
{
"label": "Art des IT-Standards",
"id": "type",
"options": [
{
"label": "Datenstruktur",
"id": "datenstruktur",
"value": "datenstruktur"
},
{
"label": "Datenaustausch (Inhalt)",
"id": "datenaustausch(inhalt)",
"value": "datenaustausch(inhalt)"
},
{
"label": "Datenaustausch (Übertragung)",
"id": "datenaustausch(uebertragung)",
"value": "datenaustausch(uebertragung)"
},
{
"label": "APIs",
"id": "api",
"value": "api"
},
{
"label": "Information / Hilfestellung",
"id": "Information / Hilfestellung",
"value": "Information / Hilfestellung"
},
{
"label": "Designsprachen / -systeme",
"id": "designsprache",
"value": "designsprache"
},
{
"label": "Vorgabe / Richtlinie",
"id": "Vorgabe / Richtlinie",
"value": "Vorgabe / Richtlinie"
},
{
"label": "technisch-fachlichen Klassifizierungen",
"id": "technisch-fachlichen Klassifizierung",
"value": "technisch-fachlichen Klassifizierung"
}
]
},
{
"label": "Status",
"id": "status",
"options": [
{
"label": "Bedarfsmeldung",
"id": "Bedarfsmeldung",
"value": "Bedarfsmeldung"
},
{
"label": "Prüfung",
"id": "Prüfung",
"value": "Prüfung"
},
{
"label": "Umsetzung",
"id": "Umsetzung",
"value": "Umsetzung"
},
{
"label": "Genehmigung",
"id": "Genehmigung",
"value": "Genehmigung"
},
{
"label": "Betriebsüberführung",
"id": "Betriebsüberführung",
"value": "Betriebsüberführung"
},
{
"label": "Regelbetrieb",
"id": "Regelbetrieb",
"value": "Regelbetrieb"
},
{
"label": "Dekommissionierungsempfehlung",
"id": "Dekommissionierungsempfehlung",
"value": "Dekommissionierungsempfehlung"
},
{
"label": "Dekommissionierung",
"id": "Dekommissionierung",
"value": "Dekommissionierung"
}
]
}
]
}
\ No newline at end of file
import { Header } from '@/views/Service'
import Link from 'next/link'
import { IconChevronLeft } from '@tabler/icons'
import { ServiceContent } from '../../types/ServiceContent'
import useContent from 'shared/use-content'
import useImgPath from 'shared/use-img-path'
import { translateStatusLableValue } from '@/lib/utils/labelHelper'
const initializedUsedContent = useContent('standards')
const { getImgPath } = useImgPath()
export default function ({ contentString }) {
const content: ServiceContent = JSON.parse(contentString)
return (
<div className={'mx-auto max-w-screen-xl'}>
<nav className="relative max-w-7xl flex">
<div className="flex items-center flex-1">
<div className="flex items-center justify-between w-full md:w-auto">
<div className="hidden lg:flex">
<Link href="/standards" passHref legacyBehavior>
<a className={'text-lg font-bold text-yellow-400 hover:text-gray-900'}>
Zurück zur Übersicht!
</a>
</Link>
</div>
<div className="flex lg:hidden">
<Link href="/standards" passHref legacyBehavior>
<a>
<IconChevronLeft
className="m-6 text-yellow-400 hover:text-gray-900"
aria-hidden="true"
/>
</a>
</Link>
</div>
</div>
</div>
</nav>
<Header
contact={{
name: content.contactInformation.name,
mail: content.contactInformation.mail,
}}
img={{
src: getImgPath(content.logo.path),
alt: content.logo.title,
}}
title={content?.name}
description={content?.text}
developer={{
name: content?.developer.name,
link: content?.developer.url,
}}
statusLabel={translateStatusLableValue(content.status)}
documentationURL={content.docsUrl}
sourceURL={content.sourceCodeUrl}
resources={content.childResources}
/>
</div>
)
}
export async function getStaticProps({ params }) {
const content = initializedUsedContent.contentByServiceName(params.serviceName)
console.log(content)
return {
props: {
contentString: JSON.stringify(content),
},
}
}
export async function getStaticPaths() {
const { contents } = useContent('standards')
const servicePaths = contents.map((content) => {
return { params: { serviceName: content.slug } }
})
return {
paths: servicePaths,
fallback: false,
}
}
import { useRouter } from 'next/router'
import { IconArrowBigRight, IconChevronLeft, IconMail, IconBooks, IconBook2 } from '@tabler/icons'
import { handleMailtoEvent, encodeEmail } from '@/lib/utils'
import { remark } from 'remark'
import html from 'remark-html'
import Tooltip from '@/components/Tooltip'
import ServiceTag from '@/components/ServiceTag'
import Chip from '@/components/Chip'
import Link from 'next/link'
import filterData from '@/lib/assets/data/currentFilter.json'
import useServiceFilter from 'shared/use-service-filter'
import { ServiceContent } from 'types/ServiceContent'
import { Tag } from 'types/content/'
import useContent from 'shared/use-content'
import useImgPath from 'shared/use-img-path'
export default function ({ contentsString }) {
const contents: ServiceContent[] = JSON.parse(contentsString)
const router = useRouter()
const filters = filterData['filter-it-standards']
const { filteredServices, activeTagFilters, filterToParams } = useServiceFilter(contents, router.query.filter)
const { getImgPath } = useImgPath()
function handleFilters(event) {
const filter = event.target.id.split('-')[0]
if (event.target.checked) {
activeTagFilters[filter].push(event.target.value)
} else if (activeTagFilters[filter].indexOf(event.target.value) >= 0) {
activeTagFilters[filter].splice(activeTagFilters[filter].indexOf(event.target.value), 1)
}
// we always track filters in query params, param change will trigger filtering
router.push(`?filter=${encodeURIComponent(filterToParams())}`, undefined, { shallow: true })
}
const hasContentTag = (content: ServiceContent, tagName: string) => {
const tags: Tag[] = content.tags.filter(tag => tag.name + ':' + tag.value === tagName)
return tags.length > 0
}
return (
<div className='container'>
<nav className="relative max-w-7xl mx-auto flex items-center justify-between px-3 sm:px-6 ">
<div className="flex items-center flex-1">
<div className="flex items-center justify-between w-full md:w-auto">
<div className="lg:block md:space-x-10 hidden">
<Link href="/" passHref legacyBehavior>
<a className={'text-lg font-bold text-yellow-400 hover:text-gray-900'}>
Föderales Entwicklungsportal
</a>
</Link>
</div>
</div>
</div>
</nav>
<div className="py-10">
<div className="max-w-2xl mx-auto px-3 sm:px-6 lg:px-8 lg:max-w-7xl lg:grid lg:grid-cols-12 lg:gap-8">
<header className="lg:col-start-3 xl:col-start-3 lg:col-span-9 xl:col-span-9 flex flex-row">
<div className="flex lg:hidden">
<Link href="/" passHref legacyBehavior>
<a>
<IconChevronLeft
className="mr-7 mt-6 text-yellow-400 hover:text-gray-900"
aria-hidden="true"
/>
</a>
</Link>
</div>
<h1>
<span className="overflow-hiddenmt-1 block text-4xl tracking-tight sm:text-5xl xl:text-7xl">
<span className="block text-gray-900">Standards der Standardisierungsagenda</span>
</span>
</h1>
</header>
<form name="filters" className="lg:block lg:col-span-2 xl:col-span-2">
<nav
aria-label="Sidebar"
className="sticky top-4 divide-y divide-gray-300"
>
{filters.map((filter) => (
<div className="pt-6 mt-4" key={filter.id}>
<p
className="text-xs font-semibold text-gray-500 uppercase tracking-wider"
id={`${filter.id}-headline`}
>
{filter.label}
</p>
<div
className="pl-3 mt-3 space-y-2"
aria-labelledby={`${filter.id}-headline`}
>
{filter.options.map((option) => (
<div key={option.value} className="flex items-center">
<input
onClick={handleFilters}
id={`${filter.id}-${option.id}`}
name={`${filter.id}[]`}
defaultValue={`${filter.id}:${option.id}`}
defaultChecked={router.query.filter ? router.query.filter.includes(`${filter.id}:${option.id}`) : false}
type="checkbox"
className="h-4 w-4 border-gray-300 rounded text-indigo-600 focus:ring-indigo-500"
/>
<label
htmlFor={`${filter.id}-${option.id}`}
className="ml-3 text-sm text-gray-500"
>
{option.label}
</label>
</div>
))}
</div>
</div>
))}
</nav>
</form>
<main className="lg:col-start-3 xl:col-start-3 lg:col-span-9 xl:col-span-9">
<div className="mt-4">
<ul role="list" className="space-y-4">
{filteredServices.map((service: ServiceContent, key) => (
<li
key={service.name}
className="bg-white px-4 pt-3 pb-6 shadow sm:p-6 sm:rounded-lg"
>
<div className="text-right md:float-right">
{hasContentTag(service, 'label:external')
? <Tooltip
delayIn={200}
delayOut={600}
content="Bei diesem Inhalt handelt es sich um einen externen Inhalt, der keiner Qualitätssicherung seitens der FITKO unterliegt."
>
<Chip text={'externer Inhalt'} style={'bg-indigo-500'} />
</Tooltip>
: ''}
</div>
<div aria-labelledby={'service-' + key}>
<div className="flex justify-between">
<div className="flex space-x-3">
<div className="flex-shrink-0 xl:py-6">
{service.logo.path !== '' && service.logo.title !== ''
? (
<img
className="max-w-[100px] h-10"
src={getImgPath(service.logo.path)}
alt={service.logo.title}
/>
)
: (
<IconBooks
className={'h10'}
/>
)}
</div>
<div className="min-w-0 flex-1 xl:py-6">
<p className="text-lg font-medium text-gray-900">
<Link href={`/standards/${service.slug}`} passHref legacyBehavior>
<a className="hover:underline">
{service.name}
</a>
</Link>
</p>
</div>
</div>
</div>
<div className="container flex flex-col md:flex-row my-2">
{service.tags.map((tag: Tag, key: number) => {
return <ServiceTag key={key} tag={tag} style={'bg-yellow-400'} />
})}
</div>
<div
className="mt-2 text-sm text-gray-700 space-y-4"
dangerouslySetInnerHTML={{
__html: service.shortDescription !== '' ? remark().use(html).processSync(service.shortDescription) : service.text,
}}
/>
<div className="mt-6 flex justify-end space-x-8">
<div className="flex text-sm">
<span className="inline-flex items-center text-sm mx-5 xl:mx-12">
<Link href={`${service.docsUrl}`} passHref legacyBehavior>
<a className="inline-flex space-x-2 text-gray-400 hover:text-gray-800">
<span className="font-medium">Direkt zur Ressource</span>
<IconBook2
className="h-5 w-5"
aria-hidden="true"
/>
</a>
</Link>
</span>
<span className="inline-flex items-center text-sm">
<Link href={`/standards/${service.slug}`} passHref legacyBehavior>
<a className="inline-flex space-x-2 text-gray-400 hover:text-gray-800">
<span className="font-medium">Mehr</span>
<IconArrowBigRight
className="h-5 w-5"
aria-hidden="true"
/>
</a>
</Link>
</span>
</div>
</div>
</div>
</li>
))}
<li
key='Additional-Resource'
className="bg-white px-4 py-6 shadow sm:p-6 sm:rounded-lg"
>
<div aria-labelledby={'service-add-on'}>
<div className="min-w-0 flex-1">
<p className="text-sm font-medium text-gray-900">
Dein zukünftiger Inhalt
</p>
</div>
<div className="mt-2 text-sm text-gray-700 space-y-4">
Du kennst weitere Inhalte, die hier nicht gelistet sind? Kontaktiere uns und gebe uns darüber Bescheid. Am einfachsten geht das über unseren Issue-Tracker im Open Source Code Repository der öffentlichen Verwaltung. Schau vor Deinem Feedback bitte in die <a className='text-blue-500' href='https://gitlab.opencode.de/fitko/feedback/-/issues'>Liste der bestehenden Issues</a> und erstelle erst dann <a className='text-blue-500' href='https://gitlab.opencode.de/fitko/feedback/-/issues/new'>ein neues Issue</a>.
</div>
<div className="mt-6 flex justify-end space-x-8" id="kontakt-link">
<div className="flex text-sm">
<span className="inline-flex items-center text-sm">
<a
href="#kontakt-link"
onMouseDown={handleMailtoEvent}
data-base={`${encodeEmail('fit-connect@fitko.de')}`}
className="inline-flex space-x-2 text-gray-400 hover:text-gray-800"
>
<span className="font-medium">Kontakt</span>
<IconMail
className="h-5 w-5"
aria-hidden="true"
/>
</a>
</span>
</div>
</div>
</div>
</li>
</ul>
</div>
</main>
</div>
</div>
</div>
)
}
export async function getStaticProps() {
const { contents } = useContent('standards')
return {
props: {
contentsString: JSON.stringify(contents),
},
}
}
......@@ -3,8 +3,8 @@ import loadAndPrepareMarkdown from './use-markdown-parser'
import fs from 'fs'
import path from 'path'
const useContent = () => {
const contents: ServiceContent[] = readMarkdownFilesFromResources()
const useContent = (folder = 'resources') => {
const contents: ServiceContent[] = readMarkdownFilesFrom(folder)
const contentByServiceName = (selectedServiceName: string) => {
if (contents.length > 0) {
......@@ -12,7 +12,7 @@ const useContent = () => {
console.log('contents array initialized', contents.length)
// return
}
const content: ServiceContent = getContentByServiceName(selectedServiceName)
const content: ServiceContent = getContentByServiceName(selectedServiceName,folder)
return content
}
......@@ -22,17 +22,17 @@ const useContent = () => {
}
}
function getContentByServiceName(selectedServiceName: string) {
return prepareServiceContent(selectedServiceName)
function getContentByServiceName(selectedServiceName: string, folder: string) {
return prepareServiceContent(selectedServiceName, folder)
}
function prepareServiceContent(selectedServiceName: string) {
const content: ServiceContent = loadAndPrepareMarkdown(selectedServiceName)
function prepareServiceContent(selectedServiceName: string, folder: string) {
const content: ServiceContent = loadAndPrepareMarkdown(selectedServiceName, folder)
return content
}
function readMarkdownFilesFromResources(): ServiceContent[] {
const resourcesDirectory = path.join(process.cwd(), 'content', 'resources')
function readMarkdownFilesFrom(folder = "resources"): ServiceContent[] {
const resourcesDirectory = path.join(process.cwd(), 'content', folder)
// read all folder in 'resources'-directory
const serviceDirectories: string[] = fs.readdirSync(resourcesDirectory, { withFileTypes: true })
......@@ -40,7 +40,7 @@ function readMarkdownFilesFromResources(): ServiceContent[] {
.map((serviceDirectory: fs.Dirent) => serviceDirectory.name)
return serviceDirectories.map((serviceDirectory: string) => {
return loadAndPrepareMarkdown(serviceDirectory)
return loadAndPrepareMarkdown(serviceDirectory, folder)
})
}
......
......@@ -5,14 +5,14 @@ import { remark } from 'remark'
import html from 'remark-html'
import { ChildResource, LabelTag, StatusTag, Tag, TypeTag } from 'types/content/'
export default function loadAndPrepareMarkdown(selectedServiceName: string) {
const markdownFileContent: string = loadMarkdown(selectedServiceName)
export default function loadAndPrepareMarkdown(selectedServiceName: string, folder: string) {
const markdownFileContent: string = loadMarkdown(selectedServiceName, folder)
const content: ServiceContent = prepareContentFromMarkdown(markdownFileContent, selectedServiceName)
return content
}
function loadMarkdown(selectedServiceName: string) {
const fullPath: string = path.join(process.cwd(), 'content', 'resources', `${selectedServiceName}`, `${selectedServiceName}.md`)
function loadMarkdown(selectedServiceName: string , folder = 'resources') {
const fullPath: string = path.join(process.cwd(), 'content', folder, `${selectedServiceName}`, `${selectedServiceName}.md`)
const markdownContent: string = fs.readFileSync(fullPath, 'utf8')
return markdownContent
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment