Commit 652fc58b authored by René Rösner's avatar René Rösner
Browse files

Feat: Added eslint config & fixes (planning#428)

parent 2c170036
......@@ -17,5 +17,19 @@
"@typescript-eslint"
],
"rules": {
"indent": [
"error",
2
],
"space-before-function-paren": [
"error",
{
"anonymous": "always",
"named": "never",
"asyncArrow": "always"
}
],
"eqeqeq": 0,
"array-callback-return": 0
}
}
}
\ No newline at end of file
......@@ -8,5 +8,5 @@ export default ({ text, style }: ChipOptions) => {
<div className={`m-px px-3 py-1 text-xs font-bold rounded-full text-white ${style} w-auto self-start shadow-sm`}>
{text}
</div>
);
};
)
}
import React, { Component, ReactElement } from 'react'
import React, { ReactElement } from 'react'
import { IconBooks } from '@tabler/icons'
type HeaderOptions = {
......@@ -24,17 +24,19 @@ export default ({ title, description, img, children }: HeaderOptions) => {
<div className="flex-1 relative pt-24 px-6 pb-8 md:px-8">
<div className="h-32 absolute top-0 p-4 inline-block transform -translate-y-1/2">
<header className={'flex justify-between space-x-6'}>
{img && img.src ? (
<img
className={'w-24 h-24'}
src={img.src}
alt={img.alt}
/>
) : (
<IconBooks
className={'w-24 h-24'}
/>
)}
{img && img.src
? (
<img
className={'w-24 h-24'}
src={img.src}
alt={img.alt}
/>
)
: (
<IconBooks
className={'w-24 h-24'}
/>
)}
<h1 className="p-5 text-3xl lg:text-5xl font-extrabold tracking-tight text-gray-900 overflow-hidden">
{title}
</h1>
......
import Chip from "@/components/Chip";
import filterData from '@/lib/assets/data/currentFilter.json';
import Chip from '@/components/Chip'
import filterData from '@/lib/assets/data/currentFilter.json'
type ServiceTagOptions = {
text: string,
style: string
}
const filters = filterData.filter;
const filters = filterData.filter
const formatLabel = (text: string) => {
const label = 0;
const value = 1;
if (text.includes(':')){
const splittedLabel = text.split(':');
return `${translateLabel(splittedLabel[label])}: ${translateValue(splittedLabel[label], splittedLabel[value])}`
}
return text;
};
function translateLabel(label: string): string {
const filterCategoriesForTranslation = filters.filter(filterCategory => {
return filterCategory.id == label
});
return filterCategoriesForTranslation.length > 0 ? filterCategoriesForTranslation[0].label : '';
const label = 0
const value = 1
if (text.includes(':')) {
const splittedLabel = text.split(':')
return `${translateLabel(splittedLabel[label])}: ${translateValue(splittedLabel[label], splittedLabel[value])}`
}
return text
}
function translateValue(label: string, value: string): string {
const filterCategoriesForTranslation = filters.filter(filterCategory => {
return filterCategory.id == label
});
if (filterCategoriesForTranslation.length > 0) {
const filterCategory = filterCategoriesForTranslation[0];
const filterValues = filterCategory.options.filter(option => {
return option.id == value
});
return filterValues.length > 0 ? filterValues[0].label : '';
} else {
return '';
}
function translateLabel(label: string): string {
const filterCategoriesForTranslation = filters.filter(filterCategory => {
return filterCategory.id == label
})
return filterCategoriesForTranslation.length > 0 ? filterCategoriesForTranslation[0].label : ''
}
function translateValue(label: string, value: string): string {
const filterCategoriesForTranslation = filters.filter(filterCategory => {
return filterCategory.id == label
})
if (filterCategoriesForTranslation.length > 0) {
const filterCategory = filterCategoriesForTranslation[0]
const filterValues = filterCategory.options.filter(option => {
return option.id == value
})
return filterValues.length > 0 ? filterValues[0].label : ''
} else {
return ''
}
}
export default ({ text, style }: ServiceTagOptions) => {
return (
<Chip text={formatLabel(text)} style={`${style} capitalize ${text == 'label:external' ? 'hidden' : '' }`} />
);
};
<Chip text={formatLabel(text)} style={`${style} capitalize ${text == 'label:external' ? 'hidden' : ''}`} />
)
}
import React, { useState, ReactElement } from "react";
import React, { useState, ReactElement } from 'react'
type TooltipOptions = {
children?: ReactElement[] | ReactElement
......@@ -9,22 +9,22 @@ type TooltipOptions = {
}
export default ({ delayIn, delayOut, direction, children, content }: TooltipOptions) => {
let timeout;
const [active, setActive] = useState(false);
let timeout
const [active, setActive] = useState(false)
const showTooltip = () => {
clearInterval(timeout);
clearInterval(timeout)
timeout = setTimeout(() => {
setActive(true);
}, delayIn || 400);
};
setActive(true)
}, delayIn || 400)
}
const hideTooltip = () => {
clearInterval(timeout);
clearInterval(timeout)
timeout = setTimeout(() => {
setActive(false);
}, delayOut || 400);
};
setActive(false)
}, delayOut || 400)
}
return (
<div
......@@ -33,10 +33,10 @@ export default ({ delayIn, delayOut, direction, children, content }: TooltipOpti
onMouseLeave={hideTooltip}
>
{children}
<div className={`tooltip-content ${direction || "top-left"} ${active ? 'active' : ''}`}>
<div className="tooltip-arrow"></div>
{content}
</div>
<div className={`tooltip-content ${direction || 'top-left'} ${active ? 'active' : ''}`}>
<div className="tooltip-arrow"></div>
{content}
</div>
</div>
);
};
)
}
import Navbar from '@/views/layout/Navbar'
import { Footer, FooterColumn } from '@/views/layout/Footer'
import { useEffect } from 'react';
import { useEffect } from 'react'
export function Layout({ children }) {
useEffect(() => {
document.body.classList.add("absolute");
document.body.classList.add("lg:relative");
});
document.body.classList.add('absolute')
document.body.classList.add('lg:relative')
})
return (
<>
......@@ -19,17 +17,17 @@ export function Layout({ children }) {
/> */}
<main className="lg:py-10">{children}</main>
<Footer logo={{
img: '/img/fitko-main-logo.svg',
alt: 'Föderale Entwicklungsportal',
img: '/img/fitko-main-logo.svg',
alt: 'Föderale Entwicklungsportal'
}} copyright={'FITKO'}>
<FooterColumn title={'Rechtliches'} links={[
{label: 'Impressum', href: 'https://www.fitko.de/impressum' },
{label: 'Datenschutz', href: 'https://www.fitko.de/datenschutz' },
{label: 'Barrierefreiheit', href: 'https://www.fitko.de/barrierefreiheitserklaerung' },
]}/>
<FooterColumn title={''} links={[
{label: '', href: '/' },
]}/>
<FooterColumn title={'Rechtliches'} links={[
{ label: 'Impressum', href: 'https://www.fitko.de/impressum' },
{ label: 'Datenschutz', href: 'https://www.fitko.de/datenschutz' },
{ label: 'Barrierefreiheit', href: 'https://www.fitko.de/barrierefreiheitserklaerung' }
]} />
<FooterColumn title={''} links={[
{ label: '', href: '/' }
]} />
</Footer>
</>
)
......
import {
ContentfulClientApi,
createClient,
Entry,
EntryCollection,
Entry
} from 'contentful'
const space = process.env.NEXT_PUBLIC_CONTENTFUL_SPACE_ID
const accessToken = process.env.NEXT_PUBLIC_CONTENTFUL_ACCESS_TOKEN
const client: ContentfulClientApi = createClient({
space: space,
accessToken: accessToken,
space,
accessToken
})
export type ServiceType = {
......@@ -36,15 +35,15 @@ export type Tag = {
export async function fetchServices(): Promise<Entry<any>[]> {
const services = await client.getEntries({
content_type: 'service',
content_type: 'service'
})
if (services.items) return services.items
console.log(`Error getting Entries for services.`)
console.log('Error getting Entries for services.')
}
export async function fetchService(slug): Promise<Entry<any>> {
const services = await client.getEntries({
content_type: 'service',
content_type: 'service'
})
return services.items.find(
(service: Entry<ServiceType>) => service.fields.slug === slug
......
import { MouseEvent } from 'react';
import { MouseEvent } from 'react'
export function removeFromObjRecursive(name: string, obj: any) {
return JSON.parse(
JSON.stringify(obj, function (key, value) {
if (key !== name) return value;
})
);
return JSON.parse(
JSON.stringify(obj, function (key, value) {
if (key !== name) return value
})
)
}
export function replaceEmailsRecursive(name: string, obj: any) {
return JSON.parse(
JSON.stringify(obj, function (key, value) {
if (key === name) return encodeEmail(value);
return value;
})
);
return JSON.parse(
JSON.stringify(obj, function (key, value) {
if (key === name) return encodeEmail(value)
return value
})
)
}
export function encodeEmail(str: string) {
return btoa(str);
return btoa(str)
}
export function decodeEmail(str: string) {
return atob(str);
return atob(str)
}
export function handleMailtoEvent(e: MouseEvent<HTMLAnchorElement>) {
const el = e.currentTarget;
const base = el.dataset.base;
const mail = decodeEmail(base);
el.href = 'mailto:' + mail;
return true;
const el = e.currentTarget
const base = el.dataset.base
const mail = decodeEmail(base)
el.href = 'mailto:' + mail
return true
}
export default {
removeFromObjRecursive,
replaceEmailsRecursive,
encodeEmail,
decodeEmail,
handleMailtoEvent,
};
removeFromObjRecursive,
replaceEmailsRecursive,
encodeEmail,
decodeEmail,
handleMailtoEvent
}
import { Layout } from '@/components/layout'
import {
IconBookmark,
IconBook,
IconRss,
IconListDetails,
IconChevronRight,
IconChevronRight
} from '@tabler/icons'
export default function NotFound() {
......@@ -13,23 +11,23 @@ export default function NotFound() {
{
title: 'Documentation',
description: 'Learn how to integrate our tools with your app',
icon: IconBook,
icon: IconBook
},
{
title: 'API Reference',
description: 'A complete API reference for our libraries',
icon: IconListDetails,
icon: IconListDetails
},
{
title: 'Guides',
description: 'Installation guides that cover popular setups',
icon: IconBookmark,
icon: IconBookmark
},
{
title: 'Blog',
description: 'Read our latest news and articles',
icon: IconRss,
},
icon: IconRss
}
]
return (
......
import { AppProps } from "next/app"
import { Layout } from "@/components/layout"
import "../styles/style.scss"
import { AppProps } from 'next/app'
import { Layout } from '@/components/layout'
import '../styles/style.scss'
export default function App({ Component, pageProps }: AppProps) {
return (
......
import Document, { Html, Head, Main, NextScript } from 'next/document'
class CustomizedDocument extends Document {
static async getInitialProps(ctx) {
const initialProps = await Document.getInitialProps(ctx)
return { ...initialProps }
}
static async getInitialProps(ctx) {
const initialProps = await Document.getInitialProps(ctx)
return { ...initialProps }
}
render() {
return (
<Html>
<Head>
<link rel="shortcut icon" href={`${process.env.NEXT_PUBLIC_BASE_PATH ? process.env.NEXT_PUBLIC_BASE_PATH : ''}/favicon-32x32.png`} />
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
)
}
render() {
return (
<Html>
<Head>
<link rel="shortcut icon" href={`${process.env.NEXT_PUBLIC_BASE_PATH ? process.env.NEXT_PUBLIC_BASE_PATH : ''}/favicon-32x32.png`} />
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
)
}
}
export default CustomizedDocument
......@@ -5,7 +5,6 @@ import FeaturedApiModules from '@/views/FeaturedApiModules'
import Organisators from '@/views/Organisators'
export default function IndexPage({ nodes }) {
return (
<>
<Head>
......
import { Header } from '@/views/Service'
import Link from "next/link"
import Link from 'next/link'
import { fetchServices, fetchService } from '@/lib/contentful'
import { removeFromObjRecursive, replaceEmailsRecursive } from '@/lib/utils'
import { documentToHtmlString } from '@contentful/rich-text-html-renderer'
import { IconChevronLeft } from '@tabler/icons'
export default function ({ service }) {
return (
<div className={'mx-auto max-w-screen-xl'}>
......@@ -35,23 +34,23 @@ export default function ({ service }) {
<Header
contact={{
name: service.contactInformation.fields.name,
mailTo: service.contactInformation.fields.eMail,
mailTo: service.contactInformation.fields.eMail
}}
img={{
src: service?.logo?.fields.file.url,
alt: service?.logo?.fields.name,
alt: service?.logo?.fields.name
}}
title={service.name}
description={documentToHtmlString(service.description)}
developer={{
name: service.developer.fields.name,
link: service.developer.fields.link,
link: service.developer.fields.link
}}
status={service.status}
documentationURL={service.documentation}
sourceURL={service.sourcecodeURL}
resources={service.ressource?.map(resource => {return { name: resource.fields.name, slug: resource.fields.slug}})
}
resources={service.ressource?.map(resource => { return { name: resource.fields.name, slug: resource.fields.slug } })
}
/>
</div>
)
......@@ -60,12 +59,12 @@ export default function ({ service }) {
export async function getStaticProps({ params }) {
const service = await fetchService(params.serviceName)
// remove unwanted data and encrypt eMails
const nosys = removeFromObjRecursive('sys', service.fields);
const fields = replaceEmailsRecursive('eMail', nosys);
const nosys = removeFromObjRecursive('sys', service.fields)
const fields = replaceEmailsRecursive('eMail', nosys)
return {
props: {
service: fields,
},
service: fields
}
}
}
......@@ -76,6 +75,6 @@ export async function getStaticPaths() {
return {
paths: servicePaths,
fallback: false,
fallback: false
}
}
import React, { useEffect, useState } from "react";
import React, { useEffect, useState } from 'react'
import { useRouter } from 'next/router'
import { fetchServices, ServiceType, Tag } from '@/lib/contentful'
import { removeFromObjRecursive, replaceEmailsRecursive } from '@/lib/utils'
import { removeFromObjRecursive, replaceEmailsRecursive, encodeEmail, handleMailtoEvent } from '@/lib/utils'
import { documentToHtmlString } from '@contentful/rich-text-html-renderer'
import { IconArrowBigRight, IconChevronLeft, IconMail, IconBooks, IconBook2 } from '@tabler/icons'
import Tooltip from '@/components/Tooltip'
import ServiceTag from "@/components/ServiceTag";
import Chip from "@/components/Chip";
import Link from "next/link"
import { encodeEmail, decodeEmail, handleMailtoEvent } from '@/lib/utils';
import ServiceTag from '@/components/ServiceTag'
import Chip from '@/components/Chip'
import Link from 'next/link'
import filterData from '@/lib/assets/data/currentFilter.json';
import filterData from '@/lib/assets/data/currentFilter.json'
export default function ({ services }) {
const router = useRouter()
const filters = filterData.filter;
const filters = filterData.filter
const defaultFilters: any = {
type: [],
status: [],
status: []
}
const [activeFilters, setActiveFilters] = useState(defaultFilters)
const [filteredServices, setFilteredServices] = useState(services)
......@@ -70,7 +69,10 @@ export default function ({ services }) {
Object.keys(activeFilters).forEach(f => {
if (activeFilters[f].length > 0) {
if (activeFilters[f].length === 0) return
if (!groups[f]) return match = false
if (!groups[f]) {
match = false
return match
}
groups[f].forEach(item => {
if (item.startsWith(f + ':') && !activeFilters[f].includes(item)) {
match = false
......@@ -186,8 +188,8 @@ export default function ({ services }) {
className="bg-white px-4 pt-3 pb-6 shadow sm:p-6 sm:rounded-lg"
>
<div className="text-right md:float-right">
{hasTag(service, 'label:external') ?
<Tooltip
{hasTag(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."
......@@ -200,17 +202,19 @@ export default function ({ services }) {
<div className="flex justify-between">
<div className="flex space-x-3">
<div className="flex-shrink-0">
{service?.logo ? (
<img
className="max-w-[100px] h-10"
src={service?.logo?.fields.file.url}
alt={service?.logo?.fields.name}
/>
) : (
<IconBooks
className={'h10'}
/>
)}
{service?.logo
? (
<img
className="max-w-[100px] h-10"
src={service?.logo?.fields.file.url}
alt={service?.logo?.fields.name}
/>