Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(docs): strapi integration #416

Merged
merged 6 commits into from
Oct 23, 2023
Merged
Show file tree
Hide file tree
Changes from 3 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
5 changes: 5 additions & 0 deletions .changeset/polite-points-rule.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"docs": minor
---

Add strapi integration example
11 changes: 11 additions & 0 deletions apps/docs/.vitepress/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,17 @@ export const sidebar = [
},
],
},
{
text: "Integrations",
link: "/resources/integrations/",
items: [
{
text: "Multi CMS instance",
link: "/resources/integrations/multi-cms",
},
{ text: "Strapi", link: "/resources/integrations/strapi/" },
],
},
{ text: "Community Modules", link: "/resources/community-modules/" },
{ text: "Troubleshooting", link: "/resources/troubleshooting" },
],
Expand Down
24 changes: 24 additions & 0 deletions apps/docs/src/resources/integrations/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
---
head:
- - meta
- name: og:title
content: Integrations - Shopware Frontends
- - meta
- name: og:description
content: "Collection of common integrations with Shopware Composable Frontends."
- - meta
- name: og:image
content: "https://frontends-og-image.vercel.app/Integrations?fontSize=150px"
---

# Integrations

This is your go-to resource for seamlessly incorporating various platforms into Shopware Frontends.
Explore the following sub-pages to find detailed instructions on integrating different systems,
ensuring a harmonious and efficient synergy between your Shopware store and diverse external services.
Streamline your operations and enhance your user experience with these comprehensive integration guide.

## CMS

<PageRef title="Multi CMS instance" sub="How to integrate 3rd CMS systems" page="multi-cms" />
<PageRef title="Strapi" sub="How to integrate Strapi CMS with the Shopware Frontends" page="strapi/index" />
109 changes: 109 additions & 0 deletions apps/docs/src/resources/integrations/multi-cms.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
---
head:
- - meta
- name: og:title
content: Integrations - Multi CMS - Shopware Frontends
- - meta
- name: og:description
content: "Multi CMS instance"
- - meta
- name: og:image
content: "https://frontends-og-image.vercel.app/Multi%20CMS?fontSize=150px"
---

# Multi CMS

Adding a New CMS Instance in Conjunction with Shopware CMS
This documentation segment guides users through the process of incorporating an additional CMS instance and use it seamlessly with the Shopware CMS.

:::warning
This example is written for the vue-demo-store template
:::

## Adding middleware

Add middleware injection to the main routing resolver file.

`templates/vue-demo-store/pages/[...all].vue`

```ts{16-23,46-54,61-66}
import { Ref, resolveComponent } from "vue";
patzick marked this conversation as resolved.
Show resolved Hide resolved
import { pascalCase } from "scule";
import {
useNavigationContext,
useNavigationSearch,
} from "@shopware-pwa/composables-next";
import { SeoUrl } from "@shopware-pwa/types";
patzick marked this conversation as resolved.
Show resolved Hide resolved
const { clearBreadcrumbs } = useBreadcrumbs();

const NOT_FOUND_COMPONENT = "errors/RoutingNotFound";
const { resolvePath } = useNavigationSearch();
const route = useRoute();
const { locale } = useI18n();
const routePath = route.path.replace(`${locale.value}`, "").replace("//", "/");

/**
* Load CMS resolver if exists
*/
let cmsPageRendererComponent: VNode | null = null;
const cmsPageRenderer = inject<((path: string) => VNode | null) | null>(
"pageRenderMiddlewares",
null,
);

const { data: seoResult } = await useAsyncData(
"cmsResponse" + routePath,
async () => {
// For client links if the history state contains seo url information we can omit the api call
if (process.client) {
if (history.state?.routeName) {
return {
routeName: history.state?.routeName,
foreignKey: history.state?.foreignKey,
};
}
}
const seoUrl = await resolvePath(routePath);
return seoUrl;
},
);

const { routeName, foreignKey } = useNavigationContext(
seoResult as Ref<SeoUrl>,
);

/**
* If there is no Shopware CMS component and an additional CMS
* resolver is available, fetch content
*/
const componentName = routeName.value;
const path = routePath.substring(1);
if (!componentName && cmsPageRenderer) {
cmsPageRendererComponent = await cmsPageRenderer(path);
}

onBeforeRouteLeave(() => {
clearBreadcrumbs();
});

function render() {
/**
* Render additional CMS component if exists
*/
if (cmsPageRendererComponent) {
return cmsPageRendererComponent;
}

if (!componentName)
return h("div", h(resolveComponent(pascalCase(NOT_FOUND_COMPONENT))));

const componentNameToResolve = pascalCase(componentName as string);
const cmsPageView = routeName && resolveComponent(componentNameToResolve);
if (cmsPageView) {
if (cmsPageView === componentNameToResolve)
return h("div", {}, "Problem resolving component: " + componentName);
return h("div", h(cmsPageView, { navigationId: foreignKey.value }));
}
return h("div", {}, "Loading...");
}
```
144 changes: 144 additions & 0 deletions apps/docs/src/resources/integrations/strapi/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
---
head:
- - meta
- name: og:title
content: Strapi integration (CMS)
- - meta
- name: og:description
content: "In this chapter you will learn how to integrate Strapi (CMS)."
- - meta
- name: og:image
content: "https://frontends-og-image.vercel.app/Strapi%Integration.png?fontSize=120px"
nav:
position: 60
---

# Strapi integration

[Strapi](https://strapi.io/) is a headless CMS that can be integrated with the Composable Frontends.
This example requires Nuxt 3 instance

### How to install

Add Strapi Nuxt module as a dev dependencies

```cmd
pnpm add -D @nuxtjs/strapi
```

Update Nuxt config `nuxt.config.ts`

```js
export default {
modules: ["@nuxtjs/strapi"],
};
```

More about installation can be found [HERE](https://strapi.nuxtjs.org/setup)

### Cases of use

## Fetching and displaying single element

As a example we will add a global banner to our demo shop.
At the beginning we created a single type on the Strapi collection, with fallowing fields

```ts
interface {
text: string; // short input field - this will represent a text that we want to display in the banner
color: string; // short input field - this will represent a color of the banner (this can be done also with color picker filed, but for this example we will use input text)
}

```

The next step is to create a banner component

```vue
<script setup lang="ts">
interface GlobalBanner {
text: string;
color: string;
}

const { findOne } = useStrapi();

const { data } = await findOne<GlobalBanner>("global-banner");
const bgColor = computed(() => data.attributes?.color || "#fff");
</script>
<template>
<section>
<div class="text-center py-1" :style="{ 'background-color': bgColor }">
{{ data.attributes.text }}
</div>
</section>
</template>
```

Now we can add our component to the layout.
`frontends/templates/vue-demo-store/layouts/default.vue`

```vue
<template>
<div>
<GlobalBanner />
<LayoutHeader />
<LayoutNotifications />
<main class="mx-auto">
<slot />
</main>
<LayoutFooter />
</div>
</template>
```

## Fetching and displaying pages

:::warning
This example is written for the vue-demo-store template and assuming that you [implemented multi CMS middleware](/resources/integrations/multi-cms.md)
:::

Create new collection type `Page` on the Stripe admin site with fields:

```ts
interface {
text: string; // Content page
seoUrl: string; // Page slug
}
```

Composable for resolving components

```ts
interface StripePage {
text: string;
seoUrl: string;
}
export function useSWStrapi() {
const getPage = async (route: string) => {
const { findOne } = useStrapi();
const response = await findOne<StripePage>("pages", undefined, {
filters: {
seoUrl: route,
},
});
return response;
};

const resolveComponent = async (route: string) => {
const page = await getPage(route);
if (!page.data[0]) return null;
return h("div", {}, page.data[0].attributes.text);
};

return {
resolveComponent,
};
}
```

Provide Strapi resolver to the `pageRenderMiddlewares`

```ts
const { resolveComponent } = useSWStrapi();
provide("pageRenderMiddlewares", resolveComponent);
```
21 changes: 21 additions & 0 deletions examples/vue-demo-store-strapi/components/GlobalBanner.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<script setup lang="ts">
interface GlobalBanner {
text: string;
color: string;
}

const { findOne } = useStrapi();

const { data } = await findOne<GlobalBanner>("global-banner");
const bgColor = computed(() => data.attributes?.color || "#fff");
</script>
<template>
<section>
<div
class="text-center py-1 text-white text-sm"
:style="{ 'background-color': bgColor }"
>
{{ data.attributes.text }}
</div>
</section>
</template>
22 changes: 22 additions & 0 deletions examples/vue-demo-store-strapi/composables/useSWStrapi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
export function useSWStrapi() {
const getPage = async (route: string) => {
const { findOne } = useStrapi();
const response = await findOne("pages", undefined, {
filters: {
seoUrl: route,
},
});
return response;
};

const resolveComponent = async (route: string) => {
const page = await getPage(route);
if (!page.data[0]) return null;
console.log("page", page);
return h("div", {}, page.data[0].attributes.text);
};

return {
resolveComponent,
};
}
5 changes: 5 additions & 0 deletions examples/vue-demo-store-strapi/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "example-vue-demo-store-strapi",
patzick marked this conversation as resolved.
Show resolved Hide resolved
"version": "0.0.0",
"private": true
}
11 changes: 6 additions & 5 deletions templates/vue-demo-store/pages/[...all].vue
Original file line number Diff line number Diff line change
Expand Up @@ -37,16 +37,17 @@ const { data: seoResult } = await useAsyncData(
},
);

onBeforeRouteLeave(() => {
clearBreadcrumbs();
});

const { routeName, foreignKey } = useNavigationContext(
seoResult as Ref<SeoUrl>,
);

const componentName = routeName.value;

onBeforeRouteLeave(() => {
clearBreadcrumbs();
});

function render() {
const componentName = routeName.value;
if (!componentName)
return h("div", h(resolveComponent(pascalCase(NOT_FOUND_COMPONENT))));

Expand Down
Loading