diff --git a/.yarn/install-state.gz b/.yarn/install-state.gz index f821ac7..7507807 100644 Binary files a/.yarn/install-state.gz and b/.yarn/install-state.gz differ diff --git a/api/collections/_article.yml b/api/collections/_article.yml deleted file mode 100644 index c8a098b..0000000 --- a/api/collections/_article.yml +++ /dev/null @@ -1,364 +0,0 @@ -name: article -type: tabs -meta: - label: - de: Einstellungen zum Artikel - en: Article Setings - activeTab: 0 -subFields: - - name: general - type: object - meta: - label: - de: Allgemein - en: General - css: - subFields: - - name: public - type: boolean - meta: - label: - de: Veröffentlicht - en: Public - helperText: - de: "Der Artikel wird auf der Seite angezeigt." - en: "This article is displayed on the page." - - name: publish_date - type: object - meta: - label: - de: Datum der Veröffentlichung - en: Release Date - css: "grid grid-50" - subFields: - - name: public_from - type: date - meta: - inputProps: - type: "datetime-local" - label: - de: Datum (ab) - en: Date (from) - - name: public_until - type: date - meta: - inputProps: - type: "datetime-local" - label: - de: Datum (bis) - en: Date (until) - - name: position - type: string - meta: - widget: select - label: - de: Position auf der Seite - en: Position on page - defaultValue: [] - choices: - - { id: "content", name: "Inhaltsbereich" } - - { id: "sidebar", name: "Sidebar" } - - name: sort - type: number - meta: - inputProps: - type: number - placeholder: 0 - label: { de: "Sortierung", en: "Sorting" } - helperText: - de: "1...5...10...100" - en: "1...5...10...100" - - name: content - type: object - meta: - label: - de: Inhalt - en: Content - css: - subFields: - - name: slug - type: string - meta: - label: { de: "Permalink", en: "Permalink" } - - name: title - type: string - meta: - label: { de: "Titel", en: "Title" } - - name: subtitle - type: string - meta: - label: { de: "Untertitel", en: "Subtitle" } - - name: types - type: tabs - meta: - label: - de: Inhalt dieses Artikel - en: Article Content - activeTab: 0 - subFields: - - name: teaser - type: string - meta: - widget: richtext - label: { de: "Teaser-Text des Artikel", en: "Article Teaser Text" } - - name: details - type: string - meta: - widget: richtext - label: { de: "Detail-Text des Artikel", en: "Article Detail Text" } - - name: contentMedia - type: object - meta: - label: - de: Medien - en: Media - css: - subFields: - - name: mediaFiles - type: object[] - meta: - label: { de: "Bilder", en: "Images" } - subFields: - - name: title - type: string - meta: - label: { de: "Datei-Titel", en: "File Title" } - - name: alternateText - type: string - meta: - label: { de: "Alternativer Text", en: "Alternate Text" } - - name: id - type: string - meta: - label: { de: "Technischer Name / ID", en: "Technical name / ID" } - - name: file - type: file - meta: - label: { de: "Datei", en: "File" } - - name: caption - type: string - meta: - label: { de: "Bildunterschrift", en: "Caption" } - - name: contentAttachments - type: object - meta: - label: - de: Anhänge / Downloads - en: Attachments / Downloads - css: - subFields: - - name: attachments - type: object[] - meta: - label: { de: "Anhänge", en: "Attachments" } - subFields: - - name: title - type: string - meta: - label: { de: "Datei-Titel", en: "File Title" } - - name: id - type: string - meta: - label: { de: "Technischer Name / ID", en: "Technical name / ID" } - - name: file - type: file - meta: - label: { de: "", en: "" } - - name: layout - type: tabs - meta: - label: - de: Layout - en: Layout - css: - subFields: - - name: variant - type: string - meta: - widget: select - label: - de: Erscheinungsbild - en: Appearance - defaultValue: ["_self"] - choices: - - { - id: "top", - name: { de: "Artikelbild oben (volle Breite)", en: "Article picture top (full width)" }, - } - - { - id: "right", - name: { de: "Artikelbild rechts (volle Höhe)", en: "Article picture right (full height)" }, - } - - { - id: "bottom", - name: { de: "Artikelbild unten (volle Breite)", en: "Article picture left (full width)" }, - } - - { - id: "left", - name: { de: "Artikelbild links (volle Höhe)", en: "Article picture left (full height)" }, - } - - { - id: "after-teaser", - name: - { - de: "Artikelbild unter Teaser (volle Breite)", - en: "Article picture under teaser (full width)", - }, - } - - { id: "top-left", name: { de: "Artikelbild oben links", en: "Article picture above left" } } - - { id: "top-right", name: { de: "Artikelbild oben rechts", en: "Article picture above right" } } - - { id: "bottom-left", name: { de: "Artikelbild unten links", en: "Article picture below left" } } - - { - id: "bottom-right", - name: { de: "Artikelbild unten rechts", en: "Article picture below right" }, - } - - name: margin - type: object - meta: - label: { de: "Abstand nach außen (Margin)", en: "Distance to the outside (Margin)" } - subFields: - - name: top - type: string - meta: - widget: select - label: - de: Oben - en: Top - choices: - - { id: "", name: "Kein Abstand" } - - { id: "mt-xs", name: "Sehr kleiner Abstand" } - - { id: "mt-sm", name: "Kleiner Abstand" } - - { id: "mt-md", name: "Normal" } - - { id: "mt-lg", name: "Großer Abstand" } - - { id: "mt-xl", name: "Sehr großer Abstand" } - - name: right - type: string - meta: - widget: select - label: - de: Rechts - en: Right - choices: - - { id: "", name: "Kein Abstand" } - - { id: "mr-xs", name: "Sehr kleiner Abstand" } - - { id: "mr-sm", name: "Kleiner Abstand" } - - { id: "mr-md", name: "Normal" } - - { id: "mr-lg", name: "Großer Abstand" } - - { id: "mr-xl", name: "Sehr großer Abstand" } - - name: bottom - type: string - meta: - widget: select - label: - de: Unten - en: Bottom - choices: - - { id: "", name: "Kein Abstand" } - - { id: "mb-xs", name: "Sehr kleiner Abstand" } - - { id: "mb-sm", name: "Kleiner Abstand" } - - { id: "mb-md", name: "Normal" } - - { id: "mb-lg", name: "Großer Abstand" } - - { id: "mb-xl", name: "Sehr großer Abstand" } - - name: left - type: string - meta: - widget: select - label: - de: Links - en: Left - choices: - - { id: "", name: "Kein Abstand" } - - { id: "ml-xs", name: "Sehr kleiner Abstand" } - - { id: "ml-sm", name: "Kleiner Abstand" } - - { id: "ml-md", name: "Normal" } - - { id: "ml-lg", name: "Großer Abstand" } - - { id: "ml-xl", name: "Sehr großer Abstand" } - - name: padding - type: object - meta: - label: { de: "Abstand nach innen (Padding)", en: "Distance inside (Padding)" } - subFields: - - name: top - type: string - meta: - widget: select - label: - de: Oben - en: Top - choices: - - { id: "", name: "Kein Abstand" } - - { id: "pt-xs", name: "Sehr kleiner Abstand" } - - { id: "pt-sm", name: "Kleiner Abstand" } - - { id: "pt-md", name: "Normal" } - - { id: "pt-lg", name: "Großer Abstand" } - - { id: "pt-xl", name: "Sehr großer Abstand" } - - name: right - type: string - meta: - widget: select - label: - de: Rechts - en: Right - choices: - - { id: "", name: "Kein Abstand" } - - { id: "pr-xs", name: "Sehr kleiner Abstand" } - - { id: "pr-sm", name: "Kleiner Abstand" } - - { id: "pr-md", name: "Normal" } - - { id: "pr-lg", name: "Großer Abstand" } - - { id: "pr-xl", name: "Sehr großer Abstand" } - - name: bottom - type: string - meta: - widget: select - label: - de: Unten - en: Bottom - choices: - - { id: "", name: "Kein Abstand" } - - { id: "pb-xs", name: "Sehr kleiner Abstand" } - - { id: "pb-sm", name: "Kleiner Abstand" } - - { id: "pb-md", name: "Normal" } - - { id: "pb-lg", name: "Großer Abstand" } - - { id: "pb-xl", name: "Sehr großer Abstand" } - - name: left - type: string - meta: - widget: select - label: - de: Links - en: Left - choices: - - { id: "", name: "Kein Abstand" } - - { id: "pl-xs", name: "Sehr kleiner Abstand" } - - { id: "pl-sm", name: "Kleiner Abstand" } - - { id: "pl-md", name: "Normal" } - - { id: "pl-lg", name: "Großer Abstand" } - - { id: "pl-xl", name: "Sehr großer Abstand" } - - name: link - type: object - meta: - label: - de: Verlinkung - en: Link - css: - subFields: - - name: url - type: string - meta: - label: { de: "Ziel-URL", en: "Target URL" } - - name: text - type: string - meta: - label: { de: "Link-Beschriftung", en: "Link-Text" } - - name: target - type: string - meta: - widget: select - label: - de: Zielfenster - en: Target - defaultValue: ["_self"] - choices: - - { id: "_self", name: "(Standardwert) gleicher Tab oder Seite" } - - { id: "_blank", name: "Neuer Tab oder Fenster" } - - { id: "_parent", name: "Elternfenster" } diff --git a/api/collections/content.yml b/api/collections/content.yml index 22a1884..93a9aa7 100644 --- a/api/collections/content.yml +++ b/api/collections/content.yml @@ -9,7 +9,7 @@ uploadPath: ../media/content # Metaangaben zur Kollektion welche in der Admin-UI verwendet werden können meta: # Navigationseintrag in der Admin-UI - label: { de: "Seiteninhalt", en: "Page Content" } + label: { de: "Seiten", en: "Pages" } # Icon (Material UI) für den Navigationseintrag muiIcon: web # Identifizierung eines Eintrags für z.B. Select-Boxen in der Admin-UI @@ -24,11 +24,13 @@ meta: mediaQuery: "(max-width:599px)" primaryText: path columns: + - name - path # Desktop - type: table mediaQuery: "(min-width:600px)" columns: + - name - path imageFilter: diff --git a/api/collections/email.yml b/api/collections/email.yml new file mode 100644 index 0000000..60e3912 --- /dev/null +++ b/api/collections/email.yml @@ -0,0 +1,75 @@ +######################################################################## +# email +######################################################################## + +name: email +uploadPath: ../media/email +meta: + label: { de: "Kontaktformular", en: "Contact Form" } + muiIcon: email + rowIdentTpl: { twig: "{{ email }} - {{ subject }}" } + views: + - type: simpleList + mediaQuery: "(max-width: 600px)" + primaryText: email + secondaryText: subject + tertiaryText: insertTime + - type: table + columns: + - insertTime + - email + - subject + +# Zugriff auf diese Kollektion +permissions: + # öffentlicher Zugriff + public: + methods: + # Liste und Einzeleinträge lesen + # checked via hook + get: false + # neuen Eintrag anlegen + post: true + # Eintrag editieren + put: false + # Eintrag löschen + delete: false + # zum Projekt zugeordneter Benutzer ohne Zusatzberechtigungen + user: + methods: + get: false + post: false + put: false + delete: false + # token als Zusatzsicherung gegen Spam, mehr siehe Hook + "token:${PUBLIC_TOKEN}": + methods: + get: false + post: true + put: false + delete: false + +hooks: + post: + create: + type: javascript + file: hooks/email/post_create.js + +fields: + - name: name + type: string + meta: + label: { de: "Name", en: "Name" } + - name: email + type: string + meta: + label: { de: "Email", en: "Email" } + - name: subject + type: string + meta: + label: { de: "Betreff", en: "Subject" } + - name: message + type: string + meta: + widget: richtext + label: { de: "Nachricht", en: "Message" } diff --git a/api/collections/fields/_locale.yml b/api/collections/fields/_locale.yml index 6e07688..c7b6929 100644 --- a/api/collections/fields/_locale.yml +++ b/api/collections/fields/_locale.yml @@ -9,7 +9,7 @@ meta: en: "Defines in which language the menu is available. The default language is 'de-DE'.", } filter: true - defaultValue: "de-DE" + defaultValue: "de" choices: - { id: af, name: { key: "labels.locales.af" } } - { id: af-ZA, name: { key: "labels.locales.af-ZA" } } diff --git a/api/collections/general.yml b/api/collections/general.yml index 3ed6ff2..47c8c9f 100644 --- a/api/collections/general.yml +++ b/api/collections/general.yml @@ -37,25 +37,219 @@ meta: entryViewFields: tabsSection: meta: - expand: general + expand: meta tabs: - name: general meta: label: { de: "Allgemein", en: "General" } - source: generalInformation - - name: general2 - meta: - label: { de: "Allgemein2", en: "General2 " } fields: - name: public type: boolean meta: + defaultValue: true label: de: Veröffentlicht en: Public helperText: de: "Alle allgemeinen Informationen werden auf der Seite angezeigt." en: "All general information are displayed on the page." + - name: meta + meta: + label: { de: "Meta", en: "Meta" } + fields: + - name: meta + type: object + subFields: + - name: metaTitle + type: string + meta: + label: { de: "Titel der Webseite", en: "Page Title" } + - name: metaDescription + type: string + meta: + label: { de: "Beschreibung der Webseite", en: "Page Description" } + - name: metaTagRobots + type: string[] + meta: + widget: chipArray + label: + de: Robots + en: Robots + defaultValue: [] + autocomplete: true + choices: + - { id: "noindex", name: "noindex" } + - { id: "index", name: "index" } + - { id: "follow", name: "follow" } + - { id: "nofollow", name: "nofollow" } + - { id: "noimageindex", name: "noimageindex" } + - { id: "none", name: "none" } + - { id: "noarchive", name: "noarchive" } + - { id: "nocache", name: "nocache" } + - { id: "nosnippet", name: "nosnippet" } + - { id: "nnavailable_after", name: "nnavailable_after" } + helperText: + de: "Noindex: Weist eine Suchmaschine an, eine Seite nicht zu indizieren.
index: Weist eine Suchmaschine an, eine Seite zu indizieren. Beachten Sie, dass Sie dieses Meta-Tag nicht hinzufügen müssen; es ist die Voreinstellung.
follow: Auch wenn die Seite nicht indexiert ist, sollte der Crawler allen Links auf einer Seite folgen und Eigenkapital an die verlinkten Seiten weitergeben.
nofollow: Weist einen Crawler an, keinen Links auf einer Seite zu folgen oder Link-Equity weiterzugeben.
noimageindex: Weist einen Crawler an, keine Bilder auf einer Seite zu indizieren.
none: Entspricht der gleichzeitigen Verwendung der noindex- und nofollow-Tags.
noarchive: Suchmaschinen sollten keinen zwischengespeicherten Link zu dieser Seite auf einem SERP anzeigen.
nocache: Wie noarchive, aber nur von Internet Explorer und Firefox verwendet.
nosnippet: Weist eine Suchmaschine an, kein Snippet dieser Seite (d. h. Meta-Beschreibung) dieser Seite auf einem SERP anzuzeigen.
nnavailable_after: Suchmaschinen sollen diese Seite nach einem bestimmten Datum nicht mehr indexieren.
" + en: "Noindex: Tells a search engine not to index a page.
index: Tells a search engine to index a page. Note that you don’t need to add this meta tag; it’s the default.
follow: Even if the page isn’t indexed, the crawler should follow all the links on a page and pass equity to the linked pages.
nofollow: Tells a crawler not to follow any links on a page or pass along any link equity.
noimageindex: Tells a crawler not to index any images on a page.
none: Equivalent to using both the noindex and nofollow tags simultaneously.
noarchive: Search engines should not show a cached link to this page on a SERP.
nocache: Same as noarchive, but only used by Internet Explorer and Firefox.
nosnippet: Tells a search engine not to show a snippet of this page (i.e. meta description) of this page on a SERP.
nnavailable_after: Search engines should no longer index this page after a particular date.
" + - name: metaKeywords + type: string + meta: + label: { de: "SEO / Schlüsselwörter", en: "SEO / Keywords" } + helperText: + de: "Beispiel: Stichwort1, Stichwort2, Stichwort3" + en: "Example: keyword1, keyword2, keyword3" + - name: person + meta: + label: { de: "Person", en: "Person" } + fields: + - name: person + type: object + subFields: + - name: salutation + type: string + meta: + label: { de: "Anrede", en: "Salutation" } + - name: firstname + type: string + meta: + label: { de: "Vorname", en: "Firstname" } + - name: lastname + type: string + meta: + label: { de: "Nachname", en: "Lastname" } + - name: additional + type: string + meta: + label: { de: "Zusatz", en: "Additional" } + - name: street + type: string + meta: + label: { de: "Straße", en: "Street" } + - name: postcode + type: string + meta: + label: { de: "Postleitzahl", en: "Postcode" } + - name: city + type: string + meta: + label: { de: "Ort", en: "City" } + - name: tel + type: string + meta: + label: { de: "Telefonnummer", en: "Phone number" } + - name: fax + type: string + meta: + label: { de: "Faxnummer", en: "Fax number" } + - name: mobile + type: string + meta: + label: { de: "Handynummer", en: "Mobile number" } + - name: email + type: string + meta: + label: { de: "E-Mail", en: "E-Mail" } + - name: company + meta: + label: { de: "Unternehmen", en: "Company" } + fields: + - name: person + type: object + subFields: + - name: companyName + type: string + meta: + label: { de: "Name des Unternehmens", en: "Company Name" } + - name: companyWebUrl + type: string + meta: + label: { de: "URL zur Webseite", en: "Website URL" } + - name: companyAddresses + type: object[] + meta: + label: + de: Adresse + en: Adresse + css: + subFields: + - name: street + type: string + meta: + label: { de: "Straße", en: "Street" } + - name: houseNr + type: string + meta: + label: { de: "Hausnummer", en: "House number" } + - name: postcode + type: string + meta: + label: { de: "PLZ", en: "ZIP" } + - name: city + type: string + meta: + label: { de: "Ort", en: "City" } + - name: tel + type: string + meta: + label: { de: "Telefon", en: "Phone number" } + - name: fax + type: string + meta: + label: { de: "Fax", en: "Fax" } + - name: email + type: string + meta: + label: { de: "E-Mail", en: "E-Mail" } + - name: media + meta: + label: { de: "Medien", en: "Media" } + fields: + - name: media + type: object + subFields: + - name: favicon + type: file + meta: + label: { de: "Favicon", en: "Favicon" } + helperText: + de: "Ein Favicon ist ein kleines Icon, Symbol oder Logo, das von Webbrowsern verwendet wird, um eine Website auf wiedererkennbare Weise zu kennzeichnen." + en: "A favicon is a small icon, symbol, or logo used by web browsers to identify a website in a recognizable way." + - name: brand + type: file + meta: + label: { de: "Logo / Brand", en: "Logo / Brand" } + helperText: + de: "Logo der Seite" + en: "Page Logo" + - name: mediaFiles + type: object[] + meta: + label: { de: "Dateien", en: "Files" } + subFields: + - name: title + type: string + meta: + label: { de: "Datei-Titel", en: "File Title" } + - name: alternateText + type: string + meta: + label: { de: "Alternativer Text", en: "Alternate Text" } + - name: id + type: string + meta: + label: { de: "Technischer Name / ID", en: "Technical name / ID" } + - name: file + type: file + meta: + label: { de: "", en: "" } + - name: copyright + meta: + label: { de: "Copyright", en: "Copyright" } + fields: + - name: copyrightText + type: string + meta: + label: { de: "Copyright Text", en: "Copyright Text" } imageFilter: xs: @@ -110,231 +304,4 @@ permissions: put: false delete: false -fields: - - name: public - type: boolean - meta: - label: - de: Veröffentlicht - en: Public - helperText: - de: "Alle allgemeinen Informationen werden auf der Seite angezeigt." - en: "All general information are displayed on the page." - - name: generalInformation - type: tabs - meta: - label: - de: Allgemeine Information - en: General Information - activeTab: 0 - subFields: - - name: types - type: object - meta: - label: - de: Allgemein - en: General - css: - subFields: - - name: public - type: boolean - meta: - label: - de: Veröffentlicht - en: Public - helperText: - de: "Alle allgemeinen Informationen werden auf der Seite angezeigt." - en: "All general information are displayed on the page." - - name: meta - type: object - meta: - label: - de: Meta - en: Meta - css: - subFields: - - name: metaTitle - type: string - meta: - label: { de: "Titel der Webseite", en: "Page Title" } - - name: metaDescription - type: string - meta: - label: { de: "Beschreibung der Webseite", en: "Page Description" } - - name: metaTagRobots - type: string[] - meta: - widget: chipArray - label: - de: Robots - en: Robots - defaultValue: [] - autocomplete: true - choices: - - { id: "noindex", name: "noindex" } - - { id: "index", name: "index" } - - { id: "follow", name: "follow" } - - { id: "nofollow", name: "nofollow" } - - { id: "noimageindex", name: "noimageindex" } - - { id: "none", name: "none" } - - { id: "noarchive", name: "noarchive" } - - { id: "nocache", name: "nocache" } - - { id: "nosnippet", name: "nosnippet" } - - { id: "nnavailable_after", name: "nnavailable_after" } - helperText: - de: "Noindex: Weist eine Suchmaschine an, eine Seite nicht zu indizieren.
index: Weist eine Suchmaschine an, eine Seite zu indizieren. Beachten Sie, dass Sie dieses Meta-Tag nicht hinzufügen müssen; es ist die Voreinstellung.
follow: Auch wenn die Seite nicht indexiert ist, sollte der Crawler allen Links auf einer Seite folgen und Eigenkapital an die verlinkten Seiten weitergeben.
nofollow: Weist einen Crawler an, keinen Links auf einer Seite zu folgen oder Link-Equity weiterzugeben.
noimageindex: Weist einen Crawler an, keine Bilder auf einer Seite zu indizieren.
none: Entspricht der gleichzeitigen Verwendung der noindex- und nofollow-Tags.
noarchive: Suchmaschinen sollten keinen zwischengespeicherten Link zu dieser Seite auf einem SERP anzeigen.
nocache: Wie noarchive, aber nur von Internet Explorer und Firefox verwendet.
nosnippet: Weist eine Suchmaschine an, kein Snippet dieser Seite (d. h. Meta-Beschreibung) dieser Seite auf einem SERP anzuzeigen.
nnavailable_after: Suchmaschinen sollen diese Seite nach einem bestimmten Datum nicht mehr indexieren.
" - en: "Noindex: Tells a search engine not to index a page.
index: Tells a search engine to index a page. Note that you don’t need to add this meta tag; it’s the default.
follow: Even if the page isn’t indexed, the crawler should follow all the links on a page and pass equity to the linked pages.
nofollow: Tells a crawler not to follow any links on a page or pass along any link equity.
noimageindex: Tells a crawler not to index any images on a page.
none: Equivalent to using both the noindex and nofollow tags simultaneously.
noarchive: Search engines should not show a cached link to this page on a SERP.
nocache: Same as noarchive, but only used by Internet Explorer and Firefox.
nosnippet: Tells a search engine not to show a snippet of this page (i.e. meta description) of this page on a SERP.
nnavailable_after: Search engines should no longer index this page after a particular date.
" - - name: metaKeywords - type: string - meta: - label: { de: "SEO / Schlüsselwörter", en: "SEO / Keywords" } - helperText: - de: "Beispiel: Stichwort1, Stichwort2, Stichwort3" - en: "Example: keyword1, keyword2, keyword3" - - name: person - type: object - meta: - label: - de: Personendaten - en: Personal Data - css: - subFields: - - name: firstname - type: string - meta: - label: { de: "Vorname", en: "Firstname" } - - name: lastname - type: string - meta: - label: { de: "Nachname", en: "Lastname" } - - name: street - type: string - meta: - label: { de: "Straße", en: "Street" } - - name: postcode - type: string - meta: - label: { de: "Postleitzahl", en: "Postcode" } - - name: city - type: string - meta: - label: { de: "Ort", en: "City" } - - name: tel - type: string - meta: - label: { de: "Telefonnummer", en: "Phone number" } - - name: mobile - type: string - meta: - label: { de: "Handynummer", en: "Mobile number" } - - name: email - type: string - meta: - label: { de: "E-Mail", en: "E-Mail" } - - name: company - type: object - meta: - label: - de: Unternehmensdaten - en: Company Data - css: - subFields: - - name: companyName - type: string - meta: - label: { de: "Name des Unternehmens", en: "Company Name" } - - name: companyWebUrl - type: string - meta: - label: { de: "URL zur Webseite", en: "Website URL" } - - name: companyAddresses - type: object[] - meta: - label: - de: Adresse - en: Adresse - css: - subFields: - - name: street - type: string - meta: - label: { de: "Straße", en: "Street" } - - name: houseNr - type: string - meta: - label: { de: "Hausnummer", en: "House number" } - - name: postcode - type: string - meta: - label: { de: "PLZ", en: "ZIP" } - - name: city - type: string - meta: - label: { de: "Ort", en: "City" } - - name: tel - type: string - meta: - label: { de: "Telefon", en: "Phone number" } - - name: fax - type: string - meta: - label: { de: "Fax", en: "Fax" } - - name: email - type: string - meta: - label: { de: "E-Mail", en: "E-Mail" } - - name: media - type: object - meta: - label: - de: Media - en: Media - css: - subFields: - - name: favicon - type: file - meta: - label: { de: "Favicon", en: "Favicon" } - helperText: - de: "Ein Favicon ist ein kleines Icon, Symbol oder Logo, das von Webbrowsern verwendet wird, um eine Website auf wiedererkennbare Weise zu kennzeichnen." - en: "A favicon is a small icon, symbol, or logo used by web browsers to identify a website in a recognizable way." - - name: brand - type: file - meta: - label: { de: "Logo / Brand", en: "Logo / Brand" } - helperText: - de: "Logo der Seite" - en: "Page Logo" - - name: mediaFiles - type: object[] - meta: - label: { de: "Dateien", en: "Files" } - subFields: - - name: title - type: string - meta: - label: { de: "Datei-Titel", en: "File Title" } - - name: alternateText - type: string - meta: - label: { de: "Alternativer Text", en: "Alternate Text" } - - name: id - type: string - meta: - label: { de: "Technischer Name / ID", en: "Technical name / ID" } - - name: file - type: file - meta: - label: { de: "", en: "" } - - name: copyright - type: object - meta: - label: - de: Copyright - en: Copyright - css: - subFields: - - name: copyrightText - type: string - meta: - label: { de: "Copyright Text", en: "Copyright Text" } +fields: [] diff --git a/api/collections/navigation.yml b/api/collections/navigation.yml index ab6d371..bc69477 100644 --- a/api/collections/navigation.yml +++ b/api/collections/navigation.yml @@ -104,15 +104,15 @@ x-settings: &settings x-page: &page name: page - type: string + type: object meta: widget: select label: { de: Seite, en: "Page" } choices: endpoint: "content" mapping: - id: "id" - name: "path" + id: "path" + name: "name" params: count: 1 sort: "ASC" @@ -170,10 +170,10 @@ fields: name: de: Hauptnavigation en: Main Navigation - - id: service + - id: footer name: - de: Servicenavigation - en: Servicen Navigation + de: Fußzeile + en: Footer Navigation - !include fields/_locale.yml diff --git a/package.json b/package.json index 00d279c..895aa45 100644 --- a/package.json +++ b/package.json @@ -49,9 +49,11 @@ "typescript": "^4.6.3" }, "dependencies": { + "@mdi/js": "^6.7.96", "@sentry/browser": "^6.19.6", "@sentry/tracing": "^6.19.6", - "core-js": "3.22.2" + "core-js": "3.22.2", + "mdi-svelte": "^1.1.2" }, "optionalDependencies": { "@cypress/code-coverage": "^3.9.12", diff --git a/public/spa.html b/public/spa.html index 1598de3..55c51ac 100644 --- a/public/spa.html +++ b/public/spa.html @@ -49,6 +49,17 @@
+ + + diff --git a/src/api.ts b/src/api.ts index cde7833..977ef7f 100644 --- a/src/api.ts +++ b/src/api.ts @@ -1,6 +1,7 @@ import { apiBaseURL } from "./config" import * as sentry from "./sentry" import * as SSR from "../api/hooks/lib/ssr.js" +import config from "../api/hooks/config" // [MIT License](LICENSE.md) © [Jason Miller](https://jasonformat.com/) const _f = function (url, options): Promise { @@ -74,9 +75,12 @@ export const api = async ( headers?: { [key: string]: string } - params?: { - [key: string]: string - } + params?: APIParams + body?: any + noToken?: boolean + signal?: AbortSignal + _groupBy?: string + showErrors?: boolean } ): Promise<{ data: T; count: number }> => { if (typeof window === "undefined") { @@ -98,6 +102,7 @@ export const api = async ( } let method = "GET" + if (options?.method) method = options.method.toUpperCase() let query = "&count=1" if (options?.filter) query += "&filter=" + encodeURIComponent(JSON.stringify(options.filter)) @@ -105,6 +110,7 @@ export const api = async ( if (options?.limit) query += "&limit=" + options.limit if (options?.offset) query += "&offset=" + options.offset if (options?.projection) query += "&projection=" + options.projection + if (options?._groupBy) query += "&_groupBy=" + options._groupBy if (options?.params) { Object.keys(options.params).forEach((p) => { @@ -116,9 +122,17 @@ export const api = async ( "Content-Type": "application/json", } + // send jwt + if (!options?.noToken) { + // try { + // headers["Authorization"] = + // "Bearer " + (await getLogin()).tokenString + // } catch (e) { } + } + if (options?.headers) headers = { ...headers, ...options.headers } - let url = apiBaseURL + endpoint + (query ? "?" + query : "") + let url = apiBaseURL + endpoint + (query ? (endpoint.includes("?") ? query : "?" + query) : "") const span = sentry.currentTransaction()?.startChild({ op: "fetch", @@ -130,21 +144,46 @@ export const api = async ( headers["sentry-trace"] = trace_id } - const response = await _fetch(url, { - method, - mode: "cors", - headers, + try { + const response = await _fetch(url, { + method, + mode: "cors", + headers, + body: options?.body ? JSON.stringify(options.body) : null, + signal: options?.signal, + }) + + span?.finish() + + // @ts-ignore + let data = (await response?.json()) || null + + if (response?.status < 200 || response?.status >= 400) throw { response, data } + + // @ts-ignore + return { data, count: response?.headers?.get("x-results-count") || 0 } + } catch (e) { + if (options?.showErrors && !(e instanceof DOMException)) { + // newNotification({ + // class: "error", + // html: "Es ist ein Fehler aufgetreten! Bitte laden Sie die Seite neu und versuchen es später nocheinmal.", + // timeout: 6000, + // }) + } + throw e + } +} + +export const sendEmail = async (type: string = "contactForm", data: any, noToken?: boolean) => { + await api("email?type=" + type, { + method: "post", + body: data, + noToken, + showErrors: true, + headers: { + token: config.publicToken, + }, }) - - span?.finish() - - // @ts-ignore - let data = (await response?.json()) || null - - if (response?.status < 200 || response?.status >= 400) throw { response, data } - - // @ts-ignore - return { data, count: response?.headers?.get("x-results-count") || 0 } } export const getContent = async (path: string): Promise => { @@ -155,14 +194,14 @@ export const getContent = async (path: string): Promise => { return null } -export const getGeneralInformation = async (): Promise => { +export const getGeneralInformation = async (): Promise => { try { - let response = await api("general", { + let response = await api("general", { method: "get", offset: 0, limit: 1, filter: { - active: true, + public: true, }, }) return response.data @@ -185,3 +224,38 @@ export const getArticles = async (): Promise => { return null } } + +export const getNavigations = async (l: Locale): Promise => { + try { + let response = await api("navigation", { + method: "get", + offset: 0, + filter: { + locale: l.key, + }, + }) + return response.data + } catch (e) { + return null + } +} + +export const getGalleries = async (galIds: number[], params?: APIParams): Promise => { + try { + let response = await api("galleries", { + method: "get", + offset: 0, + params: { + sort: "title", + ...params, + }, + filter: { + _id: { $in: galIds }, + }, + }) + + return response.data + } catch (e) { + return null + } +} diff --git a/src/components/App.svelte b/src/components/App.svelte index 9d00d14..e21e0fb 100644 --- a/src/components/App.svelte +++ b/src/components/App.svelte @@ -1,9 +1,14 @@ - +
-

__PROJECT_TITLE__

- -
- 1 - 2 - 3 - 4 +
+ + +
+ +