473 lines
14 KiB
TypeScript
473 lines
14 KiB
TypeScript
import { defineStore } from 'pinia'
|
||
import { useRuntimeConfig } from '#app'
|
||
|
||
interface CompanyLogo {
|
||
url: string
|
||
alternativeText?: string
|
||
}
|
||
|
||
interface CompanyInfo {
|
||
company: string
|
||
street: string
|
||
postalcode: string
|
||
city: string
|
||
phone: string
|
||
email: string
|
||
contact: string
|
||
district?: string
|
||
latitude?: number
|
||
longitude?: number
|
||
web: string
|
||
invertlogo?: {
|
||
data?: {
|
||
attributes?: CompanyLogo
|
||
}
|
||
}
|
||
}
|
||
|
||
interface SEO {
|
||
pageTitle: string
|
||
seoDescription: string
|
||
seoKeywords: string
|
||
type: string
|
||
seoImage?: CompanyLogo | null
|
||
}
|
||
|
||
interface PageSection {
|
||
id: number
|
||
sectionText: string
|
||
sectionImage?: CompanyLogo | null
|
||
}
|
||
|
||
interface FAQ {
|
||
question: string
|
||
answer: string
|
||
locale: string
|
||
}
|
||
|
||
interface Page {
|
||
id: number
|
||
pageName: string
|
||
pageLink: string
|
||
header_image?: CompanyLogo | null
|
||
SEO?: SEO | null
|
||
faqs: FAQ[]
|
||
pageSections: PageSection[]
|
||
}
|
||
|
||
interface PageContent {
|
||
id: number
|
||
section: string
|
||
content: any // Strapi Blocks – ggf. präzisieren, wenn du eine feste Struktur nutzt
|
||
locale: string
|
||
}
|
||
|
||
interface CustomerProject {
|
||
id: number
|
||
projectTitle: string
|
||
launchDate?: string
|
||
projectDescription?: string
|
||
link?: string
|
||
webpage?: string
|
||
technologies: { titel: string; icon?: string }[]
|
||
projectImages: CompanyLogo[]
|
||
customer?: {
|
||
id: number
|
||
company: string
|
||
city: string
|
||
} | null
|
||
}
|
||
|
||
interface Customer {
|
||
id: number
|
||
company: string
|
||
city: string
|
||
logo?: CompanyLogo | null
|
||
invertLogo?: CompanyLogo | null
|
||
projects: CustomerProject[]
|
||
}
|
||
|
||
interface NewsArticle {
|
||
id: number
|
||
header: string
|
||
teaser: string
|
||
content: any
|
||
slug: string
|
||
image?: CompanyLogo | null
|
||
createdAt: string
|
||
SEO?: SEO | null
|
||
author?: {
|
||
id: number
|
||
name: string
|
||
// optional: weitere Felder aus dem Team-Modell
|
||
} | null
|
||
categories?: {
|
||
id: number
|
||
name: string
|
||
}[]
|
||
}
|
||
|
||
interface ContactData {
|
||
name: string;
|
||
email?: string;
|
||
phone?: string;
|
||
company?: string;
|
||
message: string;
|
||
language: string;
|
||
}
|
||
|
||
|
||
export const useMainStore = defineStore('main', {
|
||
state: () => ({
|
||
locale: 'de',
|
||
menuOpen: false,
|
||
contactBoxOpen: false,
|
||
scrollPosition: 0,
|
||
screenWidth: 1440,
|
||
heroBoxHeight: 0,
|
||
darkHeroBack: false,
|
||
companyinfo: null as CompanyInfo | null,
|
||
pages: [] as Page[],
|
||
pagecontents: [] as PageContent[],
|
||
customers: [] as Customer[],
|
||
projects: [] as CustomerProject[],
|
||
articles: [] as NewsArticle[],
|
||
categories: [] as { id: number; name: string }[],
|
||
dataFetched: false,
|
||
loading: false,
|
||
error: null as { message: string; stack?: string } | null,
|
||
}),
|
||
|
||
getters: {
|
||
invertLogoUrl: (state) => {
|
||
const runtimeConfig = useRuntimeConfig()
|
||
const logoUrl = state.companyinfo?.invertlogo?.data?.attributes?.url
|
||
return logoUrl
|
||
? `${runtimeConfig.public.cmsBaseUrl}${logoUrl}`
|
||
: '/uploads/dummy_Image_4abc3f04dd.webp'
|
||
},
|
||
isMobile: (state) => state.screenWidth < 768,
|
||
getPageByLink: (state) => (link: string) =>
|
||
state.pages.find((p) => p.pageLink === link),
|
||
getCustomerById: (state) => (id: number) =>
|
||
state.customers.find((c) => c.id === id),
|
||
getFaqsByPageId: (state) => (pageId: number) =>
|
||
state.pages.find((p) => p.id === pageId)?.faqs ?? [],
|
||
getFaqsByPageLink: (state) => (link: string, locale = state.locale) => {
|
||
const page = state.pages.find(p => p.pageLink === link && p.locale === locale)
|
||
return page?.faqs ?? []
|
||
},
|
||
getProjectByLink: (state) => (link: string) =>
|
||
state.projects.find(project => project.link === link),
|
||
getArticleBySlug: (state) => (slug: string) =>
|
||
state.articles.find((a) => a.slug === slug),
|
||
getRecommendedArticles: (state) => (article: NewsArticle) => {
|
||
if (!article?.categories || !Array.isArray(state.articles)) return []
|
||
|
||
const categoryIds = article.categories.map(c => c.id)
|
||
|
||
return state.articles.filter(a => {
|
||
if (a.id === article.id) return false // sich selbst ausschließen
|
||
if (!a.categories) return false
|
||
|
||
return a.categories.some(cat => categoryIds.includes(cat.id))
|
||
})
|
||
},
|
||
getArticlesByCategoryIds: (state) => (categoryIds: number[]) => {
|
||
if (!Array.isArray(categoryIds)) return []
|
||
|
||
return state.articles.filter(article =>
|
||
article.categories?.some(cat => categoryIds.includes(cat.id))
|
||
)
|
||
},
|
||
getPageContentBySection: (state) => (section: string): PageContent | undefined =>
|
||
state.pagecontents.find((pc) => pc.section === section),
|
||
getAllPageContentsBySection: (state) => (section: string): PageContent[] =>
|
||
state.pagecontents.filter((pc) => pc.section === section),
|
||
|
||
},
|
||
|
||
actions: {
|
||
toggleMenu() {
|
||
this.menuOpen = !this.menuOpen
|
||
},
|
||
closeMenu() {
|
||
this.menuOpen = false
|
||
},
|
||
toggleContactBubble() {
|
||
this.contactBoxOpen = !this.contactBoxOpen
|
||
},
|
||
setScrollPosition(pos: number) {
|
||
this.scrollPosition = pos
|
||
},
|
||
setScreenWidth(width: number) {
|
||
this.screenWidth = width
|
||
},
|
||
setHeroBoxHeight(height: number) {
|
||
this.heroBoxHeight = height
|
||
},
|
||
setDarkHeroBack(value: boolean) {
|
||
this.darkHeroBack = value
|
||
},
|
||
syncLocaleWithI18n() {
|
||
const { locale } = useI18n()
|
||
this.locale = locale.value
|
||
|
||
watch(locale, (newLocale) => {
|
||
this.locale = newLocale
|
||
})
|
||
},
|
||
// SEND CONTACT REQUEST TO STRAPI
|
||
|
||
sendContactRequestToCMS: async (contactData: ContactData): Promise<void> => {
|
||
const router = useRouter()
|
||
const config = useRuntimeConfig()
|
||
|
||
try {
|
||
await $fetch(`${config.public.cmsBaseUrl}/api/contacts`, {
|
||
method: 'POST',
|
||
headers: {
|
||
Authorization: `Bearer ${config.public.cmsToken}`,
|
||
'Content-Type': 'application/json',
|
||
},
|
||
body: {
|
||
data: {
|
||
name: contactData.name,
|
||
email: contactData.email,
|
||
phone: contactData.phone,
|
||
company: contactData.company,
|
||
text: contactData.message,
|
||
page: router.currentRoute.value.fullPath,
|
||
language: contactData.language,
|
||
},
|
||
},
|
||
})
|
||
} catch (error) {
|
||
console.error('Fehler beim Senden der Kontaktanfrage:', error)
|
||
throw error
|
||
}
|
||
},
|
||
|
||
|
||
// FETCH ALL THE DATA FROM STRAPI
|
||
async fetchInitialData() {
|
||
if (this.dataFetched) return
|
||
|
||
this.loading = true
|
||
const { public: cfg } = useRuntimeConfig()
|
||
|
||
try {
|
||
const [companyRes, pagesRes, customersRes, projectsRes, articlesRes, categoriesRes, pageContentsRes] = await Promise.all([
|
||
$fetch(`${cfg.cmsBaseUrl}/api/companyinfo?populate=*`, {
|
||
headers: { Authorization: `Bearer ${cfg.cmsToken}` },
|
||
}),
|
||
/* $fetch(`${cfg.cmsBaseUrl}/api/pages?populate=*`, {
|
||
headers: { Authorization: `Bearer ${cfg.cmsToken}` },
|
||
}), */
|
||
$fetch(`${cfg.cmsBaseUrl}/api/pages`, {
|
||
headers: { Authorization: `Bearer ${cfg.cmsToken}` },
|
||
params: {
|
||
locale: 'all',
|
||
populate: [
|
||
'header_image',
|
||
'SEO',
|
||
'SEO.seoImage',
|
||
'faqs',
|
||
'faqs.localizations',
|
||
'pageSections',
|
||
'pageSections.sectionImage',
|
||
'pageContents',
|
||
'pageContents.content',
|
||
].join(','),
|
||
}
|
||
|
||
}),
|
||
|
||
$fetch(`${cfg.cmsBaseUrl}/api/customers?populate=*`, {
|
||
headers: { Authorization: `Bearer ${cfg.cmsToken}` },
|
||
}),
|
||
$fetch(`${cfg.cmsBaseUrl}/api/references?populate=projectImages,Technologien,customer&sort=launchDate:desc`, {
|
||
headers: { Authorization: `Bearer ${cfg.cmsToken}` },
|
||
}),
|
||
$fetch(`${cfg.cmsBaseUrl}/api/newsarticels?populate=image,SEO,categories,author&locale=all&sort=createdAt:desc`, {
|
||
headers: { Authorization: `Bearer ${cfg.cmsToken}` },
|
||
}),
|
||
/* $fetch(`${cfg.cmsBaseUrl}/api/magazin-cats?populate=*&locale=all`, {
|
||
headers: { Authorization: `Bearer ${cfg.cmsToken}` },
|
||
}),
|
||
$fetch(`${cfg.cmsBaseUrl}/api/pagecontents?populate=*&locale=all`, {
|
||
headers: { Authorization: `Bearer ${cfg.cmsToken}` },
|
||
}), */
|
||
$fetch(`${cfg.cmsBaseUrl}/api/magazin-cats`, {
|
||
headers: { Authorization: `Bearer ${cfg.cmsToken}` },
|
||
params: {
|
||
locale: 'all',
|
||
populate: '*',
|
||
},
|
||
}),
|
||
$fetch(`${cfg.cmsBaseUrl}/api/pagecontents`, {
|
||
headers: { Authorization: `Bearer ${cfg.cmsToken}` },
|
||
params: {
|
||
locale: 'all',
|
||
// Falls `content` ein RichText-Feld ist und evtl. weitere Relationen,
|
||
// solltest du die Felder explizit nennen, nicht nur '*'
|
||
populate: {
|
||
content: '*',
|
||
},
|
||
'pagination[limit]': 3000,
|
||
},
|
||
}),
|
||
])
|
||
|
||
this.companyinfo = companyRes.data?.attributes ?? companyRes
|
||
|
||
this.pages = pagesRes.data.map((item: any) => {
|
||
const a = item.attributes
|
||
return {
|
||
id: item.id,
|
||
locale: a.locale,
|
||
pageName: a.pageName,
|
||
pageLink: a.pageLink,
|
||
header_image: a.header_image?.data
|
||
? {
|
||
url: a.header_image.data.attributes.url,
|
||
alternativeText: a.header_image.data.attributes.alternativeText,
|
||
}
|
||
: null,
|
||
SEO: a.SEO
|
||
? {
|
||
pageTitle: a.SEO.pageTitle,
|
||
seoDescription: a.SEO.seoDesicription, // absichtlicher Fehler
|
||
seoKeywords: a.SEO.seoKeywords,
|
||
type: a.SEO.type,
|
||
seoImage: a.SEO.seoImage?.data
|
||
? {
|
||
url: a.SEO.seoImage.data.attributes.url,
|
||
alternativeText: a.SEO.seoImage.data.attributes.alternativeText,
|
||
}
|
||
: null,
|
||
}
|
||
: null,
|
||
faqs: a.faqs?.data?.map((f: any) => ({
|
||
question: f.attributes.question,
|
||
answer: f.attributes.answer,
|
||
locale: f.attributes.locale,
|
||
})) ?? [],
|
||
pageSections: a.pageSections?.map((s: any) => ({
|
||
id: s.id,
|
||
sectionText: s.sectionText,
|
||
sectionImage: s.sectionImage?.data
|
||
? {
|
||
url: s.sectionImage.data.attributes.url,
|
||
alternativeText: s.sectionImage.data.attributes.alternativeText,
|
||
}
|
||
: null,
|
||
})) ?? [],
|
||
}
|
||
})
|
||
|
||
this.pagecontents = pageContentsRes.data.map((item: any) => ({
|
||
id: item.id,
|
||
section: item.attributes.section,
|
||
content: item.attributes.content,
|
||
locale: item.attributes.locale
|
||
}))
|
||
|
||
this.customers = customersRes.data.map((item: any) => {
|
||
const a = item.attributes
|
||
return {
|
||
id: item.id,
|
||
company: a.company,
|
||
city: a.city,
|
||
logo: a.logo?.data?.attributes ?? null,
|
||
invertLogo: a.invertLogo?.data?.attributes ?? null,
|
||
projects: [], // Wird durch references geladen
|
||
}
|
||
})
|
||
|
||
this.projects = projectsRes.data.map((item: any) => {
|
||
const a = item.attributes
|
||
return {
|
||
id: item.id,
|
||
projectTitle: a.projectTitle,
|
||
launchDate: a.launchDate,
|
||
projectDescription: a.projectDescription,
|
||
link: a.link,
|
||
webpage: a.webpage,
|
||
technologies: a.Technologien?.data?.map((t: any) => ({
|
||
titel: t.attributes.titel,
|
||
icon: t.attributes.icon,
|
||
})) ?? [],
|
||
projectImages: a.projectImages?.data?.map((img: any) => ({
|
||
url: img.attributes.url,
|
||
alternativeText: img.attributes.alternativeText,
|
||
})) ?? [],
|
||
customer: a.customer?.data
|
||
? {
|
||
id: a.customer.data.id,
|
||
company: a.customer.data.attributes.company,
|
||
city: a.customer.data.attributes.city,
|
||
}
|
||
: null,
|
||
}
|
||
})
|
||
|
||
this.articles = articlesRes.data.map((item: any) => {
|
||
const a = item.attributes
|
||
return {
|
||
id: item.id,
|
||
header: a.header,
|
||
teaser: a.teaser,
|
||
content: a.content,
|
||
slug: a.slug,
|
||
createdAt: a.createdAt,
|
||
image: a.image?.data
|
||
? {
|
||
url: a.image.data.attributes.url,
|
||
alternativeText: a.image.data.attributes.alternativeText,
|
||
}
|
||
: null,
|
||
SEO: a.SEO
|
||
? {
|
||
pageTitle: a.SEO.pageTitle,
|
||
seoDescription: a.SEO.seoDesicription,
|
||
seoKeywords: a.SEO.seoKeywords,
|
||
type: a.SEO.type,
|
||
seoImage: a.SEO.seoImage?.data
|
||
? {
|
||
url: a.SEO.seoImage.data.attributes.url,
|
||
alternativeText: a.SEO.seoImage.data.attributes.alternativeText,
|
||
}
|
||
: null,
|
||
}
|
||
: null,
|
||
author: a.author?.data
|
||
? {
|
||
id: a.author.data.id,
|
||
name: a.author.data.attributes.name,
|
||
}
|
||
: null,
|
||
categories: a.categories?.data?.map((c: any) => ({
|
||
id: c.id,
|
||
name: c.attributes.category,
|
||
})) ?? [],
|
||
}
|
||
})
|
||
|
||
this.categories = categoriesRes.data.map((item: any) => ({
|
||
id: item.id,
|
||
name: item.attributes.category,
|
||
}))
|
||
|
||
this.dataFetched = true
|
||
} catch (err) {
|
||
const e = err as Error
|
||
this.error = { message: e.message, stack: e.stack }
|
||
console.error('Fetch-Fehler:', e)
|
||
} finally {
|
||
this.loading = false
|
||
}
|
||
},
|
||
},
|
||
})
|