[
  {
    "path": ".gitignore",
    "content": "\n# Created by https://www.toptal.com/developers/gitignore/api/node,macos,windows,visualstudiocode\n# Edit at https://www.toptal.com/developers/gitignore?templates=node,macos,windows,visualstudiocode\n\n### macOS ###\n# General\n.DS_Store\n.AppleDouble\n.LSOverride\n\n# Icon must end with two \\r\nIcon\n\n\n# Thumbnails\n._*\n\n# Files that might appear in the root of a volume\n.DocumentRevisions-V100\n.fseventsd\n.Spotlight-V100\n.TemporaryItems\n.Trashes\n.VolumeIcon.icns\n.com.apple.timemachine.donotpresent\n\n# Directories potentially created on remote AFP share\n.AppleDB\n.AppleDesktop\nNetwork Trash Folder\nTemporary Items\n.apdisk\n\n### Node ###\n# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\nlerna-debug.log*\n.pnpm-debug.log*\n\n# Diagnostic reports (https://nodejs.org/api/report.html)\nreport.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json\n\n# Runtime data\npids\n*.pid\n*.seed\n*.pid.lock\n\n# Directory for instrumented libs generated by jscoverage/JSCover\nlib-cov\n\n# Coverage directory used by tools like istanbul\ncoverage\n*.lcov\n\n# nyc test coverage\n.nyc_output\n\n# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)\n.grunt\n\n# Bower dependency directory (https://bower.io/)\nbower_components\n\n# node-waf configuration\n.lock-wscript\n\n# Compiled binary addons (https://nodejs.org/api/addons.html)\nbuild/Release\n\n# Dependency directories\nnode_modules/\njspm_packages/\n\n# Snowpack dependency directory (https://snowpack.dev/)\nweb_modules/\n\n# TypeScript cache\n*.tsbuildinfo\n\n# Optional npm cache directory\n.npm\n\n# Optional eslint cache\n.eslintcache\n\n# Microbundle cache\n.rpt2_cache/\n.rts2_cache_cjs/\n.rts2_cache_es/\n.rts2_cache_umd/\n\n# Optional REPL history\n.node_repl_history\n\n# Output of 'npm pack'\n*.tgz\n\n# Yarn Integrity file\n.yarn-integrity\n\n# dotenv environment variables file\n.env\n.env.test\n.env.production\n\n# parcel-bundler cache (https://parceljs.org/)\n.cache\n.parcel-cache\n\n# Next.js build output\n.next\nout\n\n# Nuxt.js build / generate output\n.nuxt\ndist\n\n# Gatsby files\n.cache/\n# Comment in the public line in if your project uses Gatsby and not Next.js\n# https://nextjs.org/blog/next-9-1#public-directory-support\n# public\n\n# vuepress build output\n.vuepress/dist\n\n# Serverless directories\n.serverless/\n\n# FuseBox cache\n.fusebox/\n\n# DynamoDB Local files\n.dynamodb/\n\n# TernJS port file\n.tern-port\n\n# Stores VSCode versions used for testing VSCode extensions\n.vscode-test\n\n# yarn v2\n.yarn/cache\n.yarn/unplugged\n.yarn/build-state.yml\n.yarn/install-state.gz\n.pnp.*\n\n### VisualStudioCode ###\n.vscode/*\n!.vscode/tasks.json\n!.vscode/launch.json\n!.vscode/extensions.json\n*.code-workspace\n\n# Local History for Visual Studio Code\n.history/\n\n### VisualStudioCode Patch ###\n# Ignore all local history of files\n.history\n.ionide\n\n### Windows ###\n# Windows thumbnail cache files\nThumbs.db\nThumbs.db:encryptable\nehthumbs.db\nehthumbs_vista.db\n\n# Dump file\n*.stackdump\n\n# Folder config file\n[Dd]esktop.ini\n\n# Recycle Bin used on file shares\n$RECYCLE.BIN/\n\n# Windows Installer files\n*.cab\n*.msi\n*.msix\n*.msm\n*.msp\n\n# Windows shortcuts\n*.lnk\n\n# End of https://www.toptal.com/developers/gitignore/api/node,macos,windows,visualstudiocode\n\n# Template development directory\ntemplate-dev/"
  },
  {
    "path": "LICENSE.txt",
    "content": "MIT License\n\nCopyright (c) 2021 Strapi\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "> [!WARNING]\n> This repository is only compatible with Strapi v4 version<br/>\n> The most recent information can be found at https://docs.strapi.io/\n\n\n# Strapi starters & templates\n\nMonorepo for all official Strapi v4 starters and templates.\n\n### Starters\n\n> As you may have [noticed](https://github.com/strapi/starters-and-templates/issues?q=is%3Aissue+is%3Aopen+install), a lot of the Starters are out of date and given constraints in bandwidth and other priorities, we've decided to sunset all Starters and only actively maintain a single Next.js Starter. \n\n\n- [Gatsby Blog](./packages/starters/gatsby-blog)\n- [Next Blog](./packages/starters/next-blog)\n- [Next Corporate Site](./packages/starters/next-corporate)\n\n### Templates\n\n- [Blog](./packages/templates/blog)\n- [Corporate](./packages/templates/corporate)\n- [Ecommerce](./packages/templates/ecommerce)\n"
  },
  {
    "path": "lerna.json",
    "content": "{\n  \"packages\": [\n    \"packages/templates/*\",\n    \"packages/starters/*\"\n  ],\n  \"npmClient\": \"yarn\",\n  \"useWorkspaces\": true,\n  \"version\": \"independent\"\n}\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"@strapi/starters-and-templates\",\n  \"version\": \"1.0.0\",\n  \"private\": true,\n  \"description\": \"All Strapi templates\",\n  \"repository\": \"https://github.com/strapi/starters-and-templates.git\",\n  \"license\": \"MIT\",\n  \"author\": {\n    \"name\": \"Strapi team\",\n    \"email\": \"hi@strapi.io\",\n    \"url\": \"https://strapi.io\"\n  },\n  \"maintainers\": [\n    {\n      \"name\": \"Strapi team\",\n      \"email\": \"hi@strapi.io\",\n      \"url\": \"https://strapi.io\"\n    }\n  ],\n  \"workspaces\": [\n    \"packages/**/*\"\n  ],\n  \"devDependencies\": {\n    \"lerna\": \"^4.0.0\"\n  }\n}\n"
  },
  {
    "path": "packages/starters/.gitkeep",
    "content": ""
  },
  {
    "path": "packages/starters/gatsby-blog/README.md",
    "content": "# Strapi Starter Gatsby Blog\n\nGatsby starter for creating a blog with Strapi.\n\nThis starter allows you to try Strapi with Gatsby with the example of a simple blog. It is fully customizable and due to the fact that it is open source, fully open to contributions. So do not hesitate to add new features and report bugs!\n\nThis starter uses the [Strapi blog template](https://github.com/strapi/starters-and-templates/tree/main/packages/templates/blog)\n\n## Getting started\n\nUse our `create-strapi-starter` CLI to create your project.\n\n```sh\n# Using Yarn\nyarn create strapi-starter my-project gatsby-blog\n\n# Or using NPM\nnpx create-strapi-starter my-project gatsby-blog\n```\n\nThe CLI will create a monorepo, install dependencies, and run your project automatically.\n\nThe Gatsby frontend server will run here => [http://localhost:3000](http://localhost:3000)\n\nThe Strapi backend server will run here => [http://localhost:1337](http://localhost:1337)\n\nYou will however need to manually create a full access [API token](https://docs.strapi.io/developer-docs/latest/setup-deployment-guides/configurations/optional/api-tokens.html) in Strapi. Once it's created, save it as `STRAPI_TOKEN` in your environment variables.\n\n## Deploying to production\n\nYou will need to deploy the `frontend` and `backend` projects separately. Here are the docs to deploy each one:\n\n- [Deploy Strapi](https://strapi.io/documentation/developer-docs/latest/setup-deployment-guides/deployment.html#hosting-provider-guides)\n- [Deploy Gatsby](https://www.gatsbyjs.com/docs/deploying-and-hosting/)\n\nEnjoy this starter!"
  },
  {
    "path": "packages/starters/gatsby-blog/package.json",
    "content": "{\n  \"name\": \"@strapi/starter-gatsby-blog\",\n  \"version\": \"1.0.7\",\n  \"description\": \"Strapi blog starter with Gatsby\",\n  \"keywords\": [\n    \"strapi\",\n    \"starter\",\n    \"gatsby\",\n    \"blog\"\n  ],\n  \"homepage\": \"https://github.com/strapi/starters-and-templates/tree/main/packages/starters/gatsby-blog#readme\",\n  \"bugs\": {\n    \"url\": \"https://github.com/strapi/starters-and-templates/issues\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/strapi/starters-and-templates.git\"\n  },\n  \"license\": \"MIT\",\n  \"author\": {\n    \"name\": \"Strapi team\",\n    \"email\": \"hi@strapi.io\",\n    \"url\": \"https://strapi.io\"\n  },\n  \"maintainers\": [\n    {\n      \"name\": \"Strapi team\",\n      \"email\": \"hi@strapi.io\",\n      \"url\": \"https://strapi.io\"\n    }\n  ]\n}\n"
  },
  {
    "path": "packages/starters/gatsby-blog/starter/.eslintrc.js",
    "content": "module.exports = {\n  globals: {\n    __PATH_PREFIX__: true,\n  },\n  extends: [\"react-app\", \"prettier\"],\n  plugins: [\"prettier\"],\n  rules: {\n    \"prettier/prettier\": [\n      \"error\",\n      {\n        printWidth: 80,\n        singleQuote: false,\n        trailingComma: \"es5\",\n        semi: false,\n        tabWidth: 2,\n      },\n    ],\n  },\n}\n"
  },
  {
    "path": "packages/starters/gatsby-blog/starter/.gitignore",
    "content": "node_modules/\n.cache/\npublic\n.env\n.env.production\n.env.local\n.env.development\n"
  },
  {
    "path": "packages/starters/gatsby-blog/starter/README.md",
    "content": "<p align=\"center\">\n  <a href=\"https://www.gatsbyjs.com/?utm_source=starter&utm_medium=readme&utm_campaign=minimal-starter\">\n    <img alt=\"Gatsby\" src=\"https://www.gatsbyjs.com/Gatsby-Monogram.svg\" width=\"60\" />\n  </a>\n</p>\n<h1 align=\"center\">\n  Gatsby minimal starter\n</h1>\n\n## 🚀 Quick start\n\n1.  **Create a Gatsby site.**\n\n    Use the Gatsby CLI to create a new site, specifying the minimal starter.\n\n    ```shell\n    # create a new Gatsby site using the minimal starter\n    npm init gatsby\n    ```\n\n2.  **Start developing.**\n\n    Navigate into your new site’s directory and start it up.\n\n    ```shell\n    cd my-gatsby-site/\n    npm run develop\n    ```\n\n3.  **Open the code and start customizing!**\n\n    Your site is now running at http://localhost:8000!\n\n    Edit `src/pages/index.js` to see your site update in real-time!\n\n4.  **Learn more**\n\n    - [Documentation](https://www.gatsbyjs.com/docs/?utm_source=starter&utm_medium=readme&utm_campaign=minimal-starter)\n\n    - [Tutorials](https://www.gatsbyjs.com/tutorial/?utm_source=starter&utm_medium=readme&utm_campaign=minimal-starter)\n\n    - [Guides](https://www.gatsbyjs.com/tutorial/?utm_source=starter&utm_medium=readme&utm_campaign=minimal-starter)\n\n    - [API Reference](https://www.gatsbyjs.com/docs/api-reference/?utm_source=starter&utm_medium=readme&utm_campaign=minimal-starter)\n\n    - [Plugin Library](https://www.gatsbyjs.com/plugins?utm_source=starter&utm_medium=readme&utm_campaign=minimal-starter)\n\n    - [Cheat Sheet](https://www.gatsbyjs.com/docs/cheat-sheet/?utm_source=starter&utm_medium=readme&utm_campaign=minimal-starter)\n\n## 🚀 Quick start (Gatsby Cloud)\n\nDeploy this starter with one click on [Gatsby Cloud](https://www.gatsbyjs.com/cloud/):\n\n[<img src=\"https://www.gatsbyjs.com/deploynow.svg\" alt=\"Deploy to Gatsby Cloud\">](https://www.gatsbyjs.com/dashboard/deploynow?url=https://github.com/gatsbyjs/gatsby-starter-minimal)\n"
  },
  {
    "path": "packages/starters/gatsby-blog/starter/gatsby-browser.js",
    "content": "import \"./src/styles/global.css\"\n"
  },
  {
    "path": "packages/starters/gatsby-blog/starter/gatsby-config.js",
    "content": "require(\"dotenv\").config({\n  path: `.env.${process.env.NODE_ENV}`,\n})\n\nmodule.exports = {\n  plugins: [\n    \"gatsby-plugin-gatsby-cloud\",\n    \"gatsby-plugin-postcss\",\n    {\n      resolve: \"gatsby-source-strapi\",\n      options: {\n        apiURL: process.env.STRAPI_API_URL || \"http://localhost:1337\",\n        accessToken: process.env.STRAPI_TOKEN,\n        collectionTypes: [\n          {\n            singularName: \"article\",\n            queryParams: {\n              publicationState:\n                process.env.GATSBY_IS_PREVIEW === \"true\" ? \"preview\" : \"live\",\n              populate: {\n                cover: \"*\",\n                blocks: {\n                  populate: \"*\",\n                },\n              },\n            },\n          },\n          {\n            singularName: \"author\",\n          },\n          {\n            singularName: \"category\",\n          },\n        ],\n        singleTypes: [\n          {\n            singularName: \"about\",\n            queryParams: {\n              populate: {\n                blocks: {\n                  populate: \"*\",\n                },\n              },\n            },\n          },\n          {\n            singularName: \"global\",\n            queryParams: {\n              populate: {\n                favicon: \"*\",\n                defaultSeo: {\n                  populate: \"*\",\n                },\n              },\n            },\n          },\n        ],\n      },\n    },\n    \"gatsby-plugin-image\",\n    \"gatsby-plugin-sharp\",\n    \"gatsby-transformer-sharp\",\n    \"gatsby-transformer-remark\",\n  ],\n}\n"
  },
  {
    "path": "packages/starters/gatsby-blog/starter/gatsby-node.js",
    "content": "const path = require(\"path\")\n\nexports.createPages = async ({ graphql, actions, reporter }) => {\n  const { createPage } = actions\n\n  // Define a template for blog post\n  const articlePost = path.resolve(\"./src/templates/article-post.js\")\n\n  const result = await graphql(\n    `\n      {\n        allStrapiArticle {\n          nodes {\n            title\n            slug\n          }\n        }\n      }\n    `\n  )\n\n  if (result.errors) {\n    reporter.panicOnBuild(\n      `There was an error loading your Strapi articles`,\n      result.errors\n    )\n\n    return\n  }\n\n  const articles = result.data.allStrapiArticle.nodes\n\n  if (articles.length > 0) {\n    articles.forEach((article) => {\n      createPage({\n        path: `/article/${article.slug}`,\n        component: articlePost,\n        context: {\n          slug: article.slug,\n        },\n      })\n    })\n  }\n}\n"
  },
  {
    "path": "packages/starters/gatsby-blog/starter/package.json",
    "content": "{\n  \"name\": \"my-gatsby-blog\",\n  \"version\": \"1.0.7\",\n  \"private\": true,\n  \"description\": \"Strapi Gatsby Blog\",\n  \"author\": \"Strapi team\",\n  \"keywords\": [\n    \"gatsby\"\n  ],\n  \"scripts\": {\n    \"develop\": \"gatsby develop\",\n    \"start\": \"gatsby develop\",\n    \"build\": \"gatsby build\",\n    \"serve\": \"gatsby serve\",\n    \"clean\": \"gatsby clean\",\n    \"lint\": \"eslint .\",\n    \"lint:fix\": \"eslint . --fix\"\n  },\n  \"dependencies\": {\n    \"gatsby\": \"^4.6.1\",\n    \"gatsby-plugin-gatsby-cloud\": \"^4.7.0\",\n    \"gatsby-plugin-image\": \"^2.6.0\",\n    \"gatsby-plugin-postcss\": \"^5.6.0\",\n    \"gatsby-plugin-sharp\": \"^4.6.0\",\n    \"gatsby-source-strapi\": \"^2.0.0\",\n    \"gatsby-transformer-remark\": \"^5.6.0\",\n    \"gatsby-transformer-sharp\": \"^4.6.0\",\n    \"postcss\": \"^8.4.6\",\n    \"react\": \"^17.0.1\",\n    \"react-dom\": \"^17.0.1\",\n    \"react-helmet\": \"^6.1.0\",\n    \"react-slick\": \"^0.28.1\",\n    \"slick-carousel\": \"^1.8.1\"\n  },\n  \"devDependencies\": {\n    \"@tailwindcss/line-clamp\": \"^0.3.1\",\n    \"@tailwindcss/typography\": \"^0.5.1\",\n    \"autoprefixer\": \"^10.4.2\",\n    \"eslint\": \"^8.8.0\",\n    \"eslint-config-prettier\": \"^8.3.0\",\n    \"eslint-config-react-app\": \"^7.0.0\",\n    \"eslint-plugin-prettier\": \"^4.0.0\",\n    \"prettier\": \"^2.5.1\",\n    \"prettier-plugin-tailwindcss\": \"^0.1.4\",\n    \"tailwindcss\": \"^3.0.18\"\n  }\n}\n"
  },
  {
    "path": "packages/starters/gatsby-blog/starter/postcss.config.js",
    "content": "module.exports = {\n  plugins: {\n    tailwindcss: {},\n    autoprefixer: {},\n  },\n}\n"
  },
  {
    "path": "packages/starters/gatsby-blog/starter/src/components/article-card.js",
    "content": "import React from \"react\"\nimport { Link, graphql } from \"gatsby\"\nimport { GatsbyImage, getImage } from \"gatsby-plugin-image\"\n\nconst ArticleCard = ({ article }) => {\n  return (\n    <Link\n      to={`/article/${article.slug}`}\n      className=\"overflow-hidden rounded-lg bg-white shadow-sm transition-shadow hover:shadow-md\"\n    >\n      <GatsbyImage\n        image={getImage(article.cover?.localFile)}\n        alt={article.cover?.alternativeText}\n      />\n      <div className=\"px-4 py-4\">\n        <h3 className=\"font-bold text-neutral-700\">{article.title}</h3>\n        <p className=\"line-clamp-2 mt-2 text-neutral-500\">\n          {article.description}\n        </p>\n      </div>\n    </Link>\n  )\n}\n\nexport const query = graphql`\n  fragment ArticleCard on STRAPI_ARTICLE {\n    id\n    slug\n    title\n    description\n    cover {\n      alternativeText\n      localFile {\n        childImageSharp {\n          gatsbyImageData(aspectRatio: 1.77)\n        }\n      }\n    }\n  }\n`\n\nexport default ArticleCard\n"
  },
  {
    "path": "packages/starters/gatsby-blog/starter/src/components/articles-grid.js",
    "content": "import React from \"react\"\nimport ArticleCard from \"./article-card\"\n\nconst ArticlesGrid = ({ articles }) => {\n  return (\n    <div className=\"container mt-12 grid grid-cols-1 gap-6 md:grid-cols-2 lg:grid-cols-3\">\n      {articles.map((article) => (\n        <ArticleCard article={article} />\n      ))}\n    </div>\n  )\n}\n\nexport default ArticlesGrid\n"
  },
  {
    "path": "packages/starters/gatsby-blog/starter/src/components/block-media.js",
    "content": "import React from \"react\"\nimport { GatsbyImage, getImage } from \"gatsby-plugin-image\"\n\nconst BlockMedia = ({ data }) => {\n  const isVideo = data.file.mime.startsWith(\"video\")\n\n  return (\n    <div className=\"py-8\">\n      {isVideo ? (\n        <p>TODO video</p>\n      ) : (\n        <GatsbyImage\n          image={getImage(data.file.localFile)}\n          alt={data.file.alternativeText}\n        />\n      )}\n    </div>\n  )\n}\n\nexport default BlockMedia\n"
  },
  {
    "path": "packages/starters/gatsby-blog/starter/src/components/block-quote.js",
    "content": "import React from \"react\"\n\nconst BlockQuote = ({ data }) => {\n  return (\n    <div className=\"py-6\">\n      <blockquote className=\"container max-w-xl border-l-4 border-neutral-700 py-2 pl-6 text-neutral-700\">\n        <p className=\"text-5xl font-medium italic\">{data.quoteBody}</p>\n        <cite className=\"mt-4 block font-bold uppercase not-italic\">\n          {data.title}\n        </cite>\n      </blockquote>\n    </div>\n  )\n}\n\nexport default BlockQuote\n"
  },
  {
    "path": "packages/starters/gatsby-blog/starter/src/components/block-rich-text.js",
    "content": "import React from \"react\"\n\nconst BlockRichText = ({ data }) => {\n  return (\n    <div className=\"prose mx-auto py-8\">\n      <div\n        dangerouslySetInnerHTML={{\n          __html: data.richTextBody.data.childMarkdownRemark.html,\n        }}\n      />\n    </div>\n  )\n}\n\nexport default BlockRichText\n"
  },
  {
    "path": "packages/starters/gatsby-blog/starter/src/components/block-slider.js",
    "content": "import React from \"react\"\nimport { GatsbyImage, getImage } from \"gatsby-plugin-image\"\nimport Slider from \"react-slick\"\nimport \"slick-carousel/slick/slick.css\"\nimport \"slick-carousel/slick/slick-theme.css\"\n\nconst BlockSlider = ({ data }) => {\n  return (\n    <div className=\"container max-w-3xl py-8\">\n      <Slider\n        dots={true}\n        infinite={true}\n        speed={300}\n        slidesToShow={1}\n        slidesToScroll={1}\n        arrows={true}\n        swipe={true}\n      >\n        {data.files.map((file) => (\n          <GatsbyImage\n            key={file.id}\n            image={getImage(file.localFile)}\n            alt={file.alternativeText}\n          />\n        ))}\n      </Slider>\n    </div>\n  )\n}\n\nexport default BlockSlider\n"
  },
  {
    "path": "packages/starters/gatsby-blog/starter/src/components/blocks-renderer.js",
    "content": "import React from \"react\"\nimport { graphql } from \"gatsby\"\nimport BlockRichText from \"./block-rich-text\"\nimport BlockMedia from \"./block-media\"\nimport BlockQuote from \"./block-quote\"\nimport BlockSlider from \"./block-slider\"\n\nconst componentsMap = {\n  STRAPI__COMPONENT_SHARED_RICH_TEXT: BlockRichText,\n  STRAPI__COMPONENT_SHARED_MEDIA: BlockMedia,\n  STRAPI__COMPONENT_SHARED_QUOTE: BlockQuote,\n  STRAPI__COMPONENT_SHARED_SLIDER: BlockSlider,\n}\n\nconst Block = ({ block }) => {\n  const Component = componentsMap[block.__typename]\n\n  if (!Component) {\n    return null\n  }\n\n  return <Component data={block} />\n}\n\nconst BlocksRenderer = ({ blocks }) => {\n  return (\n    <div>\n      {blocks.map((block, index) => (\n        <Block key={`${index}${block.__typename}`} block={block} />\n      ))}\n    </div>\n  )\n}\n\nexport const query = graphql`\n  fragment Blocks on STRAPI__COMPONENT_SHARED_MEDIASTRAPI__COMPONENT_SHARED_QUOTESTRAPI__COMPONENT_SHARED_RICH_TEXTSTRAPI__COMPONENT_SHARED_SLIDERUnion {\n    __typename\n    ... on STRAPI__COMPONENT_SHARED_RICH_TEXT {\n      richTextBody: body {\n        __typename\n        data {\n          id\n          childMarkdownRemark {\n            html\n          }\n        }\n      }\n    }\n    ... on STRAPI__COMPONENT_SHARED_MEDIA {\n      file {\n        mime\n        localFile {\n          childImageSharp {\n            gatsbyImageData\n          }\n        }\n      }\n    }\n    ... on STRAPI__COMPONENT_SHARED_QUOTE {\n      title\n      quoteBody: body\n    }\n    ... on STRAPI__COMPONENT_SHARED_SLIDER {\n      files {\n        id\n        mime\n        localFile {\n          childImageSharp {\n            gatsbyImageData\n          }\n        }\n      }\n    }\n  }\n`\n\nexport default BlocksRenderer\n"
  },
  {
    "path": "packages/starters/gatsby-blog/starter/src/components/footer.js",
    "content": "import React from \"react\"\n\nconst Footer = () => {\n  const currentYear = new Date().getFullYear()\n\n  return (\n    <footer className=\"mt-16 bg-neutral-100 py-8 text-neutral-700\">\n      <div className=\"container\">\n        <p>Copyright {currentYear}</p>\n      </div>\n    </footer>\n  )\n}\n\nexport default Footer\n"
  },
  {
    "path": "packages/starters/gatsby-blog/starter/src/components/headings.js",
    "content": "import React from \"react\"\n\nconst Headings = ({ title, description }) => {\n  return (\n    <header className=\"container mt-8\">\n      <h1 className=\"text-6xl font-bold text-neutral-700\">{title}</h1>\n      {description && (\n        <p className=\"mt-4 text-2xl text-neutral-500\">{description}</p>\n      )}\n    </header>\n  )\n}\n\nexport default Headings\n"
  },
  {
    "path": "packages/starters/gatsby-blog/starter/src/components/layout.js",
    "content": "import React from \"react\"\nimport Footer from \"./footer\"\nimport Navbar from \"./navbar\"\n\nconst Layout = ({ children }) => {\n  return (\n    <div className=\"flex min-h-screen flex-col justify-between bg-neutral-50 text-neutral-900\">\n      <div>\n        <Navbar />\n        {children}\n      </div>\n      <Footer />\n    </div>\n  )\n}\n\nexport default Layout\n"
  },
  {
    "path": "packages/starters/gatsby-blog/starter/src/components/navbar.js",
    "content": "import { Link } from \"gatsby\"\nimport React from \"react\"\n\nconst Navbar = () => {\n  return (\n    <header className=\"bg-primary-200\">\n      <nav className=\"container flex flex-row items-baseline justify-between py-6\">\n        <Link to=\"/\" className=\"text-xl font-medium\">\n          Blog\n        </Link>\n        <div className=\"flex flex-row items-baseline justify-end\">\n          <Link className=\"font-medium\" to=\"/about\">\n            About\n          </Link>\n        </div>\n      </nav>\n    </header>\n  )\n}\n\nexport default Navbar\n"
  },
  {
    "path": "packages/starters/gatsby-blog/starter/src/components/seo.js",
    "content": "import React from \"react\"\nimport { Helmet } from \"react-helmet\"\nimport { useStaticQuery, graphql } from \"gatsby\"\n\nconst Seo = ({ seo = {} }) => {\n  const { strapiGlobal } = useStaticQuery(graphql`\n    query {\n      strapiGlobal {\n        siteName\n        favicon {\n          localFile {\n            url\n          }\n        }\n        defaultSeo {\n          metaTitle\n          metaDescription\n          shareImage {\n            localFile {\n              url\n            }\n          }\n        }\n      }\n    }\n  `)\n\n  const { siteName, defaultSeo, favicon } = strapiGlobal\n\n  // Merge default and page-specific SEO values\n  const fullSeo = { ...defaultSeo, ...seo }\n\n  // Add site name to title\n  fullSeo.metaTitle = `${fullSeo.metaTitle} | ${siteName}`\n\n  const getMetaTags = () => {\n    const tags = []\n\n    if (fullSeo.metaTitle) {\n      tags.push(\n        {\n          property: \"og:title\",\n          content: fullSeo.metaTitle,\n        },\n        {\n          name: \"twitter:title\",\n          content: fullSeo.metaTitle,\n        }\n      )\n    }\n    if (fullSeo.metaDescription) {\n      tags.push(\n        {\n          name: \"description\",\n          content: fullSeo.metaDescription,\n        },\n        {\n          property: \"og:description\",\n          content: fullSeo.metaDescription,\n        },\n        {\n          name: \"twitter:description\",\n          content: fullSeo.metaDescription,\n        }\n      )\n    }\n    if (fullSeo.shareImage) {\n      const imageUrl = fullSeo.shareImage.localFile.url\n      tags.push(\n        {\n          name: \"image\",\n          content: imageUrl,\n        },\n        {\n          property: \"og:image\",\n          content: imageUrl,\n        },\n        {\n          name: \"twitter:image\",\n          content: imageUrl,\n        }\n      )\n    }\n    if (fullSeo.article) {\n      tags.push({\n        property: \"og:type\",\n        content: \"article\",\n      })\n    }\n    tags.push({ name: \"twitter:card\", content: \"summary_large_image\" })\n\n    return tags\n  }\n\n  const metaTags = getMetaTags()\n\n  return (\n    <Helmet\n      title={fullSeo.metaTitle}\n      link={[\n        {\n          rel: \"icon\",\n          href: favicon.localFile.url,\n        },\n      ]}\n      meta={metaTags}\n    />\n  )\n}\n\nexport default Seo\n"
  },
  {
    "path": "packages/starters/gatsby-blog/starter/src/pages/about.js",
    "content": "import React from \"react\"\nimport { useStaticQuery, graphql } from \"gatsby\"\nimport Layout from \"../components/layout\"\nimport Seo from \"../components/seo\"\nimport BlocksRenderer from \"../components/blocks-renderer\"\nimport Headings from \"../components/headings\"\n\nconst AboutPage = () => {\n  const { strapiAbout } = useStaticQuery(graphql`\n    query {\n      strapiAbout {\n        title\n        blocks {\n          ...Blocks\n        }\n      }\n    }\n  `)\n  const { title, blocks } = strapiAbout\n\n  const seo = {\n    metaTitle: title,\n    metaDescription: title,\n  }\n\n  return (\n    <Layout>\n      <Seo seo={seo} />\n      <Headings title={strapiAbout.title} />\n      <BlocksRenderer blocks={blocks} />\n    </Layout>\n  )\n}\n\nexport default AboutPage\n"
  },
  {
    "path": "packages/starters/gatsby-blog/starter/src/pages/index.js",
    "content": "import React from \"react\"\nimport { useStaticQuery, graphql } from \"gatsby\"\nimport Layout from \"../components/layout\"\nimport ArticlesGrid from \"../components/articles-grid\"\nimport Seo from \"../components/seo\"\nimport Headings from \"../components/headings\"\n\nconst IndexPage = () => {\n  const { allStrapiArticle, strapiGlobal } = useStaticQuery(graphql`\n    query {\n      allStrapiArticle {\n        nodes {\n          ...ArticleCard\n        }\n      }\n      strapiGlobal {\n        siteName\n        siteDescription\n      }\n    }\n  `)\n\n  return (\n    <Layout>\n      <Seo seo={{ metaTitle: \"Home\" }} />\n      <Headings\n        title={strapiGlobal.siteName}\n        description={strapiGlobal.siteDescription}\n      />\n      <main>\n        <ArticlesGrid articles={allStrapiArticle.nodes} />\n      </main>\n    </Layout>\n  )\n}\n\nexport default IndexPage\n"
  },
  {
    "path": "packages/starters/gatsby-blog/starter/src/styles/global.css",
    "content": "@tailwind base;\n@tailwind components;\n@tailwind utilities;\n"
  },
  {
    "path": "packages/starters/gatsby-blog/starter/src/templates/article-post.js",
    "content": "import React from \"react\"\nimport { graphql } from \"gatsby\"\nimport { GatsbyImage, getImage } from \"gatsby-plugin-image\"\nimport Layout from \"../components/layout\"\nimport BlocksRenderer from \"../components/blocks-renderer\"\nimport Seo from \"../components/seo\"\n\nconst ArticlePage = ({ data }) => {\n  const article = data.strapiArticle\n\n  const seo = {\n    metaTitle: article.title,\n    metaDescription: article.description,\n    shareImage: article.cover,\n  }\n\n  return (\n    <Layout as=\"article\">\n      <Seo seo={seo} />\n      <header className=\"container max-w-4xl py-8\">\n        <h1 className=\"text-6xl font-bold text-neutral-700\">{article.title}</h1>\n        <p className=\"mt-4 text-2xl text-neutral-500\">{article.description}</p>\n        <GatsbyImage\n          image={getImage(article?.cover?.localFile)}\n          alt={article?.cover?.alternativeText}\n          className=\"mt-6\"\n        />\n      </header>\n      <main className=\"mt-8\">\n        <BlocksRenderer blocks={article.blocks || []} />\n      </main>\n    </Layout>\n  )\n}\n\nexport const pageQuery = graphql`\n  query ($slug: String) {\n    strapiArticle(slug: { eq: $slug }) {\n      id\n      slug\n      title\n      description\n      blocks {\n        ...Blocks\n      }\n      cover {\n        alternativeText\n        localFile {\n          url\n          childImageSharp {\n            gatsbyImageData\n          }\n        }\n      }\n    }\n  }\n`\n\nexport default ArticlePage\n"
  },
  {
    "path": "packages/starters/gatsby-blog/starter/tailwind.config.js",
    "content": "const colors = require(\"tailwindcss/colors\")\n\nmodule.exports = {\n  content: [\"./src/**/*.{js,jsx,ts,tsx}\"],\n  theme: {\n    extend: {\n      colors: {\n        neutral: colors.neutral,\n        primary: colors.sky,\n      },\n    },\n    container: {\n      center: true,\n      padding: {\n        DEFAULT: \"1rem\",\n        xs: \"1rem\",\n        sm: \"2rem\",\n        xl: \"5rem\",\n        \"2xl\": \"6rem\",\n      },\n    },\n  },\n  plugins: [\n    require(\"@tailwindcss/line-clamp\"),\n    require(\"@tailwindcss/typography\"),\n  ],\n}\n"
  },
  {
    "path": "packages/starters/gatsby-blog/starter.json",
    "content": "{\n  \"template\": {\n    \"name\": \"@strapi/template-blog\",\n    \"version\": \"^2.0.0\"\n  }\n}\n"
  },
  {
    "path": "packages/starters/gatsby-corporate/README.md",
    "content": "# Placeholder for Strapi Gatsby corporate starter\n\nThe Strapi team will build this starter soon.\n\nIn the meantime, you can check out our other Strapi v4 starters:\n\n- [Gatsby Blog](https://github.com/strapi/starters-and-templates/tree/main/packages/starters/gatsby-blog)\n- [Next Blog](https://github.com/strapi/starters-and-templates/tree/main/packages/starters/next-blog)\n- [Next Corporate Site](https://github.com/strapi/starters-and-templates/tree/main/packages/starters/next-corporate)\n"
  },
  {
    "path": "packages/starters/next-blog/README.md",
    "content": "# Strapi Starter Next Blog\n\nNext starter for creating a blog with Strapi.\n\n![screenshot image](./screenshot.png)\n\nThis starter allows you to try Strapi with Next with the example of a simple blog. It is fully customizable and due to the fact that it is open source, fully open to contributions. So do not hesitate to add new features and report bugs!\n\nThis starter uses the [Strapi blog template](https://github.com/strapi/strapi-template-blog)\n\nCheck out all of our starters [here](https://strapi.io/starters)\n\n## Features\n\n- 2 Content types: Article, Category\n- 2 Created articles\n- 3 Created categories\n- Responsive design using UIkit\n\nPages:\n\n- \"/\" to display every articles\n- \"/article/:id\" to display one article\n- \"/category/:id\" display articles depending on the category\n\n## Getting started\n\nUse our `create-strapi-starter` CLI to create your project.\n\n```sh\n# Using Yarn\nyarn create strapi-starter my-project next-blog\n\n# Or using NPM\nnpx create-strapi-starter my-project next-blog\n```\n\nThe CLI will create a monorepo, install dependencies, and run your project automatically.\n\nThe Next frontend server will run here => [http://localhost:3000](http://localhost:3000)\n\nThe Strapi backend server will run here => [http://localhost:1337](http://localhost:1337)\n\n## Deploying to production\n\nYou will need to deploy the `frontend` and `backend` projects separately. Here are the docs to deploy each one:\n\n- [Deploy Strapi](https://strapi.io/documentation/developer-docs/latest/setup-deployment-guides/deployment.html#hosting-provider-guides)\n- [Deploy Next](https://nextjs.org/docs/deployment)\n\nDon't forget to setup the environment variables on your production app:\n\nFor the frontend the following environment variable is required:\n\n- `NEXT_PUBLIC_STRAPI_API_URL`: URL of your Strapi backend, without trailing slash\n\nEnjoy this starter!\n"
  },
  {
    "path": "packages/starters/next-blog/package.json",
    "content": "{\n  \"name\": \"@strapi/starter-next-blog\",\n  \"version\": \"1.0.4\",\n  \"description\": \"Strapi blog starter with Next.js\",\n  \"keywords\": [\n    \"strapi\",\n    \"starter\",\n    \"nextjs\",\n    \"blog\"\n  ],\n  \"homepage\": \"https://github.com/strapi/starters-and-templates/tree/main/packages/starters/next-blog#readme\",\n  \"bugs\": {\n    \"url\": \"https://github.com/strapi/starters-and-templates/issues\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/strapi/starters-and-templates.git\"\n  },\n  \"license\": \"MIT\",\n  \"author\": {\n    \"name\": \"Strapi team\",\n    \"email\": \"hi@strapi.io\",\n    \"url\": \"https://strapi.io\"\n  },\n  \"maintainers\": [\n    {\n      \"name\": \"Strapi team\",\n      \"email\": \"hi@strapi.io\",\n      \"url\": \"https://strapi.io\"\n    }\n  ]\n}\n"
  },
  {
    "path": "packages/starters/next-blog/starter/.eslintrc",
    "content": "{\n  \"extends\": [\n    \"next\",\n    \"prettier\"\n  ],\n  \"plugins\": [\n    \"prettier\"\n  ],\n  \"rules\": {\n    \"prettier/prettier\": \"error\"\n  }\n}\n"
  },
  {
    "path": "packages/starters/next-blog/starter/.prettierrc",
    "content": "{\n  \"printWidth\": 80,\n  \"singleQuote\": false,\n  \"trailingComma\": \"es5\",\n  \"semi\": false,\n  \"tabWidth\": 2\n}"
  },
  {
    "path": "packages/starters/next-blog/starter/assets/css/style.css",
    "content": "a {\n  text-decoration: none;\n}\n\nh1 {\n  font-family: Staatliches;\n  font-size: 120px;\n}\n\n#category {\n  font-family: Staatliches;\n  font-weight: 500;\n}\n\n#title {\n  letter-spacing: 0.4px;\n  font-size: 22px;\n  font-size: 1.375rem;\n  line-height: 1.13636;\n}\n\n#banner {\n  margin: 20px;\n  height: 800px;\n}\n\n#editor {\n  font-size: 16px;\n  font-size: 1rem;\n  line-height: 1.75;\n}\n\n.uk-navbar-container {\n  background: #fff !important;\n  font-family: Staatliches;\n}\n\nimg:hover {\n  opacity: 1;\n  transition: opacity 0.25s cubic-bezier(0.39, 0.575, 0.565, 1);\n}\n"
  },
  {
    "path": "packages/starters/next-blog/starter/components/articles.js",
    "content": "import React from \"react\"\nimport Card from \"./card\"\n\nconst Articles = ({ articles }) => {\n  const leftArticlesCount = Math.ceil(articles.length / 5)\n  const leftArticles = articles.slice(0, leftArticlesCount)\n  const rightArticles = articles.slice(leftArticlesCount, articles.length)\n\n  return (\n    <div>\n      <div className=\"uk-child-width-1-2@s\" data-uk-grid=\"true\">\n        <div>\n          {leftArticles.map((article, i) => {\n            return (\n              <Card\n                article={article}\n                key={`article__left__${article.attributes.slug}`}\n              />\n            )\n          })}\n        </div>\n        <div>\n          <div className=\"uk-child-width-1-2@m uk-grid-match\" data-uk-grid>\n            {rightArticles.map((article, i) => {\n              return (\n                <Card\n                  article={article}\n                  key={`article__left__${article.attributes.slug}`}\n                />\n              )\n            })}\n          </div>\n        </div>\n      </div>\n    </div>\n  )\n}\n\nexport default Articles\n"
  },
  {
    "path": "packages/starters/next-blog/starter/components/card.js",
    "content": "import React from \"react\"\nimport Link from \"next/link\"\nimport NextImage from \"./image\"\n\nconst Card = ({ article }) => {\n  return (\n    <Link href={`/article/${article.attributes.slug}`}>\n      <a className=\"uk-link-reset\">\n        <div className=\"uk-card uk-card-muted\">\n          <div className=\"uk-card-media-top\">\n            <NextImage image={article.attributes.image} />\n          </div>\n          <div className=\"uk-card-body\">\n            <p id=\"category\" className=\"uk-text-uppercase\">\n              {article.attributes.category.name}\n            </p>\n            <p id=\"title\" className=\"uk-text-large\">\n              {article.attributes.title}\n            </p>\n          </div>\n        </div>\n      </a>\n    </Link>\n  )\n}\n\nexport default Card\n"
  },
  {
    "path": "packages/starters/next-blog/starter/components/image.js",
    "content": "import { getStrapiMedia } from \"../lib/media\"\nimport NextImage from \"next/image\"\n\nconst Image = ({ image, style }) => {\n  const { url, alternativeText, width, height } = image.data.attributes\n\n  // const loader = () => {\n  //   return getStrapiMedia(image)\n  // }\n\n  return (\n    <NextImage\n      // loader={loader}\n      layout=\"responsive\"\n      width={width || \"100%\"}\n      height={height || \"100%\"}\n      objectFit=\"contain\"\n      src={getStrapiMedia(image)}\n      alt={alternativeText || \"\"}\n    />\n  )\n}\n\nexport default Image\n"
  },
  {
    "path": "packages/starters/next-blog/starter/components/layout.js",
    "content": "import Nav from \"./nav\"\n\nconst Layout = ({ children, categories, seo }) => (\n  <>\n    <Nav categories={categories} />\n    {children}\n  </>\n)\n\nexport default Layout\n"
  },
  {
    "path": "packages/starters/next-blog/starter/components/nav.js",
    "content": "import React from \"react\"\nimport Link from \"next/link\"\n\nconst Nav = ({ categories }) => {\n  return (\n    <div>\n      <nav className=\"uk-navbar-container\" data-uk-navbar>\n        <div className=\"uk-navbar-left\">\n          <ul className=\"uk-navbar-nav\">\n            <li>\n              <Link href=\"/\">\n                <a>Strapi Blog</a>\n              </Link>\n            </li>\n          </ul>\n        </div>\n        <div className=\"uk-navbar-right\">\n          <ul className=\"uk-navbar-nav\">\n            {categories.map((category) => {\n              return (\n                <li key={category.id}>\n                  <Link href={`/category/${category.attributes.slug}`}>\n                    <a className=\"uk-link-reset\">{category.attributes.name}</a>\n                  </Link>\n                </li>\n              )\n            })}\n          </ul>\n        </div>\n      </nav>\n    </div>\n  )\n}\n\nexport default Nav\n"
  },
  {
    "path": "packages/starters/next-blog/starter/components/seo.js",
    "content": "import Head from \"next/head\"\nimport { useContext } from \"react\"\nimport { GlobalContext } from \"../pages/_app\"\nimport { getStrapiMedia } from \"../lib/media\"\n\nconst Seo = ({ seo }) => {\n  const { defaultSeo, siteName } = useContext(GlobalContext)\n  const seoWithDefaults = {\n    ...defaultSeo,\n    ...seo,\n  }\n  const fullSeo = {\n    ...seoWithDefaults,\n    // Add title suffix\n    metaTitle: `${seoWithDefaults.metaTitle} | ${siteName}`,\n    // Get full image URL\n    shareImage: getStrapiMedia(seoWithDefaults.shareImage),\n  }\n\n  return (\n    <Head>\n      {fullSeo.metaTitle && (\n        <>\n          <title>{fullSeo.metaTitle}</title>\n          <meta property=\"og:title\" content={fullSeo.metaTitle} />\n          <meta name=\"twitter:title\" content={fullSeo.metaTitle} />\n        </>\n      )}\n      {fullSeo.metaDescription && (\n        <>\n          <meta name=\"description\" content={fullSeo.metaDescription} />\n          <meta property=\"og:description\" content={fullSeo.metaDescription} />\n          <meta name=\"twitter:description\" content={fullSeo.metaDescription} />\n        </>\n      )}\n      {fullSeo.shareImage && (\n        <>\n          <meta property=\"og:image\" content={fullSeo.shareImage} />\n          <meta name=\"twitter:image\" content={fullSeo.shareImage} />\n          <meta name=\"image\" content={fullSeo.shareImage} />\n        </>\n      )}\n      {fullSeo.article && <meta property=\"og:type\" content=\"article\" />}\n      <meta name=\"twitter:card\" content=\"summary_large_image\" />\n    </Head>\n  )\n}\n\nexport default Seo\n"
  },
  {
    "path": "packages/starters/next-blog/starter/lib/api.js",
    "content": "import qs from \"qs\"\n\n/**\n * Get full Strapi URL from path\n * @param {string} path Path of the URL\n * @returns {string} Full Strapi URL\n */\nexport function getStrapiURL(path = \"\") {\n  return `${\n    process.env.NEXT_PUBLIC_STRAPI_API_URL || \"http://localhost:1337\"\n  }${path}`\n}\n\n/**\n * Helper to make GET requests to Strapi API endpoints\n * @param {string} path Path of the API route\n * @param {Object} urlParamsObject URL params object, will be stringified\n * @param {Object} options Options passed to fetch\n * @returns Parsed API call response\n */\nexport async function fetchAPI(path, urlParamsObject = {}, options = {}) {\n  // Merge default and user options\n  const mergedOptions = {\n    headers: {\n      \"Content-Type\": \"application/json\",\n    },\n    ...options,\n  }\n\n  // Build request URL\n  const queryString = qs.stringify(urlParamsObject)\n  const requestUrl = `${getStrapiURL(\n    `/api${path}${queryString ? `?${queryString}` : \"\"}`\n  )}`\n\n  // Trigger API call\n  const response = await fetch(requestUrl, mergedOptions)\n\n  // Handle response\n  if (!response.ok) {\n    console.error(response.statusText)\n    throw new Error(`An error occurred please try again`)\n  }\n  const data = await response.json()\n  return data\n}\n"
  },
  {
    "path": "packages/starters/next-blog/starter/lib/media.js",
    "content": "import { getStrapiURL } from \"./api\"\n\nexport function getStrapiMedia(media) {\n  const { url } = media.data.attributes\n  const imageUrl = url.startsWith(\"/\") ? getStrapiURL(url) : url\n  return imageUrl\n}\n"
  },
  {
    "path": "packages/starters/next-blog/starter/next.config.js",
    "content": "/**\n * @type {import('next').NextConfig}\n */\nconst nextConfig = {\n  images: {\n    loader: \"default\",\n    domains: [\"localhost\"],\n  },\n}\n\nmodule.exports = nextConfig\n"
  },
  {
    "path": "packages/starters/next-blog/starter/package.json",
    "content": "{\n  \"name\": \"my-next-blog\",\n  \"version\": \"1.0.3\",\n  \"private\": true,\n  \"scripts\": {\n    \"develop\": \"next dev\",\n    \"dev\": \"next dev\",\n    \"build\": \"next build\",\n    \"start\": \"next start\",\n    \"deploy\": \"next build && next export\",\n    \"lint\": \"next lint\",\n    \"lint:fix\": \"next lint --fix\"\n  },\n  \"dependencies\": {\n    \"moment\": \"^2.24.0\",\n    \"next\": \"^11.0.0\",\n    \"qs\": \"^6.10.1\",\n    \"react\": \"17.0.0\",\n    \"react-dom\": \"17.0.0\",\n    \"react-markdown\": \"^4.2.2\",\n    \"react-moment\": \"^0.9.6\"\n  },\n  \"license\": \"MIT\",\n  \"devDependencies\": {\n    \"eslint\": \"^7.30.0\",\n    \"eslint-config-next\": \"^11.0.1\",\n    \"eslint-config-prettier\": \"^8.3.0\",\n    \"eslint-plugin-prettier\": \"^3.4.0\",\n    \"prettier\": \"^2.3.1\"\n  }\n}\n"
  },
  {
    "path": "packages/starters/next-blog/starter/pages/_app.js",
    "content": "import App from \"next/app\"\nimport Head from \"next/head\"\nimport \"../assets/css/style.css\"\nimport { createContext } from \"react\"\nimport { fetchAPI } from \"../lib/api\"\nimport { getStrapiMedia } from \"../lib/media\"\n\n// Store Strapi Global object in context\nexport const GlobalContext = createContext({})\n\nconst MyApp = ({ Component, pageProps }) => {\n  const { global } = pageProps\n\n  return (\n    <>\n      <Head>\n        <link\n          rel=\"shortcut icon\"\n          href={getStrapiMedia(global.attributes.favicon)}\n        />\n      </Head>\n      <GlobalContext.Provider value={global.attributes}>\n        <Component {...pageProps} />\n      </GlobalContext.Provider>\n    </>\n  )\n}\n\n// getInitialProps disables automatic static optimization for pages that don't\n// have getStaticProps. So article, category and home pages still get SSG.\n// Hopefully we can replace this with getStaticProps once this issue is fixed:\n// https://github.com/vercel/next.js/discussions/10949\nMyApp.getInitialProps = async (ctx) => {\n  // Calls page's `getInitialProps` and fills `appProps.pageProps`\n  const appProps = await App.getInitialProps(ctx)\n  // Fetch global site settings from Strapi\n  const globalRes = await fetchAPI(\"/global\", {\n    populate: {\n      favicon: \"*\",\n      defaultSeo: {\n        populate: \"*\",\n      },\n    },\n  })\n  // Pass the data to our page via props\n  return { ...appProps, pageProps: { global: globalRes.data } }\n}\n\nexport default MyApp\n"
  },
  {
    "path": "packages/starters/next-blog/starter/pages/_document.js",
    "content": "import Document, { Html, Head, Main, NextScript } from \"next/document\"\n\nclass MyDocument extends Document {\n  render() {\n    return (\n      <Html>\n        <Head>\n          {/* eslint-disable-next-line */}\n          <link\n            rel=\"stylesheet\"\n            href=\"https://fonts.googleapis.com/css?family=Staatliches\"\n          />\n          <link\n            rel=\"stylesheet\"\n            href=\"https://cdn.jsdelivr.net/npm/uikit@3.2.3/dist/css/uikit.min.css\"\n          />\n          <script\n            async\n            src=\"https://cdnjs.cloudflare.com/ajax/libs/uikit/3.2.0/js/uikit.min.js\"\n          />\n          <script\n            async\n            src=\"https://cdn.jsdelivr.net/npm/uikit@3.2.3/dist/js/uikit-icons.min.js\"\n          />\n          <script\n            async\n            src=\"https://cdnjs.cloudflare.com/ajax/libs/uikit/3.2.0/js/uikit.js\"\n          />\n        </Head>\n        <body>\n          <Main />\n          <NextScript />\n        </body>\n      </Html>\n    )\n  }\n}\n\nexport default MyDocument\n"
  },
  {
    "path": "packages/starters/next-blog/starter/pages/article/[slug].js",
    "content": "import ReactMarkdown from \"react-markdown\"\nimport Moment from \"react-moment\"\nimport { fetchAPI } from \"../../lib/api\"\nimport Layout from \"../../components/layout\"\nimport NextImage from \"../../components/image\"\nimport Seo from \"../../components/seo\"\nimport { getStrapiMedia } from \"../../lib/media\"\n\nconst Article = ({ article, categories }) => {\n  const imageUrl = getStrapiMedia(article.attributes.image)\n\n  const seo = {\n    metaTitle: article.attributes.title,\n    metaDescription: article.attributes.description,\n    shareImage: article.attributes.image,\n    article: true,\n  }\n\n  return (\n    <Layout categories={categories.data}>\n      <Seo seo={seo} />\n      <div\n        id=\"banner\"\n        className=\"uk-height-medium uk-flex uk-flex-center uk-flex-middle uk-background-cover uk-light uk-padding uk-margin\"\n        data-src={imageUrl}\n        data-srcset={imageUrl}\n        data-uk-img\n      >\n        <h1>{article.attributes.title}</h1>\n      </div>\n      <div className=\"uk-section\">\n        <div className=\"uk-container uk-container-small\">\n          <ReactMarkdown\n            source={article.attributes.content}\n            escapeHtml={false}\n          />\n          <hr className=\"uk-divider-small\" />\n          <div className=\"uk-grid-small uk-flex-left\" data-uk-grid=\"true\">\n            <div>\n              {article.attributes.author.picture && (\n                <NextImage image={article.attributes.author.picture} />\n              )}\n            </div>\n            <div className=\"uk-width-expand\">\n              <p className=\"uk-margin-remove-bottom\">\n                By {article.attributes.author.name}\n              </p>\n              <p className=\"uk-text-meta uk-margin-remove-top\">\n                <Moment format=\"MMM Do YYYY\">\n                  {article.attributes.published_at}\n                </Moment>\n              </p>\n            </div>\n          </div>\n        </div>\n      </div>\n    </Layout>\n  )\n}\n\nexport async function getStaticPaths() {\n  const articlesRes = await fetchAPI(\"/articles\", { fields: [\"slug\"] })\n\n  return {\n    paths: articlesRes.data.map((article) => ({\n      params: {\n        slug: article.attributes.slug,\n      },\n    })),\n    fallback: false,\n  }\n}\n\nexport async function getStaticProps({ params }) {\n  const articlesRes = await fetchAPI(\"/articles\", {\n    filters: {\n      slug: params.slug,\n    },\n    populate: \"*\",\n  })\n  const categoriesRes = await fetchAPI(\"/categories\")\n\n  return {\n    props: { article: articlesRes.data[0], categories: categoriesRes },\n    revalidate: 1,\n  }\n}\n\nexport default Article\n"
  },
  {
    "path": "packages/starters/next-blog/starter/pages/category/[slug].js",
    "content": "import Articles from \"../../components/articles\"\nimport { fetchAPI } from \"../../lib/api\"\nimport Layout from \"../../components/layout\"\nimport Seo from \"../../components/seo\"\n\nconst Category = ({ category, categories }) => {\n  const seo = {\n    metaTitle: category.attributes.name,\n    metaDescription: `All ${category.attributes.name} articles`,\n  }\n\n  return (\n    <Layout categories={categories.data}>\n      <Seo seo={seo} />\n      <div className=\"uk-section\">\n        <div className=\"uk-container uk-container-large\">\n          <h1>{category.attributes.name}</h1>\n          <Articles articles={category.attributes.articles.data} />\n        </div>\n      </div>\n    </Layout>\n  )\n}\n\nexport async function getStaticPaths() {\n  const categoriesRes = await fetchAPI(\"/categories\", { fields: [\"slug\"] })\n\n  return {\n    paths: categoriesRes.data.map((category) => ({\n      params: {\n        slug: category.attributes.slug,\n      },\n    })),\n    fallback: false,\n  }\n}\n\nexport async function getStaticProps({ params }) {\n  const matchingCategories = await fetchAPI(\"/categories\", {\n    filters: { slug: params.slug },\n    populate: {\n      articles: {\n        populate: \"*\",\n      },\n    },\n  })\n  const allCategories = await fetchAPI(\"/categories\")\n\n  return {\n    props: {\n      category: matchingCategories.data[0],\n      categories: allCategories,\n    },\n    revalidate: 1,\n  }\n}\n\nexport default Category\n"
  },
  {
    "path": "packages/starters/next-blog/starter/pages/index.js",
    "content": "import React from \"react\"\nimport Articles from \"../components/articles\"\nimport Layout from \"../components/layout\"\nimport Seo from \"../components/seo\"\nimport { fetchAPI } from \"../lib/api\"\n\nconst Home = ({ articles, categories, homepage }) => {\n  return (\n    <Layout categories={categories}>\n      <Seo seo={homepage.attributes.seo} />\n      <div className=\"uk-section\">\n        <div className=\"uk-container uk-container-large\">\n          <h1>{homepage.attributes.hero.title}</h1>\n          <Articles articles={articles} />\n        </div>\n      </div>\n    </Layout>\n  )\n}\n\nexport async function getStaticProps() {\n  // Run API calls in parallel\n  const [articlesRes, categoriesRes, homepageRes] = await Promise.all([\n    fetchAPI(\"/articles\", { populate: \"*\" }),\n    fetchAPI(\"/categories\", { populate: \"*\" }),\n    fetchAPI(\"/homepage\", {\n      populate: {\n        hero: \"*\",\n        seo: { populate: \"*\" },\n      },\n    }),\n  ])\n\n  return {\n    props: {\n      articles: articlesRes.data,\n      categories: categoriesRes.data,\n      homepage: homepageRes.data,\n    },\n    revalidate: 1,\n  }\n}\n\nexport default Home\n"
  },
  {
    "path": "packages/starters/next-blog/starter.json",
    "content": "{\n  \"template\": {\n    \"name\": \"@strapi/template-blog\",\n    \"version\": \"^1.0.0\"\n  }\n}\n"
  },
  {
    "path": "packages/starters/next-corporate/README.md",
    "content": "# Strapi Starter Next Corporate Site\n\nNext starter for creating a corporate site with Strapi.\n\n[View the live demo](https://strapi-starter-next-corporate.vercel.app/) • [Read the blog post](https://strapi.io/blog/strapi-starter-next-corporate-site)\n\n![screen-website](./screenshot.png)\n\nThis starter is designed for flexibility. Using it, you'll be able to manage your website content entirely in Strapi, and get a Next app automatically generated. Marketing teams will be able to create pages and design their layout without help from developers.\n\nThis starter features:\n\n- Pages creation within Strapi, no code necessary\n- Fully flexible page structure: design the pages you want using UI Sections\n- 8 UI Sections out of the box: Hero, RichText, LargeVideo, Testimonials, Pricing, BottomActions, FeatureRows, FeatureColumns\n- Easy to theme with Tailwind\n- Static site generation with Next\n- An integrated Preview Mode, to view your pages on a private URL before publishing them\n- Content in multiple languages using i18n\n\nThis starter uses the [Strapi corporate template](https://github.com/strapi/strapi-template-corporate)\n\nCheck out all of our starters [here](https://strapi.io/starters)\n\n## Getting started\n\nUse our `create-strapi-starter` CLI to create your project.\n\n```sh\n# Using Yarn\nyarn create strapi-starter my-site next-corporate\n\n# Or using NPM\nnpx create-strapi-starter my-site next-corporate\n```\n\nThe CLI will create a monorepo, install dependencies, and run your project automatically.\n\nThe Next frontend server will run here => [http://localhost:3000](http://localhost:3000)\n\nThe Strapi backend server will run here => [http://localhost:1337](http://localhost:1337)\n\n## Preview Mode\n\nYou can turn preview mode on with a URL like this:\n\n`http://localhost:3000/api/preview?secret=<preview-secret>&slug=<slug>`\n\n`<preview-secret>` is the secret token defined in your .env config,\n`<slug>` is the slug you entered in Strapi for your page.\n\nWhile preview mode is on you can access `draft` pages just like you would `published` pages.\n\nFor example [http://localhost:3000/secret](http://localhost:3000/secret) would be available in preview mode.\n\nA banner will remain under the navigation to let you know preview mode is on and it will also allow you to turn it off.\n\n## Customize your corporate site\n\nTo edit this website, you'll need to run both the frontend and the backend in your development environment.\n\n### Adding Sections\n\nWe have built sections for you, but you will likely want to add more to fit your needs. Follow these steps:\n\n- Create a new component in Strapi in the \"sections\" category\n- In the Content-Types Builder, open the Pages collection and check your new section on the `contentSections` field.\n- Create a React component that takes a `data` prop in `/frontend/components/sections`\n- To link your Strapi section to this React component, open `/frontend/components/sections.js`, and add an entry to the `sectionComponents` object.\n\n### Custom theme\n\nWe use Tailwind CSS for styling. To modify your page's look, you can edit the theme in `/front/tailwind.config.js`. Read the [Tailwind docs](https://v1.tailwindcss.com/docs/theme) to view all the changes you can make. For example, you can change the primary color like this:\n\n```js\nconst { colors } = require(`tailwindcss/defaultTheme`);\n\nmodule.exports = {\n  theme: {\n    extend: {\n      colors: {\n        primary: colors.green,\n      },\n    },\n  },\n};\n```\n\n## Deploying to production\n\nYou will need to deploy the `frontend` and `backend` projects separately. Here are the docs to deploy each one:\n\n- [Deploy Strapi](https://strapi.io/documentation/v3.x/admin-panel/deploy.html#deployment)\n- [Deploy Next.js](https://nextjs.org/docs/deployment)\n\nDon't forget to set up your environment variables on your production apps.\n\nHere are the required ones for the frontend:\n\n- `NEXT_PUBLIC_STRAPI_API_URL`: URL of your Strapi backend, without trailing slash\n- `PREVIEW_SECRET`: a random string used to protect your preview pages\n\nAnd for the backend:\n\n- `FRONTEND_URL`: URL of your frontend, without trailing slash\n- `FRONTEND_PREVIEW_SECRET`: token of Next.js preview mode defined on the frontend\n\nHave fun using this starter!\n"
  },
  {
    "path": "packages/starters/next-corporate/package.json",
    "content": "{\n  \"name\": \"@strapi/starter-next-corporate\",\n  \"version\": \"1.0.4\",\n  \"description\": \"Strapi corporate starter with Next.js\",\n  \"keywords\": [\n    \"strapi\",\n    \"starter\",\n    \"nextjs\",\n    \"corporate\"\n  ],\n  \"homepage\": \"https://github.com/strapi/starters-and-templates/tree/main/packages/starters/next-corporate#readme\",\n  \"bugs\": {\n    \"url\": \"https://github.com/strapi/starters-and-templates/issues\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/strapi/starters-and-templates.git\"\n  },\n  \"license\": \"MIT\",\n  \"author\": {\n    \"name\": \"Strapi team\",\n    \"email\": \"hi@strapi.io\",\n    \"url\": \"https://strapi.io\"\n  },\n  \"maintainers\": [\n    {\n      \"name\": \"Strapi team\",\n      \"email\": \"hi@strapi.io\",\n      \"url\": \"https://strapi.io\"\n    }\n  ]\n}\n"
  },
  {
    "path": "packages/starters/next-corporate/starter/.eslintrc",
    "content": "{\n  \"extends\": [\n    \"next\",\n    \"prettier\"\n  ],\n  \"plugins\": [\n    \"prettier\"\n  ],\n  \"rules\": {\n    \"prettier/prettier\": [\n      \"error\",\n      {\n        \"printWidth\": 80,\n        \"singleQuote\": false,\n        \"trailingComma\": \"es5\",\n        \"semi\": false,\n        \"tabWidth\": 2\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "packages/starters/next-corporate/starter/.gitignore",
    "content": ".vscode\n\n# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.\n\n# dependencies\n/node_modules\n/.pnp\n.pnp.js\n\n# testing\n/coverage\n\n# next.js\n/.next/\n/out/\n\n# production\n/build\n\n# misc\n.DS_Store\n\n# debug\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n\n# local env files\n.env.local\n.env.development.local\n.env.test.local\n.env.production.local\n"
  },
  {
    "path": "packages/starters/next-corporate/starter/README.md",
    "content": "# Next frontend\n\nThis frontend relies on Next's [Static Generation](https://nextjs.org/docs/basic-features/pages) using [Strapi](https://strapi.io/) as the data source. Make sure Strapi is running in parallel when you run this app.\n\n## Routes\n\n**pages/[[...slug]].js**\n\nThis file generates all the app's route. First, it fetches all the pages entries in Strapi. Then, it creates one route per page found. These routes can look like this:\n\n* yoursite.com\n* yoursite.com/page\n* yoursite.com/page/nested/route\n\nNotice that the path of the page can be several layers deep, or it can be the root of the site. This is possible thanks to Next's [optional catch-all routes](https://nextjs.org/docs/routing/dynamic-routes#optional-catch-all-routes).\n\nTo see how to build these nested routes, see [the Strapi project's Readme](../backend/README.md).\n\n## Available Scripts\n\nIn the project directory, you can run:\n\n**`yarn dev`**\n\nRuns the app in the development mode.  \nOpen [http://localhost:3000](http://localhost:3000) to view it in the browser.\n\nThe page will reload if you make edits.  \nYou will also see any errors in the console.\n\n**`yarn build`**\n\nBuilds the app for production to the `.next` folder.<br>\nIt correctly bundles React in production mode and optimizes the build for the best performance.\n\n**`yarn start`**\n\nStarts the application in production mode.\nThe application should be compiled with \\`next build\\` first.\n\nSee the section in Next docs about [deployment](https://nextjs.org/docs/deployment) for more\ninformation.\n"
  },
  {
    "path": "packages/starters/next-corporate/starter/components/elements/button-link.js",
    "content": "import classNames from \"classnames\"\nimport PropTypes from \"prop-types\"\nimport { buttonLinkPropTypes } from \"utils/types\"\nimport CustomLink from \"./custom-link\"\n\nconst ButtonContent = ({ button, appearance, compact }) => {\n  return (\n    <div\n      className={classNames(\n        // Common classes\n        \"block w-full lg:w-auto text-center uppercase tracking-wide font-semibold text-base md:text-sm border-2 rounded-md\",\n        // Full-size button\n        {\n          \"px-8 py-4\": compact === false,\n        },\n        // Compact button\n        {\n          \"px-6 py-2\": compact === true,\n        },\n        // Specific to when the button is fully dark\n        {\n          \"bg-primary-600 text-white border-primary-600\": appearance === \"dark\",\n        },\n        // Specific to when the button is dark outlines\n        {\n          \"text-primary-600 border-primary-600\": appearance === \"dark-outline\",\n        },\n        // Specific to when the button is fully white\n        {\n          \"bg-white text-primary-600 border-white\": appearance === \"white\",\n        },\n        // Specific to when the button is white outlines\n        {\n          \"text-white border-white\": appearance === \"white-outline\",\n        }\n      )}\n    >\n      {button.text}\n    </div>\n  )\n}\n\nconst ButtonLink = ({ button, appearance, compact = false }) => {\n  return (\n    <CustomLink link={button}>\n      <ButtonContent\n        button={button}\n        appearance={appearance}\n        compact={compact}\n      />\n    </CustomLink>\n  )\n}\n\nButtonLink.propTypes = {\n  button: buttonLinkPropTypes,\n  appearance: PropTypes.oneOf([\n    \"dark\",\n    \"white-outline\",\n    \"white\",\n    \"dark-outline\",\n  ]),\n  compact: PropTypes.bool,\n}\n\nexport default ButtonLink\n"
  },
  {
    "path": "packages/starters/next-corporate/starter/components/elements/button.js",
    "content": "import classNames from \"classnames\"\nimport PropTypes from \"prop-types\"\nimport { buttonLinkPropTypes } from \"utils/types\"\nimport Loader from \"./loader\"\n\nconst Button = ({\n  button,\n  appearance,\n  compact = false,\n  handleClick,\n  loading = false,\n  type,\n}) => {\n  return (\n    <button link={button} onClick={handleClick} type={type}>\n      <div\n        className={classNames(\n          // Common classes\n          \"flex w-full justify-center lg:w-auto text-center uppercase tracking-wide font-semibold text-base md:text-sm border-2 rounded-md\",\n          // Full-size button\n          {\n            \"px-8 py-4\": compact === false,\n          },\n          // Compact button\n          {\n            \"px-6 py-2\": compact === true,\n          },\n          // Specific to when the button is fully dark\n          {\n            \"bg-primary-600 text-white border-primary-600\":\n              appearance === \"dark\",\n          },\n          // Specific to when the button is dark outlines\n          {\n            \"text-primary-600 border-primary-600\":\n              appearance === \"dark-outline\",\n          },\n          // Specific to when the button is fully white\n          {\n            \"bg-white text-primary-600 border-white\": appearance === \"white\",\n          },\n          // Specific to when the button is white outlines\n          {\n            \"text-white border-white\": appearance === \"white-outline\",\n          }\n        )}\n      >\n        {loading && <Loader />}\n        {button.text}\n      </div>\n    </button>\n  )\n}\n\nButton.propTypes = {\n  button: buttonLinkPropTypes,\n  appearance: PropTypes.oneOf([\n    \"dark\",\n    \"white-outline\",\n    \"white\",\n    \"dark-outline\",\n  ]),\n  compact: PropTypes.bool,\n}\n\nexport default Button\n"
  },
  {
    "path": "packages/starters/next-corporate/starter/components/elements/custom-link.js",
    "content": "import Link from \"next/link\"\nimport PropTypes from \"prop-types\"\nimport { linkPropTypes } from \"utils/types\"\n\nconst CustomLink = ({ link, children }) => {\n  const isInternalLink = link.url.startsWith(\"/\")\n\n  // For internal links, use the Next.js Link component\n  if (isInternalLink) {\n    return (\n      <Link href={link.url}>\n        <a>{children}</a>\n      </Link>\n    )\n  }\n\n  // Plain <a> tags for external links\n  if (link.newTab) {\n    return (\n      <a href={link.url} target=\"_blank\" rel=\"noopener noreferrer\">\n        {children}\n      </a>\n    )\n  }\n\n  return (\n    <a href={link.url} target=\"_self\">\n      {children}\n    </a>\n  )\n}\n\nCustomLink.propTypes = {\n  link: linkPropTypes,\n  children: PropTypes.oneOfType([\n    PropTypes.arrayOf(PropTypes.node),\n    PropTypes.node,\n  ]).isRequired,\n}\n\nexport default CustomLink\n"
  },
  {
    "path": "packages/starters/next-corporate/starter/components/elements/footer.js",
    "content": "import PropTypes from \"prop-types\"\nimport { linkPropTypes, mediaPropTypes } from \"utils/types\"\nimport NextImage from \"./image\"\nimport CustomLink from \"./custom-link\"\n\nconst Footer = ({ footer }) => {\n  return (\n    <footer className=\"pt-12 bg-gray-100\">\n      <div className=\"container flex flex-col lg:flex-row lg:justify-between\">\n        <div>\n          {footer.logo && (\n            <NextImage width=\"120\" height=\"33\" media={footer.logo} />\n          )}\n        </div>\n        <nav className=\"flex flex-wrap flex-row lg:gap-20 items-start lg:justify-end mb-10\">\n          {footer.columns.map((footerColumn) => (\n            <div\n              key={footerColumn.id}\n              className=\"mt-10 lg:mt-0 w-6/12 lg:w-auto\"\n            >\n              <p className=\"uppercase tracking-wide font-semibold\">\n                {footerColumn.title}\n              </p>\n              <ul className=\"mt-2\">\n                {footerColumn.links.map((link) => (\n                  <li\n                    key={link.id}\n                    className=\"text-gray-700 py-1 px-1 -mx-1 hover:text-gray-900\"\n                  >\n                    <CustomLink link={link}>{link.text}</CustomLink>\n                  </li>\n                ))}\n              </ul>\n            </div>\n          ))}\n        </nav>\n      </div>\n      <div className=\"text-sm bg-gray-200 py-6 text-gray-700\">\n        <div className=\"container\">{footer.smallText}</div>\n      </div>\n    </footer>\n  )\n}\n\nFooter.propTypes = {\n  footer: PropTypes.shape({\n    logo: mediaPropTypes.isRequired,\n    columns: PropTypes.arrayOf(\n      PropTypes.shape({\n        id: PropTypes.oneOfType([PropTypes.string, PropTypes.number])\n          .isRequired,\n        title: PropTypes.string.isRequired,\n        links: PropTypes.arrayOf(linkPropTypes),\n      })\n    ),\n    smallText: PropTypes.string.isRequired,\n  }),\n}\n\nexport default Footer\n"
  },
  {
    "path": "packages/starters/next-corporate/starter/components/elements/image.js",
    "content": "import { getStrapiMedia } from \"utils/media\"\nimport Image from \"next/image\"\nimport PropTypes from \"prop-types\"\nimport { mediaPropTypes } from \"utils/types\"\n\nconst NextImage = ({ media, ...props }) => {\n  const { url, alternativeText, width, height } = media.data.attributes\n\n  const loader = ({ src, width }) => {\n    return getStrapiMedia(src)\n  }\n\n  // The image has a fixed width and height\n  if (props.width && props.height) {\n    return (\n      <Image loader={loader} src={url} alt={alternativeText || \"\"} {...props} />\n    )\n  }\n\n  // The image is responsive\n  return (\n    <Image\n      loader={loader}\n      layout=\"responsive\"\n      width={width || \"100%\"}\n      height={height || \"100%\"}\n      objectFit=\"contain\"\n      src={url}\n      alt={alternativeText || \"\"}\n    />\n  )\n}\n\nImage.propTypes = {\n  media: mediaPropTypes.isRequired,\n  className: PropTypes.string,\n}\n\nexport default NextImage\n"
  },
  {
    "path": "packages/starters/next-corporate/starter/components/elements/loader.js",
    "content": "import React from \"react\"\n\nconst Loader = () => {\n  return (\n    <svg\n      viewBox=\"0 0 38 38\"\n      className=\"animate-spin h-5 w-5 stroke-current text-black-600 mr-2\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n    >\n      <g fill=\"none\" fillRule=\"evenodd\">\n        <g transform=\"translate(1 1)\" strokeWidth=\"2\">\n          <circle strokeOpacity=\".5\" cx=\"18\" cy=\"18\" r=\"18\" />\n          <path d=\"M36 18c0-9.94-8.06-18-18-18\">\n            <animateTransform\n              attributeName=\"transform\"\n              type=\"rotate\"\n              from=\"0 18 18\"\n              to=\"360 18 18\"\n              dur=\"1s\"\n              repeatCount=\"indefinite\"\n            />\n          </path>\n        </g>\n      </g>\n    </svg>\n  )\n}\n\nexport default Loader\n"
  },
  {
    "path": "packages/starters/next-corporate/starter/components/elements/mobile-nav-menu.js",
    "content": "import PropTypes from \"prop-types\"\nimport { MdClose, MdChevronRight } from \"react-icons/md\"\nimport { mediaPropTypes, linkPropTypes, buttonLinkPropTypes } from \"utils/types\"\nimport { useLockBodyScroll } from \"utils/hooks\"\nimport { getButtonAppearance } from \"utils/button\"\nimport ButtonLink from \"./button-link\"\nimport NextImage from \"./image\"\nimport CustomLink from \"./custom-link\"\n\nconst MobileNavMenu = ({ navbar, closeSelf }) => {\n  // Prevent window scroll while mobile nav menu is open\n  useLockBodyScroll()\n\n  return (\n    <div className=\"w-screen h-screen fixed top-0 left-0 overflow-y-scroll bg-white z-10 pb-6\">\n      <div className=\"container h-full flex flex-col justify-between\">\n        {/* Top section */}\n        <div className=\"flex flex-row justify-between py-2 items-center\">\n          {/* Company logo */}\n          <NextImage width=\"120\" height=\"33\" media={navbar.logo} />\n          {/* Close button */}\n          <button onClick={closeSelf} className=\"py-1 px-1\">\n            <MdClose className=\"h-8 w-auto\" />\n          </button>\n        </div>\n        {/* Bottom section */}\n        <div className=\"flex flex-col justify-end w-9/12 mx-auto\">\n          <ul className=\"flex flex-col list-none gap-6 items-baseline text-xl mb-10\">\n            {navbar.links.map((navLink) => (\n              <li key={navLink.id} className=\"block w-full\">\n                <CustomLink link={navLink}>\n                  <div className=\"hover:text-gray-900 py-6 flex flex-row justify-between items-center\">\n                    <span>{navLink.text}</span>\n                    <MdChevronRight className=\"h-8 w-auto\" />\n                  </div>\n                </CustomLink>\n              </li>\n            ))}\n          </ul>\n          <ButtonLink\n            button={navbar.button}\n            appearance={getButtonAppearance(navbar.button.type, \"light\")}\n          />\n        </div>\n      </div>\n    </div>\n  )\n}\n\nMobileNavMenu.propTypes = {\n  navbar: PropTypes.shape({\n    logo: mediaPropTypes,\n    links: PropTypes.arrayOf(linkPropTypes),\n    button: buttonLinkPropTypes,\n  }),\n  closeSelf: PropTypes.func,\n}\n\nexport default MobileNavMenu\n"
  },
  {
    "path": "packages/starters/next-corporate/starter/components/elements/navbar.js",
    "content": "import { useState } from \"react\"\nimport PropTypes from \"prop-types\"\nimport Link from \"next/link\"\nimport { useRouter } from \"next/router\"\n\nimport { getButtonAppearance } from \"utils/button\"\nimport { mediaPropTypes, linkPropTypes, buttonLinkPropTypes } from \"utils/types\"\nimport { MdMenu } from \"react-icons/md\"\nimport MobileNavMenu from \"./mobile-nav-menu\"\nimport ButtonLink from \"./button-link\"\nimport NextImage from \"./image\"\nimport CustomLink from \"./custom-link\"\nimport LocaleSwitch from \"../locale-switch\"\n\nconst Navbar = ({ navbar, pageContext }) => {\n  const router = useRouter()\n  const [mobileMenuIsShown, setMobileMenuIsShown] = useState(false)\n\n  return (\n    <>\n      {/* The actual navbar */}\n      <nav className=\"border-gray-200 border-b-2 py-6 sm:py-2\">\n        <div className=\"container flex flex-row items-center justify-between\">\n          {/* Content aligned to the left */}\n          <div className=\"flex flex-row items-center\">\n            <Link href=\"/\">\n              <a className=\"h-8 w-32\">\n                <NextImage width=\"120\" height=\"33\" media={navbar.logo} />\n              </a>\n            </Link>\n            {/* List of links on desktop */}\n            <ul className=\"hidden list-none md:flex flex-row gap-4 items-baseline ml-10\">\n              {navbar.links.map((navLink) => (\n                <li key={navLink.id}>\n                  <CustomLink link={navLink} locale={router.locale}>\n                    <div className=\"hover:text-gray-900 px-2 py-1\">\n                      {navLink.text}\n                    </div>\n                  </CustomLink>\n                </li>\n              ))}\n            </ul>\n          </div>\n          <div className=\"flex\">\n            {/* Locale Switch Mobile */}\n            {pageContext.localizedPaths && (\n              <div className=\"md:hidden\">\n                <LocaleSwitch pageContext={pageContext} />\n              </div>\n            )}\n            {/* Hamburger menu on mobile */}\n            <button\n              onClick={() => setMobileMenuIsShown(true)}\n              className=\"p-1 block md:hidden\"\n            >\n              <MdMenu className=\"h-8 w-auto\" />\n            </button>\n            {/* CTA button on desktop */}\n            {navbar.button && (\n              <div className=\"hidden md:block\">\n                <ButtonLink\n                  button={navbar.button}\n                  appearance={getButtonAppearance(navbar.button.type, \"light\")}\n                  compact\n                />\n              </div>\n            )}\n            {/* Locale Switch Desktop */}\n            {pageContext.localizedPaths && (\n              <div className=\"hidden md:block\">\n                <LocaleSwitch pageContext={pageContext} />\n              </div>\n            )}\n          </div>\n        </div>\n      </nav>\n\n      {/* Mobile navigation menu panel */}\n      {mobileMenuIsShown && (\n        <MobileNavMenu\n          navbar={navbar}\n          closeSelf={() => setMobileMenuIsShown(false)}\n        />\n      )}\n    </>\n  )\n}\n\nNavbar.propTypes = {\n  navbar: PropTypes.shape({\n    logo: PropTypes.shape({\n      image: mediaPropTypes,\n      url: PropTypes.string,\n    }),\n    links: PropTypes.arrayOf(linkPropTypes),\n    button: buttonLinkPropTypes,\n  }),\n  initialLocale: PropTypes.string,\n}\n\nexport default Navbar\n"
  },
  {
    "path": "packages/starters/next-corporate/starter/components/elements/notification-banner.js",
    "content": "import Markdown from \"react-markdown\"\nimport classNames from \"classnames\"\nimport { MdClose } from \"react-icons/md\"\n\nconst NotificationBanner = ({ data: { text, type }, closeSelf }) => {\n  return (\n    <div\n      className={classNames(\n        // Common classes\n        \"text-white px-2 py-2\",\n        {\n          // Apply theme based on notification type\n          \"bg-blue-600\": type === \"info\",\n          \"bg-orange-600\": type === \"warning\",\n          \"bg-red-600\": type === \"alert\",\n        }\n      )}\n    >\n      <div className=\"container flex flex-row justify-between items-center \">\n        <div className=\"rich-text-banner flex-1\">\n          <Markdown>{text}</Markdown>\n        </div>\n        <button onClick={closeSelf} className=\"px-1 py-1 flex-shrink-0\">\n          <MdClose className=\"h-6 w-auto\" color=\"#fff\" />\n        </button>\n      </div>\n    </div>\n  )\n}\n\nexport default NotificationBanner\n"
  },
  {
    "path": "packages/starters/next-corporate/starter/components/elements/seo.js",
    "content": "import { NextSeo } from \"next-seo\"\nimport PropTypes from \"prop-types\"\nimport { getStrapiMedia } from \"utils/media\"\nimport { mediaPropTypes } from \"utils/types\"\n\nconst Seo = ({ metadata }) => {\n  // Prevent errors if no metadata was set\n  if (!metadata) return null\n\n  return (\n    <NextSeo\n      title={metadata.metaTitle}\n      description={metadata.metaDescription}\n      openGraph={{\n        // Title and description are mandatory\n        title: metadata.metaTitle,\n        description: metadata.metaDescription,\n        // Only include OG image if we have it\n        // Careful: if you disable image optimization in Strapi, this will break\n        ...(metadata.shareImage && {\n          images: Object.values(\n            metadata.shareImage.data.attributes.formats\n          ).map((image) => {\n            return {\n              url: getStrapiMedia(image.url),\n              width: image.width,\n              height: image.height,\n            }\n          }),\n        }),\n      }}\n      // Only included Twitter data if we have it\n      twitter={{\n        ...(metadata.twitterCardType && { cardType: metadata.twitterCardType }),\n        // Handle is the twitter username of the content creator\n        ...(metadata.twitterUsername && { handle: metadata.twitterUsername }),\n      }}\n    />\n  )\n}\n\nSeo.propTypes = {\n  metadata: PropTypes.shape({\n    metaTitle: PropTypes.string.isRequired,\n    metaDescription: PropTypes.string.isRequired,\n    shareImage: mediaPropTypes,\n    twitterCardType: PropTypes.string,\n    twitterUsername: PropTypes.string,\n  }),\n}\n\nexport default Seo\n"
  },
  {
    "path": "packages/starters/next-corporate/starter/components/elements/video.js",
    "content": "import PropTypes from \"prop-types\"\nimport { getStrapiMedia } from \"utils/media\"\nimport { mediaPropTypes } from \"utils/types\"\n\nconst Video = ({\n  media,\n  poster,\n  className,\n  controls = true,\n  autoPlay = false,\n}) => {\n  const fullVideoUrl = getStrapiMedia(media.url)\n  const fullPosterUrl = getStrapiMedia(poster?.url)\n\n  return (\n    <video\n      className={className}\n      poster={fullPosterUrl}\n      controls={controls}\n      autoPlay={autoPlay}\n    >\n      <source src={fullVideoUrl} type={media.mime} />\n    </video>\n  )\n}\n\nVideo.propTypes = {\n  media: mediaPropTypes.isRequired,\n  poster: mediaPropTypes,\n  className: PropTypes.string,\n  controls: PropTypes.bool,\n  autoPlay: PropTypes.bool,\n}\n\nexport default Video\n"
  },
  {
    "path": "packages/starters/next-corporate/starter/components/icons/world.js",
    "content": "import React from \"react\"\n\nconst WorldIcon = () => {\n  return (\n    <div className=\"w-4 h-4 mr-2 \">\n      <svg\n        className=\"fill-current text-primary-600\"\n        xmlns=\"http://www.w3.org/2000/svg\"\n        viewBox=\"0 0 490 490\"\n      >\n        <path d=\"M490 245C490 109.915 380.089 0 245 0S0 109.915 0 245c0 135.085 109.911 245 245 245s245-109.915 245-245zM263.846 380.696c11.096.548 21.781 1.361 31.996 2.43-11.076 17.053-22.316 31.807-31.996 43.387v-45.817zm0-38.711v-81.591h80.86c-4.208 31.012-14.804 60.245-27.914 86.267-16.352-2.171-34.078-3.82-52.946-4.676zm0-119.946v-80.36c19.587-.4 39.945-1.888 60.783-4.753 11.631 24.771 20.063 53.417 21.66 85.113h-82.443zm0-118.966v-51.29c11.357 11.26 26.119 27.703 40.158 48.699-13.635 1.373-27.106 2.33-40.158 2.591zm-37.692-.673c-16.318-.805-31.757-2.205-46.078-4.046 16.965-24.888 34.774-43.291 46.078-53.774v57.82zm0 38.825v80.815h-89.986c1.658-32.662 10.587-62.206 22.834-87.553a619.247 619.247 0 0067.152 6.738zm0 119.17v81.003c-19.581.4-39.939 1.851-60.768 4.712-12.983-25.886-23.46-54.922-27.638-85.716h88.406zm0 119.628v55.272c-11.131-12.632-25.648-30.714-39.886-52.708 13.541-1.356 26.922-2.304 39.886-2.564zm111.038 8.844c18.508 3.238 34.063 6.762 45.881 9.837-23.907 21.36-52.737 37.222-84.641 45.727 12.172-15.038 25.844-33.717 38.76-55.564zm18.791-35.812c12.927-28.029 23.045-59.309 26.832-92.66h68.71c-2.959 39.704-17.194 76.236-39.557 106.571-10.45-3.268-29.932-8.767-55.985-13.911zm28.019-131.016c-1.427-34.133-9.499-64.953-20.987-91.989a444.866 444.866 0 0044.872-12.453c23.245 29.466 38.694 65.279 43.062 104.441h-66.947zM345.135 94.558a341.42 341.42 0 00-30.991-44.13c23.562 8.348 45.226 20.672 64.008 36.41-11.037 3.104-22.05 5.585-33.017 7.72zm-206.091-2.866c-9.782-1.944-18.654-3.915-26.181-5.776a207.72 207.72 0 0152.147-31.524c-8.691 10.813-17.559 23.385-25.966 37.3zM120.62 127.3c-12.104 27.687-20.691 59.444-22.168 94.739H39.051c4.376-39.236 19.87-75.112 43.187-104.609 8.84 2.654 21.899 6.232 38.382 9.87zM99.638 260.395c3.796 33.422 13.951 64.76 26.918 92.833-16.081 3.579-32.278 8.175-48.492 13.78-22.381-30.344-36.629-66.891-39.589-106.614h61.163zm45.423 128.115c12.212 20.697 25.116 38.605 36.803 53.259-28.391-9.075-54.009-24.2-75.627-43.689 12.978-3.859 25.933-7.066 38.824-9.57z\" />\n      </svg>\n    </div>\n  )\n}\n\nexport default WorldIcon\n"
  },
  {
    "path": "packages/starters/next-corporate/starter/components/layout.js",
    "content": "import { useState } from \"react\"\nimport Navbar from \"./elements/navbar\"\nimport Footer from \"./elements/footer\"\nimport NotificationBanner from \"./elements/notification-banner\"\n\nconst Layout = ({ children, global, pageContext }) => {\n  const { navbar, footer, notificationBanner } = global.attributes\n\n  const [bannerIsShown, setBannerIsShown] = useState(true)\n  return (\n    <div className=\"flex flex-col justify-between min-h-screen\">\n      {/* Aligned to the top */}\n      <div className=\"flex-1\">\n        {notificationBanner && bannerIsShown && (\n          <NotificationBanner\n            data={notificationBanner}\n            closeSelf={() => setBannerIsShown(false)}\n          />\n        )}\n        <Navbar navbar={navbar} pageContext={pageContext} />\n        <div>{children}</div>\n      </div>\n      {/* Aligned to the bottom */}\n      <Footer footer={footer} />\n    </div>\n  )\n}\n\nexport default Layout\n"
  },
  {
    "path": "packages/starters/next-corporate/starter/components/locale-switch.js",
    "content": "import { useEffect, useState, useRef } from \"react\"\nimport { useRouter } from \"next/router\"\nimport PropTypes from \"prop-types\"\nimport Link from \"next/link\"\n\nimport Cookies from \"js-cookie\"\nimport { MdExpandMore } from \"react-icons/md\"\nimport WorldIcon from \"./icons/world\"\n\nimport { useOnClickOutside } from \"../utils/hooks\"\n\nconst LocaleSwitch = ({ pageContext }) => {\n  const isMounted = useRef(false)\n  const select = useRef()\n  const router = useRouter()\n  const [locale, setLocale] = useState()\n  const [showing, setShowing] = useState(false)\n\n  const handleLocaleChange = async (selectedLocale) => {\n    // Persist the user's language preference\n    // https://nextjs.org/docs/advanced-features/i18n-routing#leveraging-the-next_locale-cookie\n    Cookies.set(\"NEXT_LOCALE\", selectedLocale)\n    setLocale(selectedLocale)\n  }\n\n  const handleLocaleChangeRef = useRef(handleLocaleChange)\n  useOnClickOutside(select, () => setShowing(false))\n\n  useEffect(() => {\n    const localeCookie = Cookies.get(\"NEXT_LOCALE\")\n    if (!localeCookie) {\n      handleLocaleChangeRef.current(router.locale)\n    }\n\n    const checkLocaleMismatch = async () => {\n      if (\n        !isMounted.current &&\n        localeCookie &&\n        localeCookie !== pageContext.locale\n      ) {\n        // Redirect to locale page if locale mismatch\n        const localePage = getLocalizedPage(localeCookie, pageContext)\n\n        router.push(\n          `${localizePath({ ...pageContext, ...localePage })}`,\n          `${localizePath({ ...pageContext, ...localePage })}`,\n          { locale: localePage.locale }\n        )\n      }\n      setShowing(false)\n    }\n\n    setLocale(localeCookie || router.locale)\n    checkLocaleMismatch()\n\n    return () => {\n      isMounted.current = true\n    }\n  }, [locale, router, pageContext])\n\n  return (\n    <div ref={select} className=\"relative ml-4 \">\n      <button\n        type=\"button\"\n        className=\"hover:bg-primary-50 hover:text-primary-600 focus:bg-primary-50 focus:text-primary-600 focus:outline-none flex items-center justify-between px-2 py-2 cursor-pointer h-full rounded-md w-20\"\n        onClick={() => setShowing(!showing)}\n      >\n        <WorldIcon />\n        <span className=\"capitalize\">{locale}</span>\n        <MdExpandMore className=\"ml-1 text-primary-600\" />\n      </button>\n      <div\n        className={`w-full bg-white p-1 mt-1 shadow-lg rounded-md ${\n          showing ? \"absolute\" : \"hidden\"\n        }`}\n      >\n        {pageContext.localizedPaths &&\n          pageContext.localizedPaths.map(({ href, locale }) => {\n            return (\n              <Link\n                href={href}\n                key={locale}\n                locale={locale}\n                role=\"option\"\n                passHref\n              >\n                <p\n                  onClick={() => handleLocaleChange(locale)}\n                  className=\"capitalize hover:bg-primary-50 hover:text-primary-600  cursor-pointer p-2 rounded-md text-center hover:text-primary-600\"\n                >\n                  {locale}\n                </p>\n              </Link>\n            )\n          })}\n      </div>\n    </div>\n  )\n}\n\nLocaleSwitch.propTypes = {\n  initialLocale: PropTypes.string,\n}\n\nexport default LocaleSwitch\n"
  },
  {
    "path": "packages/starters/next-corporate/starter/components/sections/bottom-actions.js",
    "content": "import ButtonLink from \"@/components/elements/button-link\"\nimport { getButtonAppearance } from \"utils/button\"\n\nconst BottomActions = ({ data }) => {\n  return (\n    <section className=\"bg-primary-800 py-20 text-center\">\n      <h2 className=\"title text-white mb-10\">{data.title}</h2>\n      {/* Buttons row */}\n      <div className=\"container flex flex-row justify-center flex-wrap gap-4\">\n        {data.buttons.map((button) => (\n          <ButtonLink\n            button={button}\n            appearance={getButtonAppearance(button.type, \"dark\")}\n            key={button.id}\n          />\n        ))}\n      </div>\n    </section>\n  )\n}\n\nexport default BottomActions\n"
  },
  {
    "path": "packages/starters/next-corporate/starter/components/sections/feature-columns-group.js",
    "content": "import NextImage from \"../elements/image\"\n\nconst FeatureColumnsGroup = ({ data }) => {\n  return (\n    <div className=\"container flex flex-col lg:flex-row lg:flex-wrap gap-12 align-top py-12\">\n      {data.features.map((feature) => (\n        <div className=\"flex-1 text-lg\" key={feature.id}>\n          <div className=\"w-10 h-10\">\n            <NextImage media={feature.icon} />\n          </div>\n          <h3 className=\"font-bold mt-4 mb-4\">{feature.title}</h3>\n          <p>{feature.description}</p>\n        </div>\n      ))}\n    </div>\n  )\n}\n\nexport default FeatureColumnsGroup\n"
  },
  {
    "path": "packages/starters/next-corporate/starter/components/sections/feature-rows-group.js",
    "content": "import classNames from \"classnames\"\nimport NextImage from \"../elements/image\"\nimport Video from \"../elements/video\"\nimport CustomLink from \"../elements/custom-link\"\n\nconst FeatureRowsGroup = ({ data }) => {\n  return (\n    <div className=\"container flex flex-col gap-12 py-12\">\n      {data.features.map((feature, index) => (\n        <div\n          className={classNames(\n            // Common classes\n            \"flex flex-col justify-start md:justify-between md:items-center gap-10\",\n            {\n              \"lg:flex-row\": index % 2 === 0,\n              \"lg:flex-row-reverse\": index % 2 === 1,\n            }\n          )}\n          key={feature.id}\n        >\n          {/* Text section */}\n          <div className=\"w-full lg:w-6/12 lg:pr-6 text-lg\">\n            <h3 className=\"title\">{feature.title}</h3>\n            <p className=\"my-6\">{feature.description}</p>\n            <CustomLink link={feature.link}>\n              <div className=\"text-blue-600 with-arrow hover:underline\">\n                {feature.link.text}\n              </div>\n            </CustomLink>\n          </div>\n          {/* Media section */}\n          <div className=\"w-full sm:9/12 lg:w-4/12 max-h-full\">\n            {/* Images */}\n            {feature.media.data.attributes.mime.startsWith(\"image\") && (\n              <div className=\"w-full h-auto\">\n                <NextImage media={feature.media} />\n              </div>\n            )}\n            {/* Videos */}\n            {feature.media.data.attributes.mime.startsWith(\"video\") && (\n              <Video\n                media={feature.media}\n                className=\"w-full h-auto\"\n                autoPlay\n                controls={false}\n              />\n            )}\n          </div>\n        </div>\n      ))}\n    </div>\n  )\n}\n\nexport default FeatureRowsGroup\n"
  },
  {
    "path": "packages/starters/next-corporate/starter/components/sections/hero.js",
    "content": "import Markdown from \"react-markdown\"\nimport { getButtonAppearance } from \"utils/button\"\nimport ButtonLink from \"../elements/button-link\"\nimport NextImage from \"../elements/image\"\n\nconst Hero = ({ data }) => {\n  return (\n    <main className=\"container flex flex-col md:flex-row items-center justify-between py-12\">\n      {/* Left column for content */}\n      <div className=\"flex-1 sm:pr-8\">\n        {/* Hero section label */}\n        <p className=\"uppercase tracking-wide font-semibold\">{data.label}</p>\n        {/* Big title */}\n        <h1 className=\"title mt-2 sm:mt-0 mb-4 sm:mb-2\">{data.title}</h1>\n        {/* Description paragraph */}\n        <p className=\"text-xl mb-6\">{data.description}</p>\n        {/* Buttons row */}\n        <div className=\"flex flex-row flex-wrap gap-4\">\n          {data.buttons.map((button) => (\n            <ButtonLink\n              button={button}\n              appearance={getButtonAppearance(button.type, \"light\")}\n              key={button.id}\n            />\n          ))}\n        </div>\n        {/* Small rich text */}\n        <div className=\"text-base md:text-sm mt-4 sm:mt-3 rich-text-hero\">\n          <Markdown>{data.smallTextWithLink}</Markdown>\n        </div>\n      </div>\n      {/* Right column for the image */}\n      <div className=\"flex-shrink-0 w-full md:w-6/12 mt-6 md:mt-0\">\n        <NextImage media={data.picture} />\n      </div>\n    </main>\n  )\n}\n\nexport default Hero\n"
  },
  {
    "path": "packages/starters/next-corporate/starter/components/sections/large-video.js",
    "content": "import Video from \"../elements/video\"\n\nconst LargeVideo = ({ data }) => {\n  return (\n    <section className=\"container flex flex-col align-middle text-center pt-12 pb-16\">\n      <h2 className=\"title mb-6\">{data.title}</h2>\n      <p className=\"text-lg mb-10\">{data.description}</p>\n      {/* Video wrapper */}\n      <div className=\"w-full lg:w-9/12 mx-auto overflow-hidden shadow-2xl\">\n        <Video\n          media={data.video}\n          poster={data.poster}\n          className=\"w-full max-h-full\"\n        />\n      </div>\n    </section>\n  )\n}\n\nexport default LargeVideo\n"
  },
  {
    "path": "packages/starters/next-corporate/starter/components/sections/lead-form.js",
    "content": "import { useState } from \"react\"\nimport { fetchAPI } from \"utils/api\"\nimport * as yup from \"yup\"\nimport { Formik, Form, Field } from \"formik\"\nimport Button from \"../elements/button\"\n\nconst LeadForm = ({ data }) => {\n  const [loading, setLoading] = useState(false)\n\n  const LeadSchema = yup.object().shape({\n    email: yup.string().email().required(),\n  })\n\n  return (\n    <div className=\"py-10 text-center\">\n      <h1 className=\"text-3xl mb-10 font-bold mb-2\">{data.title}</h1>\n      <div className=\"flex flex-col items-center\">\n        <Formik\n          initialValues={{ email: \"\" }}\n          validationSchema={LeadSchema}\n          onSubmit={async (values, { setSubmitting, setErrors }) => {\n            setLoading(true)\n\n            try {\n              setErrors({ api: null })\n              await fetchAPI(\n                \"/lead-form-submissions\",\n                {},\n                {\n                  method: \"POST\",\n                  body: JSON.stringify({\n                    data: {\n                      email: values.email,\n                      location: data.location,\n                    },\n                  }),\n                }\n              )\n            } catch (err) {\n              setErrors({ api: err.message })\n            }\n\n            setLoading(false)\n            setSubmitting(false)\n          }}\n        >\n          {({ errors, touched, isSubmitting }) => (\n            <div>\n              <Form className=\"flex flex-col md:flex-row gap-4\">\n                <Field\n                  className=\"text-base focus:outline-none py-4 md:py-0 px-4 border-2 rounded-md\"\n                  type=\"email\"\n                  name=\"email\"\n                  placeholder={data.emailPlaceholder}\n                />\n                <Button\n                  type=\"submit\"\n                  button={data.submitButton}\n                  disabled={isSubmitting}\n                  loading={loading}\n                />\n              </Form>\n              <p className=\"text-red-500 h-12 text-sm mt-1 ml-2 text-left\">\n                {(errors.email && touched.email && errors.email) || errors.api}\n              </p>\n            </div>\n          )}\n        </Formik>\n      </div>\n    </div>\n  )\n}\n\nexport default LeadForm\n"
  },
  {
    "path": "packages/starters/next-corporate/starter/components/sections/pricing.js",
    "content": "import { MdCheckBox } from \"react-icons/md\"\nimport classNames from \"classnames\"\n\nconst Pricing = ({ data }) => {\n  return (\n    <div className=\"container py-12\">\n      <h1 className=\"text-4xl text-center\">{data.title}</h1>\n      <div className=\"flex flex-col lg:flex-row gap-4 lg:justify-center mt-6\">\n        {data.plans.map((plan) => (\n          <div\n            className={classNames(\n              // Common classes\n              \"rounded-md border-2 py-4 px-4 flex-1 md:w-lg\",\n              // Normal plan\n              {\n                \"bg-gray-100 text-gray-900 border-gray-300\":\n                  !plan.isRecommended,\n              },\n              // Recommended plan\n              {\n                \"bg-primary-100 text-primary-900 border-primary-300\":\n                  plan.isRecommended,\n              }\n            )}\n            key={plan.id}\n          >\n            <h2 className=\"text-2xl\">{plan.name}</h2>\n            <p\n              className={classNames(\"mt-4 text-lg\", {\n                \"text-primary-700\": plan.isRecommended,\n                \"text-gray-700\": !plan.isRecommended,\n              })}\n            >\n              {plan.description}\n            </p>\n            <p className=\"text-3xl mt-4\">\n              {plan.price === 0 ? \"Free \" : `$${plan.price} `}\n              <span className=\"text-base font-medium\">{plan.pricePeriod}</span>\n            </p>\n            <ul className=\"mt-4 flex flex-col gap-3\">\n              {plan.features.map((feature) => (\n                <li\n                  className=\"flex flex-row justify-between items-center\"\n                  key={feature.id}\n                >\n                  <span>{feature.name}</span>\n                  <MdCheckBox className=\"h-6 w-auto text-gray-900\" />\n                </li>\n              ))}\n            </ul>\n          </div>\n        ))}\n      </div>\n    </div>\n  )\n}\n\nexport default Pricing\n"
  },
  {
    "path": "packages/starters/next-corporate/starter/components/sections/rich-text.js",
    "content": "import PropTypes from \"prop-types\"\nimport Markdown from \"react-markdown\"\n\nconst RichText = ({ data }) => {\n  return (\n    <div className=\"prose prose-lg container py-12\">\n      <Markdown>{data.content}</Markdown>\n    </div>\n  )\n}\n\nRichText.propTypes = {\n  data: PropTypes.shape({\n    content: PropTypes.string,\n  }),\n}\n\nexport default RichText\n"
  },
  {
    "path": "packages/starters/next-corporate/starter/components/sections/testimonials-group.js",
    "content": "import classNames from \"classnames\"\nimport { useState } from \"react\"\nimport NextImage from \"../elements/image\"\nimport CustomLink from \"../elements/custom-link\"\n\nconst TestimonialsGroup = ({ data }) => {\n  // Only show one testimonial at a time\n  const [selectedTestimonialIndex, setSelectedTestimonialIndex] = useState(0)\n  const selectedTestimonial = data.testimonials[selectedTestimonialIndex]\n\n  return (\n    <section className=\"text-center text-lg bg-gray-200 pt-12 pb-16\">\n      <h2 className=\"title mb-4\">{data.title}</h2>\n      <p className=\"text-gray-700 mb-4\">{data.description}</p>\n      <CustomLink link={data.link}>\n        <span className=\"with-arrow text-blue-700 hover:underline\">\n          {data.link.text}\n        </span>\n      </CustomLink>\n      {/* Current testimonial card */}\n      <div className=\"max-w-5xl w-8/12 sm:w-8/12 bg-white shadow-md sm:shadow-xl mx-auto flex flex-col sm:flex-row mt-10 text-left\">\n        <div className=\"w-full md:w-4/12 flex-shrink-0\">\n          <NextImage media={selectedTestimonial.picture} />\n        </div>\n        <div className=\"px-4 py-4 sm:px-12 sm:pt-12 sm:pb-4 flex flex-col justify-between\">\n          <div>\n            <NextImage\n              width=\"120\"\n              height=\"33\"\n              media={selectedTestimonial.logo}\n            />\n            <p className=\"italic mb-6\">\n              &quot;{selectedTestimonial.text}&quot;\n            </p>\n            <p className=\"font-bold text-base sm:text-sm\">\n              {selectedTestimonial.authorName}\n            </p>\n            <p className=\"text-base sm:text-sm\">\n              {selectedTestimonial.authorTitle}\n            </p>\n          </div>\n          <CustomLink\n            link={{\n              url: selectedTestimonial.link,\n              text: \"\",\n              newTab: false,\n              id: 0,\n            }}\n          >\n            <span className=\"uppercase tracking-wide text-blue-700 hover:underline  with-arrow sm:self-end mt-6 sm:mt-0\">\n              Read story\n            </span>\n          </CustomLink>\n        </div>\n      </div>\n      {/* Change selected testimonial (only if there is more than one) */}\n      {data.testimonials.length > 1 && (\n        <div className=\"flex flex-row gap-4 mt-10 justify-center\">\n          {data.testimonials.map((testimonial, index) => (\n            <button\n              onClick={() => setSelectedTestimonialIndex(index)}\n              className={classNames(\n                // Common classes\n                \"rounded-full h-3 w-3\",\n                {\n                  \"bg-gray-500\": index !== selectedTestimonialIndex,\n                  \"bg-primary-600\": index === selectedTestimonialIndex,\n                }\n              )}\n              key={testimonial.id}\n            />\n          ))}\n        </div>\n      )}\n      {/* Logos list */}\n      <div className=\"flex flex-row flex-wrap items-center gap-6 sm:gap-20 justify-center mt-10 px-6 sm:px-0 \">\n        {data.logos.map((logo) => (\n          <NextImage key={logo.id} width=\"120\" height=\"33\" media={logo.logo} />\n        ))}\n      </div>\n    </section>\n  )\n}\n\nexport default TestimonialsGroup\n"
  },
  {
    "path": "packages/starters/next-corporate/starter/components/sections.js",
    "content": "import { useRouter } from \"next/router\"\nimport Hero from \"@/components/sections/hero\"\nimport LargeVideo from \"@/components/sections/large-video\"\nimport FeatureColumnsGroup from \"@/components/sections/feature-columns-group\"\nimport FeatureRowsGroup from \"@/components/sections/feature-rows-group\"\nimport BottomActions from \"@/components/sections/bottom-actions\"\nimport TestimonialsGroup from \"@/components/sections/testimonials-group\"\nimport RichText from \"./sections/rich-text\"\nimport Pricing from \"./sections/pricing\"\nimport LeadForm from \"./sections/lead-form\"\n\n// Map Strapi sections to section components\nconst sectionComponents = {\n  ComponentSectionsHero: Hero,\n  ComponentSectionsLargeVideo: LargeVideo,\n  ComponentSectionsFeatureColumnsGroup: FeatureColumnsGroup,\n  ComponentSectionsFeatureRowsGroup: FeatureRowsGroup,\n  ComponentSectionsBottomActions: BottomActions,\n  ComponentSectionsTestimonialsGroup: TestimonialsGroup,\n  ComponentSectionsRichText: RichText,\n  ComponentSectionsPricing: Pricing,\n  ComponentSectionsLeadForm: LeadForm,\n}\n\n// Display a section individually\nconst Section = ({ sectionData }) => {\n  // Prepare the component\n  const SectionComponent = sectionComponents[sectionData.__typename]\n\n  if (!SectionComponent) {\n    return null\n  }\n\n  // Display the section\n  return <SectionComponent data={sectionData} />\n}\n\nconst PreviewModeBanner = () => {\n  const router = useRouter()\n  const exitURL = `/api/exit-preview?redirect=${encodeURIComponent(\n    router.asPath\n  )}`\n\n  return (\n    <div className=\"py-4 bg-red-600 text-red-100 font-semibold uppercase tracking-wide\">\n      <div className=\"container\">\n        Preview mode is on.{\" \"}\n        <a\n          className=\"underline\"\n          href={`/api/exit-preview?redirect=${router.asPath}`}\n        >\n          Turn off\n        </a>\n      </div>\n    </div>\n  )\n}\n\n// Display the list of sections\nconst Sections = ({ sections, preview }) => {\n  return (\n    <div className=\"flex flex-col\">\n      {/* Show a banner if preview mode is on */}\n      {preview && <PreviewModeBanner />}\n      {/* Show the actual sections */}\n      {sections.map((section) => (\n        <Section\n          sectionData={section}\n          key={`${section.__typename}${section.id}`}\n        />\n      ))}\n    </div>\n  )\n}\n\nexport default Sections\n"
  },
  {
    "path": "packages/starters/next-corporate/starter/jsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"baseUrl\": \".\",\n    \"paths\": {\n      \"@/components/*\": [\n        \"components/*\"\n      ],\n      \"@/lib/*\": [\n        \"lib/*\"\n      ],\n      \"@/styles/*\": [\n        \"styles/*\"\n      ]\n    }\n  }\n}\n"
  },
  {
    "path": "packages/starters/next-corporate/starter/next.config.js",
    "content": "module.exports = {\n  i18n: {\n    locales: ['en', 'fr'],\n    defaultLocale: 'en',\n  },\n}\n"
  },
  {
    "path": "packages/starters/next-corporate/starter/package.json",
    "content": "{\n  \"name\": \"my-next-corporate\",\n  \"version\": \"1.0.7\",\n  \"private\": true,\n  \"scripts\": {\n    \"dev\": \"next\",\n    \"develop\": \"next\",\n    \"build\": \"next build\",\n    \"start\": \"next start\",\n    \"lint\": \"next lint\",\n    \"lint:fix\": \"next lint --fix\"\n  },\n  \"dependencies\": {\n    \"@tailwindcss/typography\": \"^0.4.1\",\n    \"classnames\": \"^2.2.6\",\n    \"cookie\": \"^0.4.1\",\n    \"date-fns\": \"2.14.0\",\n    \"formik\": \"^2.2.6\",\n    \"js-cookie\": \"3.0.0-rc.4\",\n    \"next\": \"^11.0.0\",\n    \"next-seo\": \"^4.7.1\",\n    \"postcss-import\": \"^14.0.2\",\n    \"prop-types\": \"^15.7.2\",\n    \"qs\": \"^6.10.1\",\n    \"react\": \"^17.0.0\",\n    \"react-dom\": \"^17.0.0\",\n    \"react-icons\": \"^3.10.0\",\n    \"react-markdown\": \"^6.0.2\",\n    \"tailwindcss\": \"^2.2.6\",\n    \"yup\": \"^0.32.8\"\n  },\n  \"devDependencies\": {\n    \"autoprefixer\": \"^10.3.1\",\n    \"eslint\": \"^7.30.0\",\n    \"eslint-config-next\": \"^11.0.1\",\n    \"eslint-config-prettier\": \"^8.3.0\",\n    \"eslint-plugin-prettier\": \"^3.4.0\",\n    \"postcss\": \"8.3.6\",\n    \"postcss-flexbugs-fixes\": \"^5.0.2\",\n    \"postcss-nested\": \"^5.0.5\",\n    \"postcss-preset-env\": \"^6.7.0\",\n    \"prettier\": \"^2.3.1\"\n  }\n}\n"
  },
  {
    "path": "packages/starters/next-corporate/starter/pages/[[...slug]].js",
    "content": "import ErrorPage from \"next/error\"\nimport { getPageData, fetchAPI, getGlobalData } from \"utils/api\"\nimport Sections from \"@/components/sections\"\nimport Seo from \"@/components/elements/seo\"\nimport { useRouter } from \"next/router\"\nimport Layout from \"@/components/layout\"\nimport { getLocalizedPaths } from \"utils/localize\"\n\n// The file is called [[...slug]].js because we're using Next's\n// optional catch all routes feature. See the related docs:\n// https://nextjs.org/docs/routing/dynamic-routes#optional-catch-all-routes\n\nconst DynamicPage = ({ sections, metadata, preview, global, pageContext }) => {\n  const router = useRouter()\n\n  // Check if the required data was provided\n  if (!router.isFallback && !sections?.length) {\n    return <ErrorPage statusCode={404} />\n  }\n\n  // Loading screen (only possible in preview mode)\n  if (router.isFallback) {\n    return <div className=\"container\">Loading...</div>\n  }\n\n  // Merge default site SEO settings with page specific SEO settings\n  if (metadata.shareImage?.data == null) {\n    delete metadata.shareImage\n  }\n  const metadataWithDefaults = {\n    ...global.attributes.metadata,\n    ...metadata,\n  }\n\n  return (\n    <Layout global={global} pageContext={pageContext}>\n      {/* Add meta tags for SEO*/}\n      <Seo metadata={metadataWithDefaults} />\n      {/* Display content sections */}\n      <Sections sections={sections} preview={preview} />\n    </Layout>\n  )\n}\n\nexport async function getStaticPaths(context) {\n  // Get all pages from Strapi\n  const pages = await context.locales.reduce(\n    async (currentPagesPromise, locale) => {\n      const currentPages = await currentPagesPromise\n      const localePages = await fetchAPI(\"/pages\", {\n        locale,\n        fields: [\"slug\", \"locale\"],\n      })\n      return [...currentPages, ...localePages.data]\n    },\n    Promise.resolve([])\n  )\n\n  const paths = pages.map((page) => {\n    const { slug, locale } = page.attributes\n    // Decompose the slug that was saved in Strapi\n    const slugArray = !slug ? false : slug.split(\"/\")\n\n    return {\n      params: { slug: slugArray },\n      // Specify the locale to render\n      locale,\n    }\n  })\n\n  return { paths, fallback: true }\n}\n\nexport async function getStaticProps(context) {\n  const { params, locale, locales, defaultLocale, preview = null } = context\n\n  const globalLocale = await getGlobalData(locale)\n  // Fetch pages. Include drafts if preview mode is on\n  const pageData = await getPageData({\n    slug: (!params.slug ? [\"\"] : params.slug).join(\"/\"),\n    locale,\n    preview,\n  })\n\n  if (pageData == null) {\n    // Giving the page no props will trigger a 404 page\n    return { props: {} }\n  }\n\n  // We have the required page data, pass it to the page component\n  const { contentSections, metadata, localizations, slug } = pageData.attributes\n\n  const pageContext = {\n    locale,\n    locales,\n    defaultLocale,\n    slug,\n    localizations,\n  }\n\n  const localizedPaths = getLocalizedPaths(pageContext)\n\n  return {\n    props: {\n      preview,\n      sections: contentSections,\n      metadata,\n      global: globalLocale.data,\n      pageContext: {\n        ...pageContext,\n        localizedPaths,\n      },\n    },\n  }\n}\n\nexport default DynamicPage\n"
  },
  {
    "path": "packages/starters/next-corporate/starter/pages/_app.js",
    "content": "import App from \"next/app\"\nimport Head from \"next/head\"\nimport ErrorPage from \"next/error\"\nimport { useRouter } from \"next/router\"\nimport { DefaultSeo } from \"next-seo\"\nimport { getStrapiMedia } from \"utils/media\"\nimport { getGlobalData } from \"utils/api\"\nimport \"@/styles/index.css\"\n\nconst MyApp = ({ Component, pageProps }) => {\n  // Extract the data we need\n  const { global } = pageProps\n  if (global == null) {\n    return <ErrorPage statusCode={404} />\n  }\n\n  const { metadata, favicon, metaTitleSuffix } = global.attributes\n\n  return (\n    <>\n      {/* Favicon */}\n      <Head>\n        <link\n          rel=\"shortcut icon\"\n          href={getStrapiMedia(favicon.data.attributes.url)}\n        />\n      </Head>\n      {/* Global site metadata */}\n      <DefaultSeo\n        titleTemplate={`%s | ${metaTitleSuffix}`}\n        title=\"Page\"\n        description={metadata.metaDescription}\n        openGraph={{\n          images: Object.values(\n            metadata.shareImage.data.attributes.formats\n          ).map((image) => {\n            return {\n              url: getStrapiMedia(image.url),\n              width: image.width,\n              height: image.height,\n            }\n          }),\n        }}\n        twitter={{\n          cardType: metadata.twitterCardType,\n          handle: metadata.twitterUsername,\n        }}\n      />\n      {/* Display the content */}\n      <Component {...pageProps} />\n    </>\n  )\n}\n\n// getInitialProps disables automatic static optimization for pages that don't\n// have getStaticProps. So [[...slug]] pages still get SSG.\n// Hopefully we can replace this with getStaticProps once this issue is fixed:\n// https://github.com/vercel/next.js/discussions/10949\nMyApp.getInitialProps = async (appContext) => {\n  // Calls page's `getInitialProps` and fills `appProps.pageProps`\n  const appProps = await App.getInitialProps(appContext)\n  const globalLocale = await getGlobalData(appContext.router.locale)\n\n  return {\n    ...appProps,\n    pageProps: {\n      global: globalLocale,\n    },\n  }\n}\n\nexport default MyApp\n"
  },
  {
    "path": "packages/starters/next-corporate/starter/pages/_document.js",
    "content": "import Document, { Html, Head, Main, NextScript } from \"next/document\"\n\nexport default class MyDocument extends Document {\n  render() {\n    return (\n      <Html lang=\"en\">\n        <Head />\n        <body>\n          <Main />\n          <NextScript />\n        </body>\n      </Html>\n    )\n  }\n}\n"
  },
  {
    "path": "packages/starters/next-corporate/starter/pages/api/exit-preview.js",
    "content": "import { redirect } from \"next/dist/next-server/server/api-utils\"\n\nexport default async function exit(req, res) {\n  // Exit the current user from \"Preview Mode\". This function accepts no args.\n  res.clearPreviewData()\n\n  // Redirect the user back to a provided redirect path or the index page\n  res.writeHead(307, { Location: \"/\" })\n  res.end()\n}\n"
  },
  {
    "path": "packages/starters/next-corporate/starter/pages/api/preview.js",
    "content": "import { getPageData } from \"utils/api\"\nimport { parseCookies } from \"utils/parse-cookies\"\n\nconst preview = async (req, res) => {\n  // Check the secret and next parameters\n  // This secret should only be known to this API route and the CMS\n  if (req.query.secret !== (process.env.PREVIEW_SECRET || \"secret-token\")) {\n    return res.status(401).json({ message: \"Invalid token\" })\n  }\n\n  const cookies = parseCookies(req)\n  const slugArray = req.query.slug.split(\"/\")\n  // Fetch the headless CMS to check if the provided `slug` exists\n  const pageData = await getPageData({\n    locale,\n    slug: slugArray.join(\"/\"),\n    preview: true,\n  })\n\n  // If the slug doesn't exist prevent preview mode from being enabled\n  if (!pageData) {\n    return res.status(401).json({ message: \"Invalid slug\" })\n  }\n\n  // Enable Preview Mode by setting the cookies\n  res.setPreviewData({})\n\n  // Redirect to the path from the fetched post\n  // We don't redirect to req.query.slug as that might lead to open redirect vulnerabilities\n  // Prefix with locale so previews are available in all languages\n  res.writeHead(307, {\n    Location: `/${pageData.locale}/${pageData.slug}`,\n  })\n  res.end()\n}\n\nexport default preview\n\n// You can view Preview pages with URLs like this:\n// http://localhost:3000/api/preview?secret=<preview-secret>&slug=<slug>\n// where <preview-secret> is the secret token defined in your .env config\n// and where <slug> is the slug you entered in Strapi for your page\n// The slug must match the current locale\n"
  },
  {
    "path": "packages/starters/next-corporate/starter/postcss.config.js",
    "content": "module.exports = {\n  plugins: [\n    \"postcss-import\",\n    \"tailwindcss\",\n    \"postcss-flexbugs-fixes\",\n    \"postcss-nesting\",\n    \"postcss-custom-properties\",\n    \"autoprefixer\",\n  ],\n}\n"
  },
  {
    "path": "packages/starters/next-corporate/starter/public/.gitkeep",
    "content": ""
  },
  {
    "path": "packages/starters/next-corporate/starter/styles/index.css",
    "content": "@tailwind base;\n@tailwind components;\n@tailwind utilities;\n\nhtml {\n  font-size: 13px;\n  @apply text-gray-900;\n}\n\n@media screen and (min-width: 640px) {\n  html {\n    font-size: 14px;\n  }\n}\n\n@media screen and (min-width: 768px) {\n  html {\n    font-size: 15px;\n  }\n}\n\n.rich-text-hero a {\n  @apply text-blue-600 underline;\n}\n\n.rich-text-banner {\n  @apply whitespace-pre-line;\n  a {\n    @apply underline;\n  }\n}\n\n.title {\n  @apply text-4xl leading-snug font-semibold;\n}\n\n@media (min-width: 768px) {\n  .title {\n    @apply text-5xl;\n  }\n}\n\n.with-arrow:after {\n  background-image: url(data:image/svg+xml;charset=utf-8,%3Csvg%20fill%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M1%206a.5.5%200%200%200%200%201V6zM12.854.646a.5.5%200%200%200-.708.708l.708-.708zM18%206.5l.354.354a.5.5%200%200%200%200-.708L18%206.5zm-5.854%205.146a.5.5%200%200%200%20.708.708l-.708-.708zM1%207h16.5V6H1v1zm16.646-.854l-5.5%205.5.708.708%205.5-5.5-.708-.708zm-5.5-4.792l2.75%202.75.708-.708-2.75-2.75-.708.708zm2.75%202.75l2.75%202.75.708-.708-2.75-2.75-.708.708z%22%20fill%3D%22%231264A3%22%2F%3E%3C%2Fsvg%3E);\n  content: \"\";\n  width: 19px;\n  height: 13px;\n  display: inline-block;\n  margin-left: 0.5em;\n}\n"
  },
  {
    "path": "packages/starters/next-corporate/starter/tailwind.config.js",
    "content": "const { colors } = require(`tailwindcss/defaultTheme`)\n\nmodule.exports = {\n  mode: \"jit\", // see https://tailwindcss.com/docs/just-in-time-mode\n  purge: [\"./components/**/*.js\", \"./pages/**/*.js\"],\n  darkMode: false, // or \"media\" or \"class\"\n  theme: {\n    extend: {\n      colors: {\n        primary: colors.indigo,\n      },\n      container: {\n        center: true,\n        padding: {\n          DEFAULT: \"1rem\",\n          md: \"2rem\",\n        },\n      },\n    },\n    screens: {\n      sm: \"640px\",\n      md: \"768px\",\n      lg: \"1024px\",\n      xl: \"1280px\",\n    },\n  },\n  variants: {\n    extend: {},\n  },\n  plugins: [require(\"@tailwindcss/typography\")],\n}\n"
  },
  {
    "path": "packages/starters/next-corporate/starter/utils/api.js",
    "content": "import qs from \"qs\"\n\nexport function getStrapiURL(path) {\n  return `${\n    process.env.NEXT_PUBLIC_STRAPI_API_URL || \"http://localhost:1337\"\n  }${path}`\n}\n\n/**\n * Helper to make GET requests to Strapi API endpoints\n * @param {string} path Path of the API route\n * @param {Object} urlParamsObject URL params object, will be stringified\n * @param {RequestInit} options Options passed to fetch\n * @returns Parsed API call response\n */\nexport async function fetchAPI(path, urlParamsObject = {}, options = {}) {\n  // Merge default and user options\n  const mergedOptions = {\n    headers: {\n      \"Content-Type\": \"application/json\",\n    },\n    ...options,\n  }\n\n  // Build request URL\n  const queryString = qs.stringify(urlParamsObject)\n  const requestUrl = `${getStrapiURL(\n    `/api${path}${queryString ? `?${queryString}` : \"\"}`\n  )}`\n\n  // Trigger API call\n  const response = await fetch(requestUrl, mergedOptions)\n\n  // Handle response\n  if (!response.ok) {\n    console.error(response.statusText)\n    throw new Error(`An error occurred please try again`)\n  }\n  const data = await response.json()\n  return data\n}\n\n/**\n *\n * @param {Object} options\n * @param {string} options.slug The page's slug\n * @param {string} options.locale The current locale specified in router.locale\n * @param {boolean} options.preview router isPreview value\n */\nexport async function getPageData({ slug, locale, preview }) {\n  // Find the pages that match this slug\n  const gqlEndpoint = getStrapiURL(\"/graphql\")\n  const pagesRes = await fetch(gqlEndpoint, {\n    method: \"POST\",\n    headers: {\n      \"Content-Type\": \"application/json\",\n    },\n    body: JSON.stringify({\n      query: `\n        fragment FileParts on UploadFileEntityResponse {\n          data {\n            id\n            attributes {\n              alternativeText\n              width\n              height\n              mime\n              url\n              formats\n            }\n          }\n        }\n        query GetPages(\n          $slug: String!\n          $publicationState: PublicationState!\n          $locale: I18NLocaleCode!\n        ) {        \n          pages(\n            filters: { slug: { eq: $slug } }\n            publicationState: $publicationState\n            locale: $locale\n          ) {\n            data {\n              id\n              attributes {\n                locale\n                localizations {\n                  data {\n                    id\n                    attributes {\n                      locale\n                    }\n                  }\n                }\n                slug\n                metadata {\n                  metaTitle\n                  metaDescription\n                  shareImage {\n                    ...FileParts\n                  }\n                  twitterCardType\n                  twitterUsername\n                }\n                contentSections {\n                  __typename\n                  ... on ComponentSectionsBottomActions {\n                    id\n                    title\n                    buttons {\n                      id\n                      newTab\n                      text\n                      type\n                      url\n                    }\n                  }\n                  ... on ComponentSectionsHero {\n                    id\n                    buttons {\n                      id\n                      newTab\n                      text\n                      type\n                      url\n                    }\n                    title\n                    description\n                    label\n                    picture {\n                      ...FileParts\n                    }\n                  }\n                  ... on ComponentSectionsFeatureColumnsGroup {\n                    id\n                    features {\n                      id\n                      description\n                      icon {\n                        ...FileParts\n                      }\n                      title\n                    }\n                  }\n                  ... on ComponentSectionsFeatureRowsGroup {\n                    id\n                    features {\n                      id\n                      description\n                      link {\n                        id\n                        newTab\n                        text\n                        url\n                      }\n                      media {\n                        ...FileParts\n                      }\n                      title\n                    }\n                  }\n                  ... on ComponentSectionsTestimonialsGroup {\n                    id\n                    description\n                    link {\n                      id\n                      newTab\n                      text\n                      url\n                    }\n                    logos {\n                      id\n                      title\n                      logo {\n                        ...FileParts\n                      }\n                    }\n                    testimonials {\n                      id\n                      logo {\n                        ...FileParts\n                      }\n                      picture {\n                        ...FileParts\n                      }\n                      text\n                      authorName\n                      authorTitle\n                      link\n                    }\n                    title\n                  }\n                  ... on ComponentSectionsLargeVideo {\n                    id\n                    description\n                    title\n                    poster {\n                      ...FileParts\n                    }\n                    video {\n                      ...FileParts\n                    }\n                  }\n                  ... on ComponentSectionsRichText {\n                    id\n                    content\n                  }\n                  ... on ComponentSectionsPricing {\n                    id\n                    title\n                    plans {\n                      description\n                      features {\n                        id\n                        name\n                      }\n                      id\n                      isRecommended\n                      name\n                      price\n                      pricePeriod\n                    }\n                  }\n                  ... on ComponentSectionsLeadForm {\n                    id\n                    emailPlaceholder\n                    location\n                    submitButton {\n                      id\n                      text\n                      type\n                    }\n                    title\n                  }\n                }\n              }\n            }\n          }\n        }      \n      `,\n      variables: {\n        slug,\n        publicationState: preview ? \"PREVIEW\" : \"LIVE\",\n        locale,\n      },\n    }),\n  })\n\n  const pagesData = await pagesRes.json()\n  // Make sure we found something, otherwise return null\n  if (pagesData.data?.pages == null || pagesData.data.pages.length === 0) {\n    return null\n  }\n\n  // Return the first item since there should only be one result per slug\n  return pagesData.data.pages.data[0]\n}\n\n// Get site data from Strapi (metadata, navbar, footer...)\nexport async function getGlobalData(locale) {\n  const gqlEndpoint = getStrapiURL(\"/graphql\")\n  const globalRes = await fetch(gqlEndpoint, {\n    method: \"POST\",\n    headers: {\n      \"Content-Type\": \"application/json\",\n    },\n    body: JSON.stringify({\n      query: `\n        fragment FileParts on UploadFileEntityResponse {\n          data {\n            id\n            attributes {\n              alternativeText\n              width\n              height\n              mime\n              url\n              formats\n            }\n          }\n        }\n        query GetGlobal($locale: I18NLocaleCode!) {\n          global(locale: $locale) {\n            data {\n              id\n              attributes {\n                favicon {\n                  ...FileParts\n                }\n                metadata {\n                  metaTitle\n                  metaDescription\n                  shareImage {\n                    ...FileParts\n                  }\n                  twitterCardType\n                  twitterUsername\n                }\n                metaTitleSuffix\n                notificationBanner {\n                  type\n                  text\n                }\n                navbar {\n                  logo {\n                    ...FileParts\n                  }\n                  links {\n                    id\n                    url\n                    newTab\n                    text\n                  }\n                  button {\n                    id\n                    url\n                    newTab\n                    text\n                    type\n                  }\n                }\n                footer {\n                  logo {\n                    ...FileParts\n                  }\n                  smallText\n                  columns {\n                    id\n                    title\n                    links {\n                      id\n                      url\n                      newTab\n                      text\n                    }\n                  }\n                }\n              }\n            }\n          }\n        }      \n      `,\n      variables: {\n        locale,\n      },\n    }),\n  })\n\n  const global = await globalRes.json()\n  return global.data.global\n}\n"
  },
  {
    "path": "packages/starters/next-corporate/starter/utils/button.js",
    "content": "// Decide what the button will look like based on its type (primary or secondary)\n// and on its background (light or dark).\nexport function getButtonAppearance(type, background) {\n  if (type === 'primary') {\n    if (background === 'light') {\n      // Dark primary button on a light background\n      return 'dark'\n    }\n    // Fully white primary button on a dark background\n    return 'white'\n  }\n  if (type === 'secondary') {\n    if (background === 'light') {\n      // Dark outline primary button on a light background\n      return 'dark-outline'\n    }\n    // White outline primary button on a dark background\n    return 'white-outline'\n  }\n\n  // Shouldn't happen, but default to dark button just in case\n  return 'dark'\n}\n"
  },
  {
    "path": "packages/starters/next-corporate/starter/utils/hooks.js",
    "content": "import { useEffect } from 'react'\n\n// Got from https://usehooks.com/useLockBodyScroll/\nexport function useLockBodyScroll() {\n  useEffect(() => {\n    // Get original body overflow\n    const originalStyle = window.getComputedStyle(document.body).overflow\n\n    // Prevent scrolling on mount\n    document.body.style.overflow = 'hidden'\n\n    // Re-enable scrolling when component unmounts\n    return () => (document.body.style.overflow = originalStyle)\n  }, []) // Empty array ensures effect is only run on mount and unmount\n}\n\nexport function useOnClickOutside(ref, handler) {\n  useEffect(() => {\n    const listener = (event) => {\n      // Do nothing if clicking ref's element or descendent elements\n      if (!ref.current || ref.current.contains(event.target)) {\n        return\n      }\n\n      handler(event)\n    }\n\n    document.addEventListener('mousedown', listener)\n    document.addEventListener('touchstart', listener)\n\n    return () => {\n      document.removeEventListener('mousedown', listener)\n      document.removeEventListener('touchstart', listener)\n    }\n  }, [ref, handler])\n}\n"
  },
  {
    "path": "packages/starters/next-corporate/starter/utils/localize.js",
    "content": "import { fetchAPI } from \"./api\"\n\nexport async function getLocalizedPage(targetLocale, pageContext) {\n  const localization = pageContext.localizations.data.find(\n    (localization) => localization.attributes.locale === targetLocale\n  )\n  const localePage = await fetchAPI(`/pages/${localization.id}`)\n  return localePage\n}\n\nexport function localizePath(page) {\n  const { locale, defaultLocale, slug } = page\n\n  if (locale === defaultLocale) {\n    // The default locale is not prefixed\n    return `/${slug}`\n  }\n\n  // The slug should have a localePrefix\n  return `/${locale}/${slug}`\n}\n\nexport function getLocalizedPaths(page) {\n  const paths = page.locales.map((locale) => {\n    return {\n      locale: locale,\n      href: localizePath({ ...page, locale }),\n    }\n  })\n\n  return paths\n}\n"
  },
  {
    "path": "packages/starters/next-corporate/starter/utils/media.js",
    "content": "export function getStrapiMedia(url) {\n  if (url == null) {\n    return null\n  }\n\n  // Return the full URL if the media is hosted on an external provider\n  if (url.startsWith(\"http\") || url.startsWith(\"//\")) {\n    return url\n  }\n\n  // Otherwise prepend the URL path with the Strapi URL\n  return `${\n    process.env.NEXT_PUBLIC_STRAPI_API_URL || \"http://localhost:1337\"\n  }${url}`\n}\n"
  },
  {
    "path": "packages/starters/next-corporate/starter/utils/parse-cookies.js",
    "content": "import cookie from \"cookie\"\n\nexport function parseCookies(req) {\n  return cookie.parse(req ? req.headers.cookie || '' : document.cookie)\n}\n"
  },
  {
    "path": "packages/starters/next-corporate/starter/utils/types.js",
    "content": "import PropTypes from \"prop-types\"\n\nexport const linkPropTypes = PropTypes.shape({\n  id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,\n  url: PropTypes.string.isRequired,\n  text: PropTypes.string.isRequired,\n  newTab: PropTypes.bool,\n})\n\nexport const mediaPropTypes = PropTypes.shape({\n  data: PropTypes.shape({\n    id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,\n    attributes: PropTypes.shape({\n      alternativeText: PropTypes.string,\n      mime: PropTypes.string.isRequired,\n      url: PropTypes.string.isRequired,\n    }),\n  }),\n})\n\nexport const buttonLinkPropTypes = PropTypes.shape({\n  theme: PropTypes.string,\n  text: PropTypes.string.isRequired,\n  newTab: PropTypes.bool,\n})\n"
  },
  {
    "path": "packages/starters/next-corporate/starter.json",
    "content": "{\n  \"template\": {\n    \"name\": \"@strapi/template-corporate\",\n    \"version\": \"^1.0.0\"\n  }\n}\n"
  },
  {
    "path": "packages/templates/.gitkeep",
    "content": ""
  },
  {
    "path": "packages/templates/blog/README.md",
    "content": "# strapi-template-blog\n\nA Strapi template to create Strapi projects pre-configured for blog apps.\n\n## Usage\n\n```bash\n# Using Yarn\nyarn create strapi-app my-app-name --template blog\n\n# Or using NPM\nnpx create-strapi-app my-app-name --template blog\n```\n\n## Starters\n\nThis template is used by the following starters:\n\n* [strapi-starter-next-blog](https://github.com/strapi/strapi-starter-next-blog)\n* [strapi-starter-nuxt-blog](https://github.com/strapi/strapi-starter-nuxt-blog)\n* [strapi-starter-gatsby-blog](https://github.com/strapi/strapi-starter-gatsby-blog)\n* [strapi-starter-gridsome-blog](https://github.com/strapi/strapi-starter-gridsome-blog)\n"
  },
  {
    "path": "packages/templates/blog/package.json",
    "content": "{\n  \"name\": \"@strapi/template-blog\",\n  \"version\": \"2.0.5\",\n  \"description\": \"Strapi blog template\",\n  \"keywords\": [\n    \"strapi\",\n    \"template\",\n    \"blog\"\n  ],\n  \"homepage\": \"https://github.com/strapi/starters-and-templates#readme\",\n  \"bugs\": {\n    \"url\": \"https://github.com/strapi/starters-and-templates/issues\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/strapi/starters-and-templates.git\"\n  },\n  \"license\": \"MIT\",\n  \"author\": {\n    \"name\": \"Strapi team\",\n    \"email\": \"hi@strapi.io\",\n    \"url\": \"https://strapi.io\"\n  },\n  \"maintainers\": [\n    {\n      \"name\": \"Strapi team\",\n      \"email\": \"hi@strapi.io\",\n      \"url\": \"https://strapi.io\"\n    }\n  ],\n  \"gitHead\": \"d6d5bd264d1df0a0f23b5234d84348f8d78882e3\"\n}\n"
  },
  {
    "path": "packages/templates/blog/template/data/data.json",
    "content": "{\n  \"global\": {\n    \"siteName\": \"Strapi Blog\",\n    \"defaultSeo\": {\n      \"metaTitle\": \"Page\",\n      \"metaDescription\": \"A blog made with Strapi\",\n      \"shareImage\": null\n    },\n    \"siteDescription\": \"A Blog made with Strapi\",\n    \"favicon\": null\n  },\n  \"about\": {\n    \"title\": \"About the strapi blog\",\n    \"blocks\": [\n      {\n        \"__component\": \"shared.quote\",\n        \"title\": \"Thelonius Monk\",\n        \"body\": \"You've got to dig it to dig it, you dig?\"\n      },\n      {\n        \"__component\": \"shared.rich-text\",\n        \"body\": \"## Dedit imago conspicuus cum capillis totidem inhibere\\n\\nLorem markdownum **rerum**, est limine: columbas: ab infelix hostem arbore nudis\\ncrudelis. Videtur reliquit ambo ferrum dote sub amne fatis **illuc**, in magis,\\nnec.\"\n      },\n      {\n        \"__component\": \"shared.media\",\n        \"file\": \"coffee-art.jpg\"\n      }\n    ]\n  },\n  \"categories\": [\n    {\n      \"name\": \"news\",\n      \"slug\": \"news\"\n    },\n    {\n      \"name\": \"tech\",\n      \"slug\": \"tech\"\n    },\n    {\n      \"name\": \"food\",\n      \"slug\": \"food\"\n    },\n    {\n      \"name\": \"nature\",\n      \"slug\": \"nature\"\n    },\n    {\n      \"name\": \"story\",\n      \"slug\": \"story\"\n    }\n  ],\n  \"authors\": [\n    {\n      \"name\": \"David Doe\",\n      \"email\": \"daviddoe@strapi.io\",\n      \"avatar\": \"daviddoe@strapi.io.jpg\"\n    },\n    {\n      \"name\": \"Sarah Baker\",\n      \"email\": \"sarahbaker@strapi.io\",\n      \"avatar\": \"sarahbaker@strapi.io.jpg\"\n    }\n  ],\n  \"articles\": [\n    {\n      \"title\": \"The internet's Own boy\",\n      \"slug\": \"the-internet-s-own-boy\",\n      \"category\": {\n        \"id\": 5\n      },\n      \"author\": {\n        \"id\": 1\n      },\n      \"description\": \"Follow the story of Aaron Swartz, the boy who could change the world\",\n      \"cover\": null,\n      \"blocks\": [\n        {\n          \"__component\": \"shared.rich-text\",\n          \"body\": \"## Probant \\n\\nse Lorem markdownum negat. Argo *saxa* videnda cornuaque hunc qui tanta spes teneas! Obliquis est dicenti est salutat ille tamen iuvenum nostrae dolore. - Colores nocituraque comitata eripiunt - Addit quodcunque solum cui et dextram illis - Nulli meus nec extemplo ille ferebat pressit Se blandita fulvae vox gravem Pittheus cesserunt sanguine herbis tu comitum tenuit. Sui in ruunt; Doridaque maculosae fuissem! Et loqui. \\n\\n## Abit sua\\n\\nse Lorem markdownum negat. Argo *saxa* videnda cornuaque hunc qui tanta spes teneas! Obliquis est dicenti est salutat ille tamen iuvenum nostrae dolore. - Colores nocituraque comitata eripiunt - Addit quodcunque solum cui et dextram illis - Nulli meus nec extemplo ille ferebat pressit Se blandita fulvae vox gravem Pittheus cesserunt sanguine herbis tu comitum tenuit. Sui in ruunt; Doridaque maculosae fuissem! Et loqui. \"\n        },\n        {\n          \"__component\": \"shared.quote\",\n          \"title\": \"Thelonius Monk\",\n          \"body\": \"You've got to dig it to dig it, you dig?\"\n        },\n        {\n          \"__component\": \"shared.media\",\n          \"file\": \"coffee-art.jpg\"\n        },\n        {\n          \"__component\": \"shared.rich-text\",\n          \"body\": \"## Spatiantia astra \\n\\nFoeda, medio silva *errandum*: onus formam munere. Mutata bibulis est auxiliare arces etiamnunc verbis virgineo Priamidas illa Thescelus, nam fit locis lucis auras. Exitus hospes gratulor ut pondere [speslimite](http://www.curas.io/figuram); quid habent, Avernales faciente de. Pervenit Ino sonabile supplex cognoscenti vires, Bacchumque errat miserarum venandi dignabere dedisti. Discrimina iuncosaque virgaque tot sine superest [fissus](http://quos.org/sitet.aspx). Non color esset potest non sumit, sed vix arserat. Nisi immo silva tantum pectusque quos pennis quisquam artus!\"\n        },\n        {\n          \"__component\": \"shared.slider\",\n          \"files\": [\n            \"coffee-art.jpg\",\n            \"coffee-beans.jpg\"\n          ]\n        }\n      ]\n    },\n    {\n      \"title\": \"This shrimp is awesome\",\n      \"slug\": \"this-shrimp-is-awesome\",\n      \"category\": {\n        \"id\": 4\n      },\n      \"author\": {\n        \"id\": 1\n      },\n      \"description\": \"Mantis shrimps, or stomatopods, are marine crustaceans of the order Stomatopoda.\",\n      \"cover\": null,\n      \"blocks\": [\n        {\n          \"__component\": \"shared.rich-text\",\n          \"body\": \"## Probant \\n\\nse Lorem markdownum negat. Argo *saxa* videnda cornuaque hunc qui tanta spes teneas! Obliquis est dicenti est salutat ille tamen iuvenum nostrae dolore. - Colores nocituraque comitata eripiunt - Addit quodcunque solum cui et dextram illis - Nulli meus nec extemplo ille ferebat pressit Se blandita fulvae vox gravem Pittheus cesserunt sanguine herbis tu comitum tenuit. Sui in ruunt; Doridaque maculosae fuissem! Et loqui. \\n\\n## Abit sua\\n\\nse Lorem markdownum negat. Argo *saxa* videnda cornuaque hunc qui tanta spes teneas! Obliquis est dicenti est salutat ille tamen iuvenum nostrae dolore. - Colores nocituraque comitata eripiunt - Addit quodcunque solum cui et dextram illis - Nulli meus nec extemplo ille ferebat pressit Se blandita fulvae vox gravem Pittheus cesserunt sanguine herbis tu comitum tenuit. Sui in ruunt; Doridaque maculosae fuissem! Et loqui. \"\n        },\n        {\n          \"__component\": \"shared.quote\",\n          \"title\": \"Thelonius Monk\",\n          \"body\": \"You've got to dig it to dig it, you dig?\"\n        },\n        {\n          \"__component\": \"shared.media\",\n          \"file\": \"coffee-art.jpg\"\n        },\n        {\n          \"__component\": \"shared.rich-text\",\n          \"body\": \"## Spatiantia astra \\n\\nFoeda, medio silva *errandum*: onus formam munere. Mutata bibulis est auxiliare arces etiamnunc verbis virgineo Priamidas illa Thescelus, nam fit locis lucis auras. Exitus hospes gratulor ut pondere [speslimite](http://www.curas.io/figuram); quid habent, Avernales faciente de. Pervenit Ino sonabile supplex cognoscenti vires, Bacchumque errat miserarum venandi dignabere dedisti. Discrimina iuncosaque virgaque tot sine superest [fissus](http://quos.org/sitet.aspx). Non color esset potest non sumit, sed vix arserat. Nisi immo silva tantum pectusque quos pennis quisquam artus!\"\n        },\n        {\n          \"__component\": \"shared.slider\",\n          \"files\": [\n            \"coffee-art.jpg\",\n            \"coffee-beans.jpg\"\n          ]\n        }\n      ]\n    },\n    {\n      \"title\": \"A bug is becoming a meme on the internet\",\n      \"slug\": \"a-bug-is-becoming-a-meme-on-the-internet\",\n      \"category\": {\n        \"id\": 2\n      },\n      \"author\": {\n        \"id\": 2\n      },\n      \"description\": \"How a bug on MySQL is becoming a meme on the internet\",\n      \"cover\": null,\n      \"blocks\": [\n        {\n          \"__component\": \"shared.rich-text\",\n          \"body\": \"## Probant \\n\\nse Lorem markdownum negat. Argo *saxa* videnda cornuaque hunc qui tanta spes teneas! Obliquis est dicenti est salutat ille tamen iuvenum nostrae dolore. - Colores nocituraque comitata eripiunt - Addit quodcunque solum cui et dextram illis - Nulli meus nec extemplo ille ferebat pressit Se blandita fulvae vox gravem Pittheus cesserunt sanguine herbis tu comitum tenuit. Sui in ruunt; Doridaque maculosae fuissem! Et loqui. \\n\\n## Abit sua\\n\\nse Lorem markdownum negat. Argo *saxa* videnda cornuaque hunc qui tanta spes teneas! Obliquis est dicenti est salutat ille tamen iuvenum nostrae dolore. - Colores nocituraque comitata eripiunt - Addit quodcunque solum cui et dextram illis - Nulli meus nec extemplo ille ferebat pressit Se blandita fulvae vox gravem Pittheus cesserunt sanguine herbis tu comitum tenuit. Sui in ruunt; Doridaque maculosae fuissem! Et loqui. \"\n        },\n        {\n          \"__component\": \"shared.quote\",\n          \"title\": \"Thelonius Monk\",\n          \"body\": \"You've got to dig it to dig it, you dig?\"\n        },\n        {\n          \"__component\": \"shared.media\",\n          \"file\": \"coffee-art.jpg\"\n        },\n        {\n          \"__component\": \"shared.rich-text\",\n          \"body\": \"## Spatiantia astra \\n\\nFoeda, medio silva *errandum*: onus formam munere. Mutata bibulis est auxiliare arces etiamnunc verbis virgineo Priamidas illa Thescelus, nam fit locis lucis auras. Exitus hospes gratulor ut pondere [speslimite](http://www.curas.io/figuram); quid habent, Avernales faciente de. Pervenit Ino sonabile supplex cognoscenti vires, Bacchumque errat miserarum venandi dignabere dedisti. Discrimina iuncosaque virgaque tot sine superest [fissus](http://quos.org/sitet.aspx). Non color esset potest non sumit, sed vix arserat. Nisi immo silva tantum pectusque quos pennis quisquam artus!\"\n        },\n        {\n          \"__component\": \"shared.slider\",\n          \"files\": [\n            \"coffee-art.jpg\",\n            \"coffee-beans.jpg\"\n          ]\n        }\n      ]\n    },\n    {\n      \"title\": \"Beautiful picture\",\n      \"slug\": \"beautiful-picture\",\n      \"category\": {\n        \"id\": 4\n      },\n      \"author\": {\n        \"id\": 2\n      },\n      \"description\": \"Description of a beautiful picture\",\n      \"cover\": null,\n      \"blocks\": [\n        {\n          \"__component\": \"shared.rich-text\",\n          \"body\": \"## Probant \\n\\nse Lorem markdownum negat. Argo *saxa* videnda cornuaque hunc qui tanta spes teneas! Obliquis est dicenti est salutat ille tamen iuvenum nostrae dolore. - Colores nocituraque comitata eripiunt - Addit quodcunque solum cui et dextram illis - Nulli meus nec extemplo ille ferebat pressit Se blandita fulvae vox gravem Pittheus cesserunt sanguine herbis tu comitum tenuit. Sui in ruunt; Doridaque maculosae fuissem! Et loqui. \\n\\n## Abit sua\\n\\nse Lorem markdownum negat. Argo *saxa* videnda cornuaque hunc qui tanta spes teneas! Obliquis est dicenti est salutat ille tamen iuvenum nostrae dolore. - Colores nocituraque comitata eripiunt - Addit quodcunque solum cui et dextram illis - Nulli meus nec extemplo ille ferebat pressit Se blandita fulvae vox gravem Pittheus cesserunt sanguine herbis tu comitum tenuit. Sui in ruunt; Doridaque maculosae fuissem! Et loqui. \"\n        },\n        {\n          \"__component\": \"shared.quote\",\n          \"title\": \"Thelonius Monk\",\n          \"body\": \"You've got to dig it to dig it, you dig?\"\n        },\n        {\n          \"__component\": \"shared.media\",\n          \"file\": \"coffee-art.jpg\"\n        },\n        {\n          \"__component\": \"shared.rich-text\",\n          \"body\": \"## Spatiantia astra \\n\\nFoeda, medio silva *errandum*: onus formam munere. Mutata bibulis est auxiliare arces etiamnunc verbis virgineo Priamidas illa Thescelus, nam fit locis lucis auras. Exitus hospes gratulor ut pondere [speslimite](http://www.curas.io/figuram); quid habent, Avernales faciente de. Pervenit Ino sonabile supplex cognoscenti vires, Bacchumque errat miserarum venandi dignabere dedisti. Discrimina iuncosaque virgaque tot sine superest [fissus](http://quos.org/sitet.aspx). Non color esset potest non sumit, sed vix arserat. Nisi immo silva tantum pectusque quos pennis quisquam artus!\"\n        },\n        {\n          \"__component\": \"shared.slider\",\n          \"files\": [\n            \"coffee-art.jpg\",\n            \"coffee-beans.jpg\"\n          ]\n        }\n      ]\n    },\n    {\n      \"title\": \"What's inside a Black Hole\",\n      \"slug\": \"what-s-inside-a-black-hole\",\n      \"category\": {\n        \"id\": 1\n      },\n      \"author\": {\n        \"id\": 2\n      },\n      \"description\": \"Maybe the answer is in this article, or not...\",\n      \"cover\": null,\n      \"blocks\": [\n        {\n          \"__component\": \"shared.rich-text\",\n          \"body\": \"## Probant \\n\\nse Lorem markdownum negat. Argo *saxa* videnda cornuaque hunc qui tanta spes teneas! Obliquis est dicenti est salutat ille tamen iuvenum nostrae dolore. - Colores nocituraque comitata eripiunt - Addit quodcunque solum cui et dextram illis - Nulli meus nec extemplo ille ferebat pressit Se blandita fulvae vox gravem Pittheus cesserunt sanguine herbis tu comitum tenuit. Sui in ruunt; Doridaque maculosae fuissem! Et loqui. \\n\\n## Abit sua\\n\\nse Lorem markdownum negat. Argo *saxa* videnda cornuaque hunc qui tanta spes teneas! Obliquis est dicenti est salutat ille tamen iuvenum nostrae dolore. - Colores nocituraque comitata eripiunt - Addit quodcunque solum cui et dextram illis - Nulli meus nec extemplo ille ferebat pressit Se blandita fulvae vox gravem Pittheus cesserunt sanguine herbis tu comitum tenuit. Sui in ruunt; Doridaque maculosae fuissem! Et loqui. \"\n        },\n        {\n          \"__component\": \"shared.quote\",\n          \"title\": \"Thelonius Monk\",\n          \"body\": \"You've got to dig it to dig it, you dig?\"\n        },\n        {\n          \"__component\": \"shared.media\",\n          \"file\": \"coffee-art.jpg\"\n        },\n        {\n          \"__component\": \"shared.rich-text\",\n          \"body\": \"## Spatiantia astra \\n\\nFoeda, medio silva *errandum*: onus formam munere. Mutata bibulis est auxiliare arces etiamnunc verbis virgineo Priamidas illa Thescelus, nam fit locis lucis auras. Exitus hospes gratulor ut pondere [speslimite](http://www.curas.io/figuram); quid habent, Avernales faciente de. Pervenit Ino sonabile supplex cognoscenti vires, Bacchumque errat miserarum venandi dignabere dedisti. Discrimina iuncosaque virgaque tot sine superest [fissus](http://quos.org/sitet.aspx). Non color esset potest non sumit, sed vix arserat. Nisi immo silva tantum pectusque quos pennis quisquam artus!\"\n        },\n        {\n          \"__component\": \"shared.slider\",\n          \"files\": [\n            \"coffee-art.jpg\",\n            \"coffee-beans.jpg\"\n          ]\n        }\n      ]\n    }\n  ]\n}\n"
  },
  {
    "path": "packages/templates/blog/template/src/api/.gitkeep",
    "content": ""
  },
  {
    "path": "packages/templates/blog/template/src/api/about/content-types/about/schema.json",
    "content": "{\n  \"kind\": \"singleType\",\n  \"collectionName\": \"abouts\",\n  \"info\": {\n    \"singularName\": \"about\",\n    \"pluralName\": \"abouts\",\n    \"displayName\": \"About\",\n    \"description\": \"Write about yourself and the content you create\"\n  },\n  \"options\": {\n    \"draftAndPublish\": false\n  },\n  \"pluginOptions\": {},\n  \"attributes\": {\n    \"title\": {\n      \"type\": \"string\"\n    },\n    \"blocks\": {\n      \"type\": \"dynamiczone\",\n      \"components\": [\n        \"shared.media\",\n        \"shared.quote\",\n        \"shared.rich-text\",\n        \"shared.slider\"\n      ]\n    }\n  }\n}\n"
  },
  {
    "path": "packages/templates/blog/template/src/api/about/controllers/about.js",
    "content": "'use strict';\n\n/**\n *  about controller\n */\n\nconst { createCoreController } = require('@strapi/strapi').factories;\n\nmodule.exports = createCoreController('api::about.about');\n"
  },
  {
    "path": "packages/templates/blog/template/src/api/about/routes/about.js",
    "content": "'use strict';\n\n/**\n * about router.\n */\n\nconst { createCoreRouter } = require('@strapi/strapi').factories;\n\nmodule.exports = createCoreRouter('api::about.about');\n"
  },
  {
    "path": "packages/templates/blog/template/src/api/about/services/about.js",
    "content": "'use strict';\n\n/**\n * about service.\n */\n\nconst { createCoreService } = require('@strapi/strapi').factories;\n\nmodule.exports = createCoreService('api::about.about');\n"
  },
  {
    "path": "packages/templates/blog/template/src/api/article/content-types/article/schema.json",
    "content": "{\n  \"kind\": \"collectionType\",\n  \"collectionName\": \"articles\",\n  \"info\": {\n    \"singularName\": \"article\",\n    \"pluralName\": \"articles\",\n    \"displayName\": \"Article\",\n    \"description\": \"Create your blog content\"\n  },\n  \"options\": {\n    \"draftAndPublish\": true\n  },\n  \"pluginOptions\": {},\n  \"attributes\": {\n    \"title\": {\n      \"type\": \"string\"\n    },\n    \"description\": {\n      \"type\": \"text\",\n      \"maxLength\": 80\n    },\n    \"slug\": {\n      \"type\": \"uid\",\n      \"targetField\": \"title\"\n    },\n    \"cover\": {\n      \"type\": \"media\",\n      \"multiple\": false,\n      \"required\": false,\n      \"allowedTypes\": [\n        \"images\",\n        \"files\",\n        \"videos\"\n      ]\n    },\n    \"author\": {\n      \"type\": \"relation\",\n      \"relation\": \"manyToOne\",\n      \"target\": \"api::author.author\",\n      \"inversedBy\": \"articles\"\n    },\n    \"category\": {\n      \"type\": \"relation\",\n      \"relation\": \"manyToOne\",\n      \"target\": \"api::category.category\",\n      \"inversedBy\": \"articles\"\n    },\n    \"blocks\": {\n      \"type\": \"dynamiczone\",\n      \"components\": [\n        \"shared.media\",\n        \"shared.quote\",\n        \"shared.rich-text\",\n        \"shared.slider\"\n      ]\n    }\n  }\n}\n"
  },
  {
    "path": "packages/templates/blog/template/src/api/article/controllers/article.js",
    "content": "'use strict';\n\n/**\n *  article controller\n */\n\nconst { createCoreController } = require('@strapi/strapi').factories;\n\nmodule.exports = createCoreController('api::article.article');\n"
  },
  {
    "path": "packages/templates/blog/template/src/api/article/routes/article.js",
    "content": "'use strict';\n\n/**\n * article router.\n */\n\nconst { createCoreRouter } = require('@strapi/strapi').factories;\n\nmodule.exports = createCoreRouter('api::article.article');\n"
  },
  {
    "path": "packages/templates/blog/template/src/api/article/services/article.js",
    "content": "'use strict';\n\n/**\n * article service.\n */\n\nconst { createCoreService } = require('@strapi/strapi').factories;\n\nmodule.exports = createCoreService('api::article.article');\n"
  },
  {
    "path": "packages/templates/blog/template/src/api/author/content-types/author/schema.json",
    "content": "{\n  \"kind\": \"collectionType\",\n  \"collectionName\": \"authors\",\n  \"info\": {\n    \"singularName\": \"author\",\n    \"pluralName\": \"authors\",\n    \"displayName\": \"Author\",\n    \"description\": \"Create authors for your content\"\n  },\n  \"options\": {\n    \"draftAndPublish\": false\n  },\n  \"pluginOptions\": {},\n  \"attributes\": {\n    \"name\": {\n      \"type\": \"string\"\n    },\n    \"avatar\": {\n      \"type\": \"media\",\n      \"multiple\": false,\n      \"required\": false,\n      \"allowedTypes\": [\n        \"images\",\n        \"files\",\n        \"videos\"\n      ]\n    },\n    \"email\": {\n      \"type\": \"string\"\n    },\n    \"articles\": {\n      \"type\": \"relation\",\n      \"relation\": \"oneToMany\",\n      \"target\": \"api::article.article\",\n      \"mappedBy\": \"author\"\n    }\n  }\n}\n"
  },
  {
    "path": "packages/templates/blog/template/src/api/author/controllers/author.js",
    "content": "'use strict';\n\n/**\n *  author controller\n */\n\nconst { createCoreController } = require('@strapi/strapi').factories;\n\nmodule.exports = createCoreController('api::author.author');\n"
  },
  {
    "path": "packages/templates/blog/template/src/api/author/routes/author.js",
    "content": "'use strict';\n\n/**\n * author router.\n */\n\nconst { createCoreRouter } = require('@strapi/strapi').factories;\n\nmodule.exports = createCoreRouter('api::author.author');\n"
  },
  {
    "path": "packages/templates/blog/template/src/api/author/services/author.js",
    "content": "'use strict';\n\n/**\n * author service.\n */\n\nconst { createCoreService } = require('@strapi/strapi').factories;\n\nmodule.exports = createCoreService('api::author.author');\n"
  },
  {
    "path": "packages/templates/blog/template/src/api/category/content-types/category/schema.json",
    "content": "{\n  \"kind\": \"collectionType\",\n  \"collectionName\": \"categories\",\n  \"info\": {\n    \"singularName\": \"category\",\n    \"pluralName\": \"categories\",\n    \"displayName\": \"Category\",\n    \"description\": \"Organize your content into categories\"\n  },\n  \"options\": {\n    \"draftAndPublish\": false\n  },\n  \"pluginOptions\": {},\n  \"attributes\": {\n    \"name\": {\n      \"type\": \"string\"\n    },\n    \"slug\": {\n      \"type\": \"uid\"\n    },\n    \"articles\": {\n      \"type\": \"relation\",\n      \"relation\": \"oneToMany\",\n      \"target\": \"api::article.article\",\n      \"mappedBy\": \"category\"\n    },\n    \"description\": {\n      \"type\": \"text\"\n    }\n  }\n}\n"
  },
  {
    "path": "packages/templates/blog/template/src/api/category/controllers/category.js",
    "content": "'use strict';\n\n/**\n *  category controller\n */\n\nconst { createCoreController } = require('@strapi/strapi').factories;\n\nmodule.exports = createCoreController('api::category.category');\n"
  },
  {
    "path": "packages/templates/blog/template/src/api/category/routes/category.js",
    "content": "'use strict';\n\n/**\n * category router.\n */\n\nconst { createCoreRouter } = require('@strapi/strapi').factories;\n\nmodule.exports = createCoreRouter('api::category.category');\n"
  },
  {
    "path": "packages/templates/blog/template/src/api/category/services/category.js",
    "content": "'use strict';\n\n/**\n * category service.\n */\n\nconst { createCoreService } = require('@strapi/strapi').factories;\n\nmodule.exports = createCoreService('api::category.category');\n"
  },
  {
    "path": "packages/templates/blog/template/src/api/global/content-types/global/schema.json",
    "content": "{\n  \"kind\": \"singleType\",\n  \"collectionName\": \"globals\",\n  \"info\": {\n    \"singularName\": \"global\",\n    \"pluralName\": \"globals\",\n    \"displayName\": \"Global\",\n    \"description\": \"Define global settings\"\n  },\n  \"options\": {\n    \"draftAndPublish\": false\n  },\n  \"pluginOptions\": {},\n  \"attributes\": {\n    \"siteName\": {\n      \"type\": \"string\",\n      \"required\": true\n    },\n    \"favicon\": {\n      \"type\": \"media\",\n      \"multiple\": false,\n      \"required\": false,\n      \"allowedTypes\": [\n        \"images\",\n        \"files\",\n        \"videos\"\n      ]\n    },\n    \"siteDescription\": {\n      \"type\": \"text\",\n      \"required\": true\n    },\n    \"defaultSeo\": {\n      \"type\": \"component\",\n      \"repeatable\": false,\n      \"component\": \"shared.seo\"\n    }\n  }\n}\n"
  },
  {
    "path": "packages/templates/blog/template/src/api/global/controllers/global.js",
    "content": "'use strict';\n\n/**\n *  global controller\n */\n\nconst { createCoreController } = require('@strapi/strapi').factories;\n\nmodule.exports = createCoreController('api::global.global');\n"
  },
  {
    "path": "packages/templates/blog/template/src/api/global/routes/global.js",
    "content": "'use strict';\n\n/**\n * global router.\n */\n\nconst { createCoreRouter } = require('@strapi/strapi').factories;\n\nmodule.exports = createCoreRouter('api::global.global');\n"
  },
  {
    "path": "packages/templates/blog/template/src/api/global/services/global.js",
    "content": "'use strict';\n\n/**\n * global service.\n */\n\nconst { createCoreService } = require('@strapi/strapi').factories;\n\nmodule.exports = createCoreService('api::global.global');\n"
  },
  {
    "path": "packages/templates/blog/template/src/bootstrap.js",
    "content": "\"use strict\";\n\nconst fs = require(\"fs-extra\");\nconst path = require(\"path\");\nconst mime = require(\"mime-types\");\nconst set = require(\"lodash.set\");\nconst {\n  categories,\n  authors,\n  articles,\n  global,\n  about,\n} = require(\"../data/data.json\");\n\nasync function isFirstRun() {\n  const pluginStore = strapi.store({\n    environment: strapi.config.environment,\n    type: \"type\",\n    name: \"setup\",\n  });\n  const initHasRun = await pluginStore.get({ key: \"initHasRun\" });\n  await pluginStore.set({ key: \"initHasRun\", value: true });\n  return !initHasRun;\n}\n\nasync function setPublicPermissions(newPermissions) {\n  // Find the ID of the public role\n  const publicRole = await strapi\n    .query(\"plugin::users-permissions.role\")\n    .findOne({\n      where: {\n        type: \"public\",\n      },\n    });\n\n  // Create the new permissions and link them to the public role\n  const allPermissionsToCreate = [];\n  Object.keys(newPermissions).map((controller) => {\n    const actions = newPermissions[controller];\n    const permissionsToCreate = actions.map((action) => {\n      return strapi.query(\"plugin::users-permissions.permission\").create({\n        data: {\n          action: `api::${controller}.${controller}.${action}`,\n          role: publicRole.id,\n        },\n      });\n    });\n    allPermissionsToCreate.push(...permissionsToCreate);\n  });\n  await Promise.all(allPermissionsToCreate);\n}\n\nfunction getFileSizeInBytes(filePath) {\n  const stats = fs.statSync(filePath);\n  const fileSizeInBytes = stats[\"size\"];\n  return fileSizeInBytes;\n}\n\nfunction getFileData(fileName) {\n  const filePath = path.join(\"data\", \"uploads\", fileName);\n  // Parse the file metadata\n  const size = getFileSizeInBytes(filePath);\n  const ext = fileName.split(\".\").pop();\n  const mimeType = mime.lookup(ext);\n\n  return {\n    path: filePath,\n    name: fileName,\n    size,\n    type: mimeType,\n  };\n}\n\nasync function uploadFile(file, name) {\n  return strapi\n    .plugin(\"upload\")\n    .service(\"upload\")\n    .upload({\n      files: file,\n      data: {\n        fileInfo: {\n          alternativeText: `An image uploaded to Strapi called ${name}`,\n          caption: name,\n          name,\n        },\n      },\n    });\n}\n\n// Create an entry and attach files if there are any\nasync function createEntry({ model, entry }) {\n  try {\n    // Actually create the entry in Strapi\n    await strapi.entityService.create(`api::${model}.${model}`, {\n      data: entry,\n    });\n  } catch (error) {\n    console.error({ model, entry, error });\n  }\n}\n\nasync function checkFileExistsBeforeUpload(files) {\n  const existingFiles = [];\n  const uploadedFiles = [];\n  const filesCopy = [...files];\n\n  for (const fileName of filesCopy) {\n    // Check if the file already exists in Strapi\n    const fileWhereName = await strapi.query(\"plugin::upload.file\").findOne({\n      where: {\n        name: fileName,\n      },\n    });\n\n    if (fileWhereName) {\n      // File exists, don't upload it\n      existingFiles.push(fileWhereName);\n    } else {\n      // File doesn't exist, upload it\n      const fileData = getFileData(fileName);\n      const fileNameNoExtension = fileName.split('.').shift()\n      const [file] = await uploadFile(fileData, fileNameNoExtension);\n      uploadedFiles.push(file);\n    }\n  }\n  const allFiles = [...existingFiles, ...uploadedFiles];\n  // If only one file then return only that file\n  return allFiles.length === 1 ? allFiles[0] : allFiles;\n}\n\nasync function updateBlocks(blocks) {\n  const updatedBlocks = [];\n  for (const block of blocks) {\n    if (block.__component === \"shared.media\") {\n      const uploadedFiles = await checkFileExistsBeforeUpload([block.file]);\n      // Copy the block to not mutate directly\n      const blockCopy = { ...block };\n      // Replace the file name on the block with the actual file\n      blockCopy.file = uploadedFiles;\n      updatedBlocks.push(blockCopy);\n    } else if (block.__component === \"shared.slider\") {\n      // Get files already uploaded to Strapi or upload new files\n      const existingAndUploadedFiles = await checkFileExistsBeforeUpload(\n        block.files\n      );\n      // Copy the block to not mutate directly\n      const blockCopy = { ...block };\n      // Replace the file names on the block with the actual files\n      blockCopy.files = existingAndUploadedFiles;\n      // Push the updated block\n      updatedBlocks.push(blockCopy);\n    } else {\n      // Just push the block as is\n      updatedBlocks.push(block);\n    }\n  }\n\n  return updatedBlocks;\n}\n\nasync function importArticles() {\n  for (const article of articles) {\n    const cover = await checkFileExistsBeforeUpload([`${article.slug}.jpg`]);\n    const updatedBlocks = await updateBlocks(article.blocks);\n\n    await createEntry({\n      model: \"article\",\n      entry: {\n        ...article,\n        cover,\n        blocks: updatedBlocks,\n        // Make sure it's not a draft\n        publishedAt: Date.now(),\n      },\n    });\n  }\n}\n\nasync function importGlobal() {\n  const favicon = await checkFileExistsBeforeUpload([\"favicon.png\"]);\n  const shareImage = await checkFileExistsBeforeUpload([\"default-image.png\"])\n  return createEntry({\n    model: \"global\",\n    entry: {\n      ...global,\n      favicon,\n      // Make sure it's not a draft\n      publishedAt: Date.now(),\n      defaultSeo: {\n        ...global.defaultSeo,\n        shareImage\n      }\n    },\n  });\n}\n\nasync function importAbout() {\n  const updatedBlocks = await updateBlocks(about.blocks);\n\n  await createEntry({\n    model: \"about\",\n    entry: {\n      ...about,\n      blocks: updatedBlocks,\n      // Make sure it's not a draft\n      publishedAt: Date.now(),\n    },\n  });\n}\n\nasync function importCategories() {\n  for (const category of categories) {\n    await createEntry({ model: \"category\", entry: category });\n  }\n}\n\nasync function importAuthors() {\n  for (const author of authors) {\n    const avatar = await checkFileExistsBeforeUpload([author.avatar]);\n\n    await createEntry({\n      model: \"author\",\n      entry: {\n        ...author,\n        avatar,\n      },\n    });\n  }\n}\n\nasync function importSeedData() {\n  // Allow read of application content types\n  await setPublicPermissions({\n    article: [\"find\", \"findOne\"],\n    category: [\"find\", \"findOne\"],\n    author: [\"find\", \"findOne\"],\n    global: [\"find\", \"findOne\"],\n    about: [\"find\", \"findOne\"],\n  });\n\n  // Create all entries\n  await importCategories();\n  await importAuthors();\n  await importArticles();\n  await importGlobal();\n  await importAbout();\n}\n\nmodule.exports = async () => {\n  const shouldImportSeedData = await isFirstRun();\n\n  if (shouldImportSeedData) {\n    try {\n      console.log(\"Setting up the template...\");\n      await importSeedData();\n      console.log(\"Ready to go\");\n    } catch (error) {\n      console.log(\"Could not import seed data\");\n      console.error(error);\n    }\n  }\n};\n"
  },
  {
    "path": "packages/templates/blog/template/src/components/shared/media.json",
    "content": "{\n  \"collectionName\": \"components_shared_media\",\n  \"info\": {\n    \"displayName\": \"Media\",\n    \"icon\": \"file-video\"\n  },\n  \"options\": {},\n  \"attributes\": {\n    \"file\": {\n      \"allowedTypes\": [\n        \"images\",\n        \"files\",\n        \"videos\"\n      ],\n      \"type\": \"media\",\n      \"multiple\": false\n    }\n  }\n}\n"
  },
  {
    "path": "packages/templates/blog/template/src/components/shared/quote.json",
    "content": "{\n  \"collectionName\": \"components_shared_quotes\",\n  \"info\": {\n    \"displayName\": \"Quote\",\n    \"icon\": \"indent\"\n  },\n  \"options\": {},\n  \"attributes\": {\n    \"title\": {\n      \"type\": \"string\"\n    },\n    \"body\": {\n      \"type\": \"text\"\n    }\n  }\n}\n"
  },
  {
    "path": "packages/templates/blog/template/src/components/shared/rich-text.json",
    "content": "{\n  \"collectionName\": \"components_shared_rich_texts\",\n  \"info\": {\n    \"displayName\": \"Rich text\",\n    \"icon\": \"align-justify\",\n    \"description\": \"\"\n  },\n  \"options\": {},\n  \"attributes\": {\n    \"body\": {\n      \"type\": \"richtext\"\n    }\n  }\n}\n"
  },
  {
    "path": "packages/templates/blog/template/src/components/shared/seo.json",
    "content": "{\n  \"collectionName\": \"components_shared_seos\",\n  \"info\": {\n    \"name\": \"Seo\",\n    \"icon\": \"allergies\",\n    \"displayName\": \"Seo\",\n    \"description\": \"\"\n  },\n  \"options\": {},\n  \"attributes\": {\n    \"metaTitle\": {\n      \"type\": \"string\",\n      \"required\": true\n    },\n    \"metaDescription\": {\n      \"type\": \"text\",\n      \"required\": true\n    },\n    \"shareImage\": {\n      \"type\": \"media\",\n      \"multiple\": false,\n      \"required\": false,\n      \"allowedTypes\": [\n        \"images\"\n      ]\n    }\n  }\n}\n"
  },
  {
    "path": "packages/templates/blog/template/src/components/shared/slider.json",
    "content": "{\n  \"collectionName\": \"components_shared_sliders\",\n  \"info\": {\n    \"displayName\": \"Slider\",\n    \"icon\": \"address-book\",\n    \"description\": \"\"\n  },\n  \"options\": {},\n  \"attributes\": {\n    \"files\": {\n      \"type\": \"media\",\n      \"multiple\": true,\n      \"required\": false,\n      \"allowedTypes\": [\n        \"images\"\n      ]\n    }\n  }\n}\n"
  },
  {
    "path": "packages/templates/blog/template/src/extensions/.gitkeep",
    "content": ""
  },
  {
    "path": "packages/templates/blog/template/src/index.js",
    "content": "\"use strict\";\nconst bootstrap = require(\"./bootstrap\");\n\nmodule.exports = {\n  /**\n   * An asynchronous register function that runs before\n   * your application is initialized.\n   *\n   * This gives you an opportunity to extend code.\n   */\n  register(/*{ strapi }*/) {},\n\n  /**\n   * An asynchronous bootstrap function that runs before\n   * your application gets started.\n   *\n   * This gives you an opportunity to set up your data model,\n   * run jobs, or perform some special logic.\n   */\n  bootstrap,\n};\n"
  },
  {
    "path": "packages/templates/blog/template.json",
    "content": "{\n  \"package\": {\n    \"dependencies\": {\n      \"@strapi/plugin-graphql\": \"^4.0.5\",\n      \"fs-extra\": \"^10.0.0\",\n      \"lodash.set\": \"^4.3.2\",\n      \"mime-types\": \"^2.1.27\"\n    }\n  }\n}\n"
  },
  {
    "path": "packages/templates/corporate/README.md",
    "content": "# strapi-template-corporate\n\nA Strapi template to create Strapi projects pre-configured for corporate apps.\n\n## Usage\n\n```bash\n# Using Yarn\nyarn create strapi-app my-app-name --template corporate\n\n# Or using NPM\nnpx create-strapi-app my-app-name --template corporate\n```\n\n## Starters\n\nThis template is used by the following starters:\n\n* [Strapi Starter Next Corporate Site](https://github.com/strapi/strapi-starter-next-corporate)\n* [Strapi Starter Gatsby Corporate Site](https://github.com/strapi/strapi-starter-gatsby-corporate)\n"
  },
  {
    "path": "packages/templates/corporate/package.json",
    "content": "{\n  \"name\": \"@strapi/template-corporate\",\n  \"version\": \"1.0.0\",\n  \"description\": \"Strapi corporate template\",\n  \"keywords\": [\n    \"strapi\",\n    \"template\",\n    \"corporate\"\n  ],\n  \"homepage\": \"https://github.com/strapi/starters-and-templates#readme\",\n  \"bugs\": {\n    \"url\": \"https://github.com/strapi/starters-and-templates/issues\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/strapi/starters-and-templates.git\"\n  },\n  \"license\": \"MIT\",\n  \"author\": {\n    \"name\": \"Strapi team\",\n    \"email\": \"hi@strapi.io\",\n    \"url\": \"https://strapi.io\"\n  },\n  \"maintainers\": [\n    {\n      \"name\": \"Strapi team\",\n      \"email\": \"hi@strapi.io\",\n      \"url\": \"https://strapi.io\"\n    }\n  ],\n  \"gitHead\": \"d6d5bd264d1df0a0f23b5234d84348f8d78882e3\"\n}\n"
  },
  {
    "path": "packages/templates/corporate/template/data/data.js",
    "content": "const { global, pages } = require(\"./en\");\nconst { globalFR, pagesFR } = require(\"./fr\");\nconst { leadFormSubmissions } = require(\"./lead-form-submissions.json\");\n\nmodule.exports = {\n  globals: [global, globalFR],\n  pages: [...pages, ...pagesFR],\n  leadFormSubmissions,\n};\n"
  },
  {
    "path": "packages/templates/corporate/template/data/en/global.json",
    "content": "{\n  \"global\": {\n    \"id\": 1,\n    \"locale\": \"en\",\n    \"metaTitleSuffix\": \"Strapi Corporate\",\n    \"metadata\": {\n      \"id\": 1,\n      \"metaTitle\": \"Strapi starter for Corporate Sites\",\n      \"metaDescription\": \"Build your corporate site with Strapi\",\n      \"twitterCardType\": \"summary\",\n      \"twitterUsername\": \"strapijs\",\n      \"shareImage\": null\n    },\n    \"notificationBanner\": {\n      \"id\": 1,\n      \"text\": \"This page was built using the Strapi starter for Corporate Sites.\\n[View other Strapi starters](https://strapi.io/starters)\",\n      \"type\": \"info\"\n    },\n    \"navbar\": {\n      \"id\": 1,\n      \"links\": [\n        {\n          \"id\": 1,\n          \"url\": \"/pricing\",\n          \"newTab\": false,\n          \"text\": \"Pricing\"\n        },\n        {\n          \"id\": 3,\n          \"url\": \"/contact\",\n          \"newTab\": false,\n          \"text\": \"Contact\"\n        }\n      ],\n      \"button\": {\n        \"id\": 13,\n        \"url\": \"#\",\n        \"newTab\": false,\n        \"text\": \"Sign up\",\n        \"type\": \"secondary\"\n      },\n      \"logo\" : null\n    },\n    \"footer\": {\n      \"id\": 1,\n      \"smallText\": \"© Copyright My Company™\",\n      \"columns\": [\n        {\n          \"id\": 1,\n          \"title\": \"Product\",\n          \"links\": [\n            {\n              \"id\": 11,\n              \"url\": \"#\",\n              \"newTab\": false,\n              \"text\": \"Features\"\n            },\n            {\n              \"id\": 13,\n              \"url\": \"#\",\n              \"newTab\": false,\n              \"text\": \"Sign up\"\n            }\n          ]\n        },\n        {\n          \"id\": 2,\n          \"title\": \"Legal\",\n          \"links\": [\n            {\n              \"id\": 15,\n              \"url\": \"#\",\n              \"newTab\": false,\n              \"text\": \"Privacy policy\"\n            },\n            {\n              \"id\": 16,\n              \"url\": \"#\",\n              \"newTab\": false,\n              \"text\": \"Terms & conditions\"\n            }\n          ]\n        },\n        {\n          \"id\": 5,\n          \"title\": \"Company\",\n          \"links\": [\n            {\n              \"id\": 18,\n              \"url\": \"#\",\n              \"newTab\": false,\n              \"text\": \"Careers\"\n            },\n            {\n              \"id\": 21,\n              \"url\": \"#\",\n              \"newTab\": false,\n              \"text\": \"Team\"\n            }\n          ]\n        },\n        {\n          \"id\": 7,\n          \"title\": \"Social\",\n          \"links\": [\n            {\n              \"id\": 24,\n              \"url\": \"#\",\n              \"newTab\": false,\n              \"text\": \"Twitter\"\n            },\n            {\n              \"id\": 25,\n              \"url\": \"#\",\n              \"newTab\": false,\n              \"text\": \"LinkedIn\"\n            }\n          ]\n        }\n      ],\n      \"logo\": null\n    },\n    \"favicon\": null,\n    \"localizations\": [\n      {\n        \"id\": 2,\n        \"locale\": \"fr\"\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "packages/templates/corporate/template/data/en/index.js",
    "content": "const { global } = require(\"./global.json\");\nconst { pages } = require(\"./pages.json\");\n\nmodule.exports = {\n  global,\n  pages\n}"
  },
  {
    "path": "packages/templates/corporate/template/data/en/pages.json",
    "content": "{\n  \"pages\": [\n    {\n      \"id\": 1,\n      \"slug\": \"\",\n      \"locale\": \"en\",\n      \"localizations\": [\n        {\n          \"id\": 5,\n          \"locale\": \"fr\"\n        }\n      ],\n      \"shortName\": \"Home\",\n      \"publishedAt\": \"2021-10-22T08:11:55.490Z\",\n      \"metadata\": {\n        \"id\": 2,\n        \"metaTitle\": \"Strapi corporate site starter\",\n        \"metaDescription\": \"Build a fully editable site with Strapi\",\n        \"twitterCardType\": \"summary\",\n        \"twitterUsername\": null,\n        \"shareImage\": null\n      },\n      \"contentSections\": [\n        {\n          \"id\": 1,\n          \"__component\": \"sections.hero\",\n          \"title\": \"The best way to build your Corporate Site\",\n          \"label\": \"New Strapi starter\",\n          \"description\": \"Get started with your Strapi business website in seconds.\",\n          \"smallTextWithLink\": \"Want to build your own from scratch? Tutorial coming soon\",\n          \"buttons\": [\n            {\n              \"id\": 2,\n              \"url\": \"https://github.com/strapi/strapi-template-corporate\",\n              \"newTab\": false,\n              \"text\": \"Get started\",\n              \"type\": \"primary\"\n            },\n            {\n              \"id\": 4,\n              \"url\": \"https://github.com/strapi/strapi-template-corporate\",\n              \"newTab\": true,\n              \"text\": \"See the code\",\n              \"type\": \"secondary\"\n            }\n          ],\n          \"picture\": null\n        },\n        {\n          \"id\": 1,\n          \"__component\": \"sections.feature-rows-group\",\n          \"features\": [\n            {\n              \"id\": 1,\n              \"title\": \"Let marketing teams build their pages\",\n              \"description\": \"Thanks to a pre-built list of UI sections, marketers can design exactly the pages they want.\",\n              \"link\": {\n                \"id\": 7,\n                \"url\": \"#\",\n                \"newTab\": false,\n                \"text\": \"View the list of UI sections\"\n              },\n              \"media\": null\n            },\n            {\n              \"id\": 2,\n              \"title\": \"Leave your developers alone\",\n              \"description\": \"You can publish, edit and delete pages without help from developers. Your technical team can finally focus on their tasks.\",\n              \"link\": {\n                \"id\": 8,\n                \"url\": \"#\",\n                \"newTab\": false,\n                \"text\": \"View how to create a page\"\n              },\n              \"media\": null\n            }\n          ]\n        },\n        {\n          \"id\": 1,\n          \"__component\": \"sections.feature-columns-group\",\n          \"features\": [\n            {\n              \"id\": 1,\n              \"title\": \"Preview your changes\",\n              \"description\": \"Thanks to an integrated Preview Mode, you can visualize your pages before publishing them.\",\n              \"icon\": null\n            },\n            {\n              \"id\": 2,\n              \"title\": \"Fully responsive\",\n              \"description\": \"This starter works well on all screens, whether it's mobile, tablet or desktop.\",\n              \"icon\": null\n            },\n            {\n              \"id\": 3,\n              \"title\": \"Easy to customize\",\n              \"description\": \"We use Tailwind for styling. You can change your site's theme without digging through the code.\",\n              \"icon\": null\n            }\n          ]\n        },\n        {\n          \"id\": 1,\n          \"__component\": \"sections.testimonials-group\",\n          \"title\": \"Your customer testimonials here\",\n          \"description\": \"This section is where you can showcase your customers. Insert quotes, and show the logos of companies who like your product\",\n          \"link\": {\n            \"id\": 2,\n            \"url\": \"#\",\n            \"newTab\": false,\n            \"text\": \"All testimonials\"\n          },\n          \"logos\": [\n            {\n              \"id\": 1,\n              \"title\": \"Strapi\",\n              \"logo\": null\n            },\n            {\n              \"id\": 2,\n              \"title\": \"Strapi 2\",\n              \"logo\": null\n            },\n            {\n              \"id\": 3,\n              \"title\": \"Strapi 3\",\n              \"logo\": null\n            }\n          ],\n          \"testimonials\": [\n            {\n              \"id\": 1,\n              \"text\": \"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.\",\n              \"authorName\": \"Your Customer Name\",\n              \"authorTitle\": \"A happy customer\",\n              \"link\": \"#\",\n              \"logo\": null,\n              \"picture\": null\n            }\n          ]\n        },\n        {\n          \"id\": 1,\n          \"__component\": \"sections.lead-form\",\n          \"title\": \"Subscribe To Our Newsletter\",\n          \"emailPlaceholder\": \"email@company.com\",\n          \"submitButton\": {\n            \"id\": 1,\n            \"__component\": \"links.button\",\n            \"text\": \"Subscribe\",\n            \"type\": \"primary\"\n          },\n          \"location\": \"Home Page Bottom\"\n        },\n        {\n          \"id\": 2,\n          \"__component\": \"sections.bottom-actions\",\n          \"title\": \"Start building your website\",\n          \"buttons\": [\n            {\n              \"id\": 7,\n              \"url\": \"https://github.com/strapi/strapi-template-corporate\",\n              \"newTab\": false,\n              \"text\": \"Get started\",\n              \"type\": \"primary\"\n            },\n            {\n              \"id\": 8,\n              \"url\": \"https://github.com/strapi/strapi-template-corporate\",\n              \"newTab\": false,\n              \"text\": \"See the code\",\n              \"type\": \"secondary\"\n            }\n          ]\n        }\n      ]\n    },\n    {\n      \"id\": 2,\n      \"slug\": \"pricing\",\n      \"locale\": \"en\",\n      \"localizations\": [\n        {\n          \"id\": 6,\n          \"locale\": \"fr\"\n        }\n      ],\n      \"shortName\": \"Pricing\",\n      \"publishedAt\": \"2021-10-22T08:11:55.490Z\",\n      \"metadata\": {\n        \"id\": 3,\n        \"metaTitle\": \"Pricing\",\n        \"metaDescription\": \"The different plans\",\n        \"twitterCardType\": \"summary\",\n        \"twitterUsername\": null,\n        \"shareImage\": null\n      },\n      \"contentSections\": [\n        {\n          \"id\": 1,\n          \"__component\": \"sections.pricing\",\n          \"title\": \"Clear pricing table\",\n          \"plans\": [\n            {\n              \"id\": 1,\n              \"name\": \"Hobby\",\n              \"description\": \"Perfect for side projects\",\n              \"isRecommended\": null,\n              \"price\": 0,\n              \"pricePeriod\": \"forever\",\n              \"features\": [\n                {\n                  \"id\": 1,\n                  \"name\": \"A cool feature\"\n                },\n                {\n                  \"id\": 2,\n                  \"name\": \"Another cool feature\"\n                },\n                {\n                  \"id\": 3,\n                  \"name\": \"Some other cool feature\"\n                }\n              ]\n            },\n            {\n              \"id\": 2,\n              \"name\": \"Premium\",\n              \"description\": \"A more advanced plan for SMBs\",\n              \"isRecommended\": true,\n              \"price\": 20,\n              \"pricePeriod\": \"per month\",\n              \"features\": [\n                {\n                  \"id\": 4,\n                  \"name\": \"The coolest feature\"\n                },\n                {\n                  \"id\": 5,\n                  \"name\": \"Nice feature\"\n                },\n                {\n                  \"id\": 6,\n                  \"name\": \"Fun feature\"\n                }\n              ]\n            },\n            {\n              \"id\": 3,\n              \"name\": \"Enterprise\",\n              \"description\": \"For large companies needs\",\n              \"isRecommended\": null,\n              \"price\": 299,\n              \"pricePeriod\": \"per month\",\n              \"features\": [\n                {\n                  \"id\": 7,\n                  \"name\": \"Amazing feature\"\n                },\n                {\n                  \"id\": 8,\n                  \"name\": \"Wow effect feature\"\n                },\n                {\n                  \"id\": 9,\n                  \"name\": \"Mesmerizing feature\"\n                }\n              ]\n            }\n          ]\n        }\n      ]\n    },\n    {\n      \"id\": 3,\n      \"slug\": \"secret\",\n      \"locale\": \"en\",\n      \"localizations\": [\n        {\n          \"id\": 7,\n          \"locale\": \"fr\"\n        }\n      ],\n      \"shortName\": \"Secret\",\n      \"metadata\": {\n        \"id\": 7,\n        \"metaTitle\": \"Secret page\",\n        \"metaDescription\": \"Preview-only page\",\n        \"twitterCardType\": \"summary\",\n        \"twitterUsername\": null,\n        \"shareImage\": null\n      },\n      \"contentSections\": [\n        {\n          \"id\": 2,\n          \"__component\": \"sections.rich-text\",\n          \"content\": \"## Secret page\\n\\nYou can only view this page in Preview Mode.\"\n        }\n      ]\n    },\n    {\n      \"id\": 4,\n      \"slug\": \"contact\",\n      \"locale\": \"en\",\n      \"localizations\": [\n        {\n          \"id\": 8,\n          \"locale\": \"fr\"\n        }\n      ],\n      \"shortName\": \"Contact\",\n      \"publishedAt\": \"2021-10-22T08:11:55.490Z\",\n      \"metadata\": {\n        \"id\": 4,\n        \"metaTitle\": \"Contact\",\n        \"metaDescription\": \"Get in touch with our team\",\n        \"twitterCardType\": \"summary\",\n        \"twitterUsername\": null,\n        \"shareImage\": null\n      },\n      \"contentSections\": [\n        {\n          \"id\": 1,\n          \"__component\": \"sections.rich-text\",\n          \"content\": \"# Get in touch\\n\\n> This is an example of a page that relies almost entirely on the RichText section. It's useful for blog articles, or content-heavy pages like legal terms.\\n\\nWe'd love to hear from you.\\n\\n## Social media\\n\\n* [Twitter](#)\\n* [Twitter](#)\\n* [Twitter](#)\\n\\n## Postal address\\n\\n404 Headless Street\\n__92210__ **Saint Cloud**, *France*\\n\\nLorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.\"\n        },\n        {\n          \"id\": 1,\n          \"__component\": \"sections.bottom-actions\",\n          \"title\": \"Get in touch\",\n          \"buttons\": [\n            {\n              \"id\": 1,\n              \"url\": \"#\",\n              \"newTab\": false,\n              \"text\": \"Send an email\",\n              \"type\": \"primary\"\n            },\n            {\n              \"id\": 3,\n              \"url\": \"#\",\n              \"newTab\": false,\n              \"text\": \"DM us on Twitter\",\n              \"type\": \"secondary\"\n            }\n          ]\n        }\n      ]\n    }\n  ]\n}\n"
  },
  {
    "path": "packages/templates/corporate/template/data/fr/global.json",
    "content": "{\n  \"globalFR\": {\n    \"id\": 2,\n    \"locale\": \"fr\",\n    \"metaTitleSuffix\": \"Strapi Corporate\",\n    \"metadata\": {\n      \"id\": 5,\n      \"metaTitle\": \"Strapi starter for Corporate Sites\",\n      \"metaDescription\": \"Créer votre site corporate avec Strapi\",\n      \"twitterCardType\": \"summary\",\n      \"twitterUsername\": \"strapijs\",\n      \"shareImage\": null\n    },\n    \"notificationBanner\": {\n      \"id\": 2,\n      \"text\": \"Cette page a été crée avec le Strapi starter pour les sites Corporate.\\n [Voir les autres starters Strapi](https://strapi.io/starters)\",\n      \"type\": \"info\"\n    },\n    \"navbar\": {\n      \"id\": 2,\n      \"links\": [\n        {\n          \"id\": 4,\n          \"url\": \"/pricing\",\n          \"newTab\": false,\n          \"text\": \"tarifs\"\n        },\n        {\n          \"id\": 5,\n          \"url\": \"/contact\",\n          \"newTab\": false,\n          \"text\": \"nous contacter\"\n        }\n      ],\n      \"button\": {\n        \"id\": 14,\n        \"url\": \"#\",\n        \"newTab\": false,\n        \"text\": \"S'inscrire\",\n        \"type\": \"secondary\"\n      },\n      \"logo\" : null\n    },\n    \"footer\": {\n      \"id\": 2,\n      \"smallText\": \"© Copyright Mon Entreprise™\",\n      \"columns\": [\n        {\n          \"id\": 3,\n          \"title\": \"Produit\",\n          \"links\": [\n            {\n              \"id\": 12,\n              \"url\": \"#\",\n              \"newTab\": false,\n              \"text\": \"Fonctionnalités\"\n            },\n            {\n              \"id\": 14,\n              \"url\": \"#\",\n              \"newTab\": false,\n              \"text\": \"S'inscrire\"\n            }\n          ]\n        },\n        {\n          \"id\": 4,\n          \"title\": \"Légal\",\n          \"links\": [\n            {\n              \"id\": 17,\n              \"url\": \"#\",\n              \"newTab\": false,\n              \"text\": \"Confidentialité\"\n            },\n            {\n              \"id\": 19,\n              \"url\": \"#\",\n              \"newTab\": false,\n              \"text\": \"Mentions légales\"\n            }\n          ]\n        },\n        {\n          \"id\": 6,\n          \"title\": \"Entreprise\",\n          \"links\": [\n            {\n              \"id\": 20,\n              \"url\": \"#\",\n              \"newTab\": false,\n              \"text\": \"Nous rejoindre\"\n            },\n            {\n              \"id\": 22,\n              \"url\": \"#\",\n              \"newTab\": false,\n              \"text\": \"L'équipe\"\n            }\n          ]\n        },\n        {\n          \"id\": 8,\n          \"title\": \"Réseaux sociaux\",\n          \"links\": [\n            {\n              \"id\": 23,\n              \"url\": \"#\",\n              \"newTab\": false,\n              \"text\": \"Twitter\"\n            },\n            {\n              \"id\": 26,\n              \"url\": \"#\",\n              \"newTab\": false,\n              \"text\": \"LinkedIn\"\n            }\n          ]\n        }\n      ],\n      \"logo\": null\n    },\n    \"favicon\": null,\n    \"localizations\": [\n      {\n        \"id\": 1,\n        \"locale\": \"en\"\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "packages/templates/corporate/template/data/fr/index.js",
    "content": "const { globalFR } = require(\"./global.json\");\nconst { pagesFR } = require(\"./pages.json\");\n\nmodule.exports = {\n  globalFR,\n  pagesFR\n}"
  },
  {
    "path": "packages/templates/corporate/template/data/fr/pages.json",
    "content": "{\n  \"pagesFR\": [\n    {\n      \"id\": 5,\n      \"slug\": \"\",\n      \"locale\": \"fr\",\n      \"localizations\": [\n        {\n          \"id\": 1,\n          \"locale\": \"en\"\n        }\n      ],\n      \"shortName\": \"Home\",\n      \"publishedAt\":\"2021-10-22T08:11:55.490Z\",\n      \"metadata\": {\n        \"id\": 6,\n        \"metaTitle\": \"Strapi corporate site starter\",\n        \"metaDescription\": \"Construire un site modifiable avec Strapi\",\n        \"twitterCardType\": \"summary\",\n        \"twitterUsername\": null,\n        \"shareImage\": null\n      },\n      \"contentSections\": [\n        {\n          \"id\": 2,\n          \"__component\": \"sections.hero\",\n          \"title\": \"La meilleure façon de faire votre site Corporate\",\n          \"label\": \"Nouveau starter Strapi\",\n          \"description\": \"Commencer vite avec votre site corporate\",\n          \"smallTextWithLink\": \"Vous voulez construire votre propre starter? Guide à venir\",\n          \"buttons\": [\n            {\n              \"id\": 5,\n              \"url\": \"https://github.com/strapi/strapi-template-corporate\",\n              \"newTab\": false,\n              \"text\": \"Commencer\",\n              \"type\": \"primary\"\n            },\n            {\n              \"id\": 6,\n              \"url\": \"https://github.com/strapi/strapi-template-corporate\",\n              \"newTab\": true,\n              \"text\": \"Voir le code\",\n              \"type\": \"secondary\"\n            }\n          ],\n          \"picture\": null\n        },\n        {\n          \"id\": 2,\n          \"__component\": \"sections.feature-rows-group\",\n          \"features\": [\n            {\n              \"id\": 3,\n              \"title\": \"Les équipes marketing peuvent maintenant faire leurs propres pages\",\n              \"description\": \"Grace à une liste pré-construite de blocs UI, les équipes marketing peuvent faire la mise en page exactement comme elles veulent\",\n              \"link\": {\n                \"id\": 9,\n                \"url\": \"#\",\n                \"newTab\": false,\n                \"text\": \"Voir la liste de blocs UI\"\n              },\n              \"media\": null\n            },\n            {\n              \"id\": 4,\n              \"title\": \"Laissez vos développeurs tranquilles\",\n              \"description\": \"Vous pouvez publier, modifier, et supprimer des pages sans l'aide d'un développeur.  Votre équipe technique peut enfin se concentrer sur d'autre tâches.\",\n              \"link\": {\n                \"id\": 10,\n                \"url\": \"#\",\n                \"newTab\": false,\n                \"text\": \"Voir comment créer une page\"\n              },\n              \"media\": null\n            }\n          ]\n        },\n        {\n          \"id\": 2,\n          \"__component\": \"sections.feature-columns-group\",\n          \"features\": [\n            {\n              \"id\": 4,\n              \"title\": \"Visualisez vos changments\",\n              \"description\": \"Grace à un mode preview, vous pouvez voir vos pages avant de les publier.\",\n              \"icon\": null\n            },\n            {\n              \"id\": 5,\n              \"title\": \"100% responsive\",\n              \"description\": \"Ce starter marche sur toutes les tailles d'écrans.\",\n              \"icon\": null\n            },\n            {\n              \"id\": 6,\n              \"title\": \"Facile à personnaliser\",\n              \"description\": \"On utilise Tailwind pour les styles. Vous pouvez changez le thème de votre site sans avoir besoin d'aller dans le code.\",\n              \"icon\": null\n            }\n          ]\n        },\n        {\n          \"id\": 2,\n          \"__component\": \"sections.testimonials-group\",\n          \"title\": \"Ici, les témoignages des vos clients.\",\n          \"description\": \"Dans cette partie, vous pouvez mettre en avant vos clients.  Mettez des citations, et montrez les logos des entreprises qui aiment votre produit.\",\n          \"link\": {\n            \"id\": 6,\n            \"url\": \"#\",\n            \"newTab\": false,\n            \"text\": \"All testimonials\"\n          },\n          \"logos\": [\n            {\n              \"id\": 4,\n              \"title\": \"Strapi\",\n              \"logo\": null\n            },\n            {\n              \"id\": 5,\n              \"title\": \"Strapi 2\",\n              \"logo\": null\n            },\n            {\n              \"id\": 6,\n              \"title\": \"Strapi 3\",\n              \"logo\": null\n            }\n          ],\n          \"testimonials\": [\n            {\n              \"id\": 2,\n              \"text\": \"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.\",\n              \"authorName\": \"Nom d'un(e) Client(e) ici\",\n              \"authorTitle\": \"Un(e) Client(e) Satisfait(e)\",\n              \"link\": \"#\",\n              \"logo\": null,\n              \"picture\": null\n            }\n          ]\n        },\n        {\n          \"id\": 2,\n          \"__component\": \"sections.lead-form\",\n          \"title\": \"S'inscrire à notre Newsletter\",\n          \"emailPlaceholder\": \"email@company.com\",\n          \"submitButton\": {\n            \"id\": 2,\n            \"__component\": \"links.button\",\n            \"text\": \"S'inscrire\",\n            \"type\": \"primary\"\n          },\n          \"location\": \"Home Page Bottom\"\n        },\n        {\n          \"id\": 3,\n          \"__component\": \"sections.bottom-actions\",\n          \"title\": \"Start building your website\",\n          \"buttons\": [\n            {\n              \"id\": 10,\n              \"url\": \"https://github.com/strapi/strapi-template-corporate\",\n              \"newTab\": false,\n              \"text\": \"Commencer\",\n              \"type\": \"primary\"\n            },\n            {\n              \"id\": 12,\n              \"url\": \"https://github.com/strapi/strapi-template-corporate\",\n              \"newTab\": false,\n              \"text\": \"Voir le code\",\n              \"type\": \"secondary\"\n            }\n          ]\n        }\n      ]\n    },\n    {\n      \"id\": 6,\n      \"slug\": \"pricing\",\n      \"locale\": \"fr\",\n      \"localizations\": [\n        {\n          \"id\": 2,\n          \"locale\": \"en\"\n        }\n      ],\n      \"shortName\": \"Pricing\",\n      \"publishedAt\":\"2021-10-22T08:11:55.490Z\",\n      \"metadata\": {\n        \"id\": 8,\n        \"metaTitle\": \"Pricing\",\n        \"metaDescription\": \"Les différentes offres\",\n        \"twitterCardType\": \"summary\",\n        \"twitterUsername\": null,\n        \"shareImage\": null\n      },\n      \"contentSections\": [\n        {\n          \"id\": 2,\n          \"__component\": \"sections.pricing\",\n          \"title\": \"Tableau de tarifs\",\n          \"plans\": [\n            {\n              \"id\": 4,\n              \"name\": \"Hobby\",\n              \"description\": \"Parfait pour des projets personnels\",\n              \"isRecommended\": null,\n              \"price\": 0,\n              \"pricePeriod\": \"toujours\",\n              \"features\": [\n                {\n                  \"id\": 10,\n                  \"name\": \"Voilà une feature\"\n                },\n                {\n                  \"id\": 11,\n                  \"name\": \"Une autre\"\n                },\n                {\n                  \"id\": 12,\n                  \"name\": \"Encore une autre\"\n                }\n              ]\n            },\n            {\n              \"id\": 5,\n              \"name\": \"Premium\",\n              \"description\": \"Un plan plus avancé\",\n              \"isRecommended\": true,\n              \"price\": 20,\n              \"pricePeriod\": \"par mois\",\n              \"features\": [\n                {\n                  \"id\": 13,\n                  \"name\": \"Fonctionnalité 1\"\n                },\n                {\n                  \"id\": 14,\n                  \"name\": \"Fonctionnalité 2\"\n                },\n                {\n                  \"id\": 15,\n                  \"name\": \"Fonctionnalité 3\"\n                }\n              ]\n            },\n            {\n              \"id\": 6,\n              \"name\": \"Entreprise\",\n              \"description\": \"Pour les besoin des grands entreprises\",\n              \"isRecommended\": null,\n              \"price\": 299,\n              \"pricePeriod\": \"par mois\",\n              \"features\": [\n                {\n                  \"id\": 16,\n                  \"name\": \"Fonctionnalité 1\"\n                },\n                {\n                  \"id\": 17,\n                  \"name\": \"Fonctionnalité 2\"\n                },\n                {\n                  \"id\": 18,\n                  \"name\": \"Fonctionnalité 3\"\n                }\n              ]\n            }\n          ]\n        }\n      ]\n    },\n    {\n      \"id\": 7,\n      \"slug\": \"secret\",\n      \"locale\": \"fr\",\n      \"localizations\": [\n        {\n          \"id\": 3,\n          \"locale\": \"en\"\n        }\n      ],\n      \"shortName\": \"Secret\",\n      \"metadata\": {\n        \"id\": 9,\n        \"metaTitle\": \"Page secrète\",\n        \"metaDescription\": \"Preview-only page\",\n        \"twitterCardType\": \"summary\",\n        \"twitterUsername\": null,\n        \"shareImage\": null\n      },\n      \"contentSections\": [\n        {\n          \"id\": 3,\n          \"__component\": \"sections.rich-text\",\n          \"content\": \"## Page secrète. \\n\\n Vous seul pouvez voir cette page en Preview Mode\"\n        }\n      ]\n    },\n    {\n      \"id\": 8,\n      \"slug\": \"contact\",\n      \"locale\": \"fr\",\n      \"localizations\": [\n        {\n          \"id\": 4,\n          \"locale\": \"en\"\n        }\n      ],\n      \"shortName\": \"Contact\",\n      \"publishedAt\":\"2021-10-22T08:11:55.490Z\",\n      \"metadata\": {\n        \"id\": 10,\n        \"metaTitle\": \"Nous Contacter\",\n        \"metaDescription\": \"Contactez notre équipe\",\n        \"twitterCardType\": \"summary\",\n        \"twitterUsername\": null,\n        \"shareImage\": null\n      },\n      \"contentSections\": [\n        {\n          \"id\": 4,\n          \"__component\": \"sections.rich-text\",\n          \"content\": \"# Contactez nous\\n\\n> Ceci est un exemple d'une page qui repose presque entièrement sur le composant RichText. Vous pouvez l'utiliser pour des articles de blog, ou pour des pages comportant beaucoup de texte, comme par exemple les conditions d'utilisation.\\n\\nNous aimerions beaucoup avoir vos retours.\\n\\n## Social media\\n\\n* [Twitter](#)\\n* [Twitter](#)\\n* [Twitter](#)\\n\\n## Postal address\\n\\n404 Headless Street\\n__92210__ **Saint Cloud**, *France*\\n\\nLorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.\"\n        },\n        {\n          \"id\": 4,\n          \"__component\": \"sections.bottom-actions\",\n          \"title\": \"Get in touch\",\n          \"buttons\": [\n            {\n              \"id\": 9,\n              \"url\": \"#\",\n              \"newTab\": false,\n              \"text\": \"Envoyer un mail\",\n              \"type\": \"primary\"\n            },\n            {\n              \"id\": 11,\n              \"url\": \"#\",\n              \"newTab\": false,\n              \"text\": \"DM nous sur Twitter\",\n              \"type\": \"secondary\"\n            }\n          ]\n        }\n      ]\n    }\n  ]\n}\n"
  },
  {
    "path": "packages/templates/corporate/template/data/lead-form-submissions.json",
    "content": "{\n  \"leadFormSubmissions\": [\n    {\n      \"email\": \"johne@thebeatles.com\",\n      \"status\": \"seen\",\n      \"location\": \"Home Page Bottom\"\n    },\n    {\n      \"email\": \"paul@thebeatles.com\",\n      \"status\": \"contacted\",\n      \"location\": \"Home Page Bottom\"\n    },\n    {\n      \"email\": \"george@thebeatles.com\",\n      \"status\": \"ignored\",\n      \"location\": \"Home Page Bottom\"\n    }\n  ]\n}"
  },
  {
    "path": "packages/templates/corporate/template/src/api/.gitkeep",
    "content": ""
  },
  {
    "path": "packages/templates/corporate/template/src/api/global/content-types/global/schema.json",
    "content": "{\n  \"kind\": \"singleType\",\n  \"collectionName\": \"globals\",\n  \"info\": {\n    \"singularName\": \"global\",\n    \"pluralName\": \"globals\",\n    \"displayName\": \"Global\",\n    \"name\": \"global\"\n  },\n  \"options\": {\n    \"increments\": true,\n    \"timestamps\": true,\n    \"draftAndPublish\": false\n  },\n  \"pluginOptions\": {\n    \"i18n\": {\n      \"localized\": true\n    }\n  },\n  \"attributes\": {\n    \"metadata\": {\n      \"type\": \"component\",\n      \"repeatable\": false,\n      \"component\": \"meta.metadata\",\n      \"pluginOptions\": {\n        \"i18n\": {\n          \"localized\": true\n        }\n      }\n    },\n    \"metaTitleSuffix\": {\n      \"type\": \"string\",\n      \"required\": true,\n      \"pluginOptions\": {\n        \"i18n\": {\n          \"localized\": true\n        }\n      }\n    },\n    \"favicon\": {\n      \"allowedTypes\": [\n        \"images\"\n      ],\n      \"type\": \"media\",\n      \"multiple\": false,\n      \"pluginOptions\": {\n        \"i18n\": {\n          \"localized\": true\n        }\n      }\n    },\n    \"notificationBanner\": {\n      \"type\": \"component\",\n      \"repeatable\": false,\n      \"component\": \"elements.notification-banner\",\n      \"pluginOptions\": {\n        \"i18n\": {\n          \"localized\": true\n        }\n      }\n    },\n    \"navbar\": {\n      \"type\": \"component\",\n      \"repeatable\": false,\n      \"component\": \"layout.navbar\",\n      \"pluginOptions\": {\n        \"i18n\": {\n          \"localized\": true\n        }\n      }\n    },\n    \"footer\": {\n      \"type\": \"component\",\n      \"repeatable\": false,\n      \"component\": \"layout.footer\",\n      \"pluginOptions\": {\n        \"i18n\": {\n          \"localized\": true\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "packages/templates/corporate/template/src/api/global/controllers/global.js",
    "content": "'use strict';\n\n/**\n *  global controller\n */\n\nconst { createCoreController } = require('@strapi/strapi').factories;\n\nmodule.exports = createCoreController('api::global.global');\n"
  },
  {
    "path": "packages/templates/corporate/template/src/api/global/routes/global.js",
    "content": "'use strict';\n\n/**\n * global router.\n */\n\nconst { createCoreRouter } = require('@strapi/strapi').factories;\n\nmodule.exports = createCoreRouter('api::global.global');\n"
  },
  {
    "path": "packages/templates/corporate/template/src/api/global/services/global.js",
    "content": "'use strict';\n\n/**\n * global service.\n */\n\nconst { createCoreService } = require('@strapi/strapi').factories;\n\nmodule.exports = createCoreService('api::global.global');\n"
  },
  {
    "path": "packages/templates/corporate/template/src/api/lead-form-submission/content-types/lead-form-submission/schema.json",
    "content": "{\n  \"kind\": \"collectionType\",\n  \"collectionName\": \"lead_form_submissions\",\n  \"info\": {\n    \"singularName\": \"lead-form-submission\",\n    \"pluralName\": \"lead-form-submissions\",\n    \"displayName\": \"Lead form submission\",\n    \"name\": \"lead-form-submission\"\n  },\n  \"options\": {\n    \"increments\": true,\n    \"timestamps\": true,\n    \"draftAndPublish\": false\n  },\n  \"attributes\": {\n    \"email\": {\n      \"type\": \"string\"\n    },\n    \"status\": {\n      \"type\": \"enumeration\",\n      \"enum\": [\n        \"seen\",\n        \"contacted\",\n        \"ignored\"\n      ]\n    },\n    \"location\": {\n      \"type\": \"string\"\n    }\n  }\n}\n"
  },
  {
    "path": "packages/templates/corporate/template/src/api/lead-form-submission/controllers/lead-form-submission.js",
    "content": "'use strict';\n\n/**\n *  lead-form-submission controller\n */\n\nconst { createCoreController } = require('@strapi/strapi').factories;\n\nmodule.exports = createCoreController('api::lead-form-submission.lead-form-submission');\n"
  },
  {
    "path": "packages/templates/corporate/template/src/api/lead-form-submission/routes/lead-form-submission.js",
    "content": "'use strict';\n\n/**\n * lead-form-submission router.\n */\n\nconst { createCoreRouter } = require('@strapi/strapi').factories;\n\nmodule.exports = createCoreRouter('api::lead-form-submission.lead-form-submission');\n"
  },
  {
    "path": "packages/templates/corporate/template/src/api/lead-form-submission/services/lead-form-submission.js",
    "content": "'use strict';\n\n/**\n * lead-form-submission service.\n */\n\nconst { createCoreService } = require('@strapi/strapi').factories;\n\nmodule.exports = createCoreService('api::lead-form-submission.lead-form-submission');\n"
  },
  {
    "path": "packages/templates/corporate/template/src/api/page/content-types/page/schema.json",
    "content": "{\n  \"kind\": \"collectionType\",\n  \"collectionName\": \"pages\",\n  \"info\": {\n    \"singularName\": \"page\",\n    \"pluralName\": \"pages\",\n    \"displayName\": \"Page\",\n    \"name\": \"page\"\n  },\n  \"options\": {\n    \"increments\": true,\n    \"timestamps\": true,\n    \"draftAndPublish\": true\n  },\n  \"pluginOptions\": {\n    \"i18n\": {\n      \"localized\": true\n    }\n  },\n  \"attributes\": {\n    \"shortName\": {\n      \"type\": \"string\",\n      \"pluginOptions\": {\n        \"i18n\": {\n          \"localized\": true\n        }\n      }\n    },\n    \"metadata\": {\n      \"type\": \"component\",\n      \"repeatable\": false,\n      \"component\": \"meta.metadata\",\n      \"required\": true,\n      \"pluginOptions\": {\n        \"i18n\": {\n          \"localized\": true\n        }\n      }\n    },\n    \"contentSections\": {\n      \"type\": \"dynamiczone\",\n      \"components\": [\n        \"sections.hero\",\n        \"sections.bottom-actions\",\n        \"sections.feature-columns-group\",\n        \"sections.feature-rows-group\",\n        \"sections.testimonials-group\",\n        \"sections.large-video\",\n        \"sections.rich-text\",\n        \"sections.pricing\",\n        \"sections.lead-form\"\n      ],\n      \"pluginOptions\": {\n        \"i18n\": {\n          \"localized\": true\n        }\n      }\n    },\n    \"slug\": {\n      \"pluginOptions\": {\n        \"i18n\": {\n          \"localized\": false\n        }\n      },\n      \"type\": \"string\",\n      \"regex\": \"^$|^[a-zA-Z/-]+$\"\n    }\n  }\n}\n"
  },
  {
    "path": "packages/templates/corporate/template/src/api/page/controllers/page.js",
    "content": "'use strict';\n\n/**\n *  page controller\n */\n\nconst { createCoreController } = require('@strapi/strapi').factories;\n\nmodule.exports = createCoreController('api::page.page');\n"
  },
  {
    "path": "packages/templates/corporate/template/src/api/page/routes/page.js",
    "content": "'use strict';\n\n/**\n * page router.\n */\n\nconst { createCoreRouter } = require('@strapi/strapi').factories;\n\nmodule.exports = createCoreRouter('api::page.page');\n"
  },
  {
    "path": "packages/templates/corporate/template/src/api/page/services/page.js",
    "content": "'use strict';\n\n/**\n * page service.\n */\n\nconst { createCoreService } = require('@strapi/strapi').factories;\n\nmodule.exports = createCoreService('api::page.page');\n"
  },
  {
    "path": "packages/templates/corporate/template/src/bootstrap.js",
    "content": "const fs = require(\"fs\");\nconst { pages, globals, leadFormSubmissions } = require(\"../data/data\");\nconst set = require(\"lodash.set\");\n\nasync function isFirstRun() {\n  const pluginStore = strapi.store({\n    environment: strapi.config.environment,\n    type: \"type\",\n    name: \"setup\",\n  });\n  const initHasRun = await pluginStore.get({ key: \"initHasRun\" });\n  await pluginStore.set({ key: \"initHasRun\", value: true });\n  return !initHasRun;\n}\n\nasync function setPublicPermissions(newPermissions) {\n  // Find the ID of the public role\n  const publicRole = await strapi\n    .query(\"plugin::users-permissions.role\")\n    .findOne({\n      where: {\n        type: \"public\",\n      },\n    });\n\n  // Create the new permissions and link them to the public role\n  const allPermissionsToCreate = [];\n  Object.keys(newPermissions).map(controller => {\n    const actions = newPermissions[controller];\n    const permissionsToCreate = actions.map(action => {\n      return strapi.query(\"plugin::users-permissions.permission\").create({\n        data: {\n          action: `api::${controller}.${controller}.${action}`,\n          role: publicRole.id,\n        },\n      });\n    });\n    allPermissionsToCreate.push(...permissionsToCreate);\n  });\n  await Promise.all(allPermissionsToCreate);\n}\n\nfunction getFileSizeInBytes(filePath) {\n  const stats = fs.statSync(filePath);\n  const fileSizeInBytes = stats[\"size\"];\n  return fileSizeInBytes;\n}\n\nfunction getFileData(fileName) {\n  const filePath = `./data/uploads/${fileName}`;\n\n  // Parse the file metadata\n  const size = getFileSizeInBytes(filePath);\n  const ext = fileName.split(\".\").pop();\n  const mimeType = `image/${ext === \"svg\" ? \"svg+xml\" : ext}`;\n\n  return {\n    path: filePath,\n    name: fileName,\n    size,\n    type: mimeType,\n  };\n}\n\n// Create an entry and attach files if there are any\nasync function createEntry(model, entry, files) {\n  try {\n    if (files) {\n      for (const [key, file] of Object.entries(files)) {\n        // Get file name without the extension\n        const [fileName] = file.name.split('.');\n        // Upload each individual file\n        const uploadedFile = await strapi\n          .plugin(\"upload\")\n          .service(\"upload\")\n          .upload({\n            files: file,\n            data: {\n              fileInfo: {\n                alternativeText: fileName,\n                caption: fileName,\n                name: fileName,\n              },\n            },\n          });\n\n        // Attach each file to its entry\n        set(entry, key, uploadedFile[0].id);\n      }\n    }\n\n    // Actually create the entry in Strapi\n    const createdEntry = await strapi.entityService.create(\n      `api::${model}.${model}`,\n      {\n        data: entry,\n      }\n    );\n  } catch (e) {\n    console.log(e);\n  }\n}\n\nasync function importPages(pages) {\n  const getPageCover = (slug) => {\n    switch (slug) {\n      case \"\":\n        return getFileData(\"undraw-content-team.png\");\n      default:\n        return null;\n    }\n  };\n\n  return pages.map(async (page) => {\n    const files = {};\n    const shareImage = getPageCover(page.slug);\n    if (shareImage) {\n      files[\"metadata.shareImage\"] = shareImage;\n    }\n    // Check if dynamic zone has attached files\n    page.contentSections.forEach((section, index) => {\n      if (section.__component === \"sections.hero\") {\n        files[`contentSections.${index}.picture`] = getFileData(\n          \"undraw-content-team.svg\"\n        );\n      } else if (section.__component === \"sections.feature-rows-group\") {\n        const getFeatureMedia = (featureIndex) => {\n          switch (featureIndex) {\n            case 0:\n              return getFileData(\"undraw-design-page.svg\");\n            case 1:\n              return getFileData(\"undraw-create-page.svg\");\n            default:\n              return null;\n          }\n        };\n        section.features.forEach((feature, featureIndex) => {\n          files[`contentSections.${index}.features.${featureIndex}.media`] =\n            getFeatureMedia(featureIndex);\n        });\n      } else if (section.__component === \"sections.feature-columns-group\") {\n        const getFeatureMedia = (featureIndex) => {\n          switch (featureIndex) {\n            case 0:\n              return getFileData(\"preview.svg\");\n            case 1:\n              return getFileData(\"devices.svg\");\n            case 2:\n              return getFileData(\"palette.svg\");\n            default:\n              return null;\n          }\n        };\n        section.features.forEach((feature, featureIndex) => {\n          files[`contentSections.${index}.features.${featureIndex}.icon`] =\n            getFeatureMedia(featureIndex);\n        });\n      } else if (section.__component === \"sections.testimonials-group\") {\n        section.logos.forEach((logo, logoIndex) => {\n          files[`contentSections.${index}.logos.${logoIndex}.logo`] =\n            getFileData(\"logo.png\");\n        });\n        section.testimonials.forEach((testimonial, testimonialIndex) => {\n          files[\n            `contentSections.${index}.testimonials.${testimonialIndex}.logo`\n          ] = getFileData(\"logo.png\");\n          files[\n            `contentSections.${index}.testimonials.${testimonialIndex}.picture`\n          ] = getFileData(\"user.png\");\n        });\n      }\n    });\n\n    await createEntry(\"page\", page, files);\n  });\n}\n\nasync function importGlobal() {\n  // Add images\n  const files = {\n    favicon: getFileData(\"favicon.png\"),\n    \"metadata.shareImage\": getFileData(\"undraw-content-team.png\"),\n    \"navbar.logo\": getFileData(\"logo.png\"),\n    \"footer.logo\": getFileData(\"logo.png\"),\n  };\n\n  // Create entry\n  globals.forEach(async (locale) => {\n    await createEntry(\"global\", locale, files);\n  });\n}\n\nasync function importLeadFormSubmissionData() {\n  leadFormSubmissions.forEach(async (submission) => {\n    await createEntry(\"lead-form-submissions\", submission);\n  });\n}\n\nasync function importSeedData() {\n  // Allow read of application content types\n  await setPublicPermissions({\n    global: [\"find\"],\n    page: [\"find\", \"findOne\"],\n    \"lead-form-submission\": [\"create\"],\n  });\n\n  await strapi.query(\"plugin::i18n.locale\").create({\n    data: {\n      name: \"French (fr)\",\n      code: \"fr\",\n    },\n  });\n\n  // Create all entries\n  await importGlobal();\n  await importPages(pages);\n  await importLeadFormSubmissionData();\n}\n\nmodule.exports = async () => {\n  const shouldImportSeedData = await isFirstRun();\n  if (shouldImportSeedData) {\n    try {\n      await importSeedData();\n    } catch (error) {\n      console.log(\"Could not import seed data\");\n      console.error(error);\n    }\n  }\n};\n"
  },
  {
    "path": "packages/templates/corporate/template/src/components/elements/feature-column.json",
    "content": "{\n  \"collectionName\": \"components_slices_feature_columns\",\n  \"info\": {\n    \"name\": \"FeatureColumn\",\n    \"displayName\": \"Feature column\",\n    \"icon\": \"align-center\",\n    \"description\": \"\"\n  },\n  \"options\": {},\n  \"attributes\": {\n    \"title\": {\n      \"type\": \"string\",\n      \"required\": true\n    },\n    \"description\": {\n      \"type\": \"text\"\n    },\n    \"icon\": {\n      \"allowedTypes\": [\n        \"images\"\n      ],\n      \"type\": \"media\",\n      \"multiple\": false,\n      \"required\": true\n    }\n  }\n}\n"
  },
  {
    "path": "packages/templates/corporate/template/src/components/elements/feature-row.json",
    "content": "{\n  \"collectionName\": \"components_slices_feature_rows\",\n  \"info\": {\n    \"name\": \"FeatureRow\",\n    \"displayName\": \"Feature row\",\n    \"icon\": \"arrows-alt-h\",\n    \"description\": \"\"\n  },\n  \"options\": {},\n  \"attributes\": {\n    \"title\": {\n      \"type\": \"string\",\n      \"required\": true\n    },\n    \"description\": {\n      \"type\": \"text\"\n    },\n    \"media\": {\n      \"allowedTypes\": [\n        \"images\",\n        \"videos\"\n      ],\n      \"type\": \"media\",\n      \"multiple\": false,\n      \"required\": true\n    },\n    \"link\": {\n      \"type\": \"component\",\n      \"repeatable\": false,\n      \"component\": \"links.link\"\n    }\n  }\n}\n"
  },
  {
    "path": "packages/templates/corporate/template/src/components/elements/feature.json",
    "content": "{\n  \"collectionName\": \"components_elements_features\",\n  \"info\": {\n    \"name\": \"feature\",\n    \"displayName\": \"Feature\",\n    \"icon\": \"traffic-light\"\n  },\n  \"options\": {},\n  \"attributes\": {\n    \"name\": {\n      \"type\": \"string\"\n    }\n  }\n}\n"
  },
  {
    "path": "packages/templates/corporate/template/src/components/elements/footer-section.json",
    "content": "{\n  \"collectionName\": \"components_links_footer_sections\",\n  \"info\": {\n    \"name\": \"FooterSection\",\n    \"displayName\": \"Footer section\",\n    \"icon\": \"chevron-circle-down\"\n  },\n  \"options\": {},\n  \"attributes\": {\n    \"title\": {\n      \"type\": \"string\"\n    },\n    \"links\": {\n      \"type\": \"component\",\n      \"repeatable\": true,\n      \"component\": \"links.link\"\n    }\n  }\n}\n"
  },
  {
    "path": "packages/templates/corporate/template/src/components/elements/logos.json",
    "content": "{\n  \"collectionName\": \"components_elements_logos\",\n  \"info\": {\n    \"name\": \"logos\",\n    \"displayName\": \"Logos\",\n    \"icon\": \"apple-alt\"\n  },\n  \"options\": {},\n  \"attributes\": {\n    \"title\": {\n      \"type\": \"string\"\n    },\n    \"logo\": {\n      \"allowedTypes\": [\n        \"images\"\n      ],\n      \"type\": \"media\",\n      \"multiple\": false,\n      \"required\": false\n    }\n  }\n}\n"
  },
  {
    "path": "packages/templates/corporate/template/src/components/elements/notification-banner.json",
    "content": "{\n  \"collectionName\": \"components_elements_notification_banners\",\n  \"info\": {\n    \"name\": \"NotificationBanner\",\n    \"displayName\": \"Notification banner\",\n    \"icon\": \"exclamation\"\n  },\n  \"options\": {},\n  \"attributes\": {\n    \"text\": {\n      \"type\": \"richtext\"\n    },\n    \"type\": {\n      \"type\": \"enumeration\",\n      \"enum\": [\n        \"alert\",\n        \"info\",\n        \"warning\"\n      ],\n      \"required\": true\n    }\n  }\n}\n"
  },
  {
    "path": "packages/templates/corporate/template/src/components/elements/plan.json",
    "content": "{\n  \"collectionName\": \"components_elements_plans\",\n  \"info\": {\n    \"name\": \"plan\",\n    \"displayName\": \"Pricing plan\",\n    \"icon\": \"search-dollar\"\n  },\n  \"options\": {},\n  \"attributes\": {\n    \"name\": {\n      \"type\": \"string\"\n    },\n    \"description\": {\n      \"type\": \"text\"\n    },\n    \"features\": {\n      \"type\": \"component\",\n      \"repeatable\": true,\n      \"component\": \"elements.feature\"\n    },\n    \"isRecommended\": {\n      \"type\": \"boolean\"\n    },\n    \"price\": {\n      \"type\": \"decimal\"\n    },\n    \"pricePeriod\": {\n      \"type\": \"string\"\n    }\n  }\n}\n"
  },
  {
    "path": "packages/templates/corporate/template/src/components/elements/testimonial.json",
    "content": "{\n  \"collectionName\": \"components_slices_testimonials\",\n  \"info\": {\n    \"name\": \"Testimonial\",\n    \"displayName\": \"Testimonial\",\n    \"icon\": \"user-check\"\n  },\n  \"options\": {},\n  \"attributes\": {\n    \"logo\": {\n      \"allowedTypes\": [\n        \"images\"\n      ],\n      \"type\": \"media\",\n      \"multiple\": false,\n      \"required\": false\n    },\n    \"picture\": {\n      \"allowedTypes\": [\n        \"images\"\n      ],\n      \"type\": \"media\",\n      \"multiple\": false,\n      \"required\": false\n    },\n    \"text\": {\n      \"type\": \"text\"\n    },\n    \"authorName\": {\n      \"type\": \"string\"\n    },\n    \"authorTitle\": {\n      \"type\": \"string\"\n    },\n    \"link\": {\n      \"type\": \"string\"\n    }\n  }\n}\n"
  },
  {
    "path": "packages/templates/corporate/template/src/components/layout/footer.json",
    "content": "{\n  \"collectionName\": \"components_layout_footers\",\n  \"info\": {\n    \"name\": \"Footer\",\n    \"displayName\": \"Footer\",\n    \"icon\": \"caret-square-down\"\n  },\n  \"options\": {},\n  \"attributes\": {\n    \"logo\": {\n      \"allowedTypes\": [\n        \"images\"\n      ],\n      \"type\": \"media\",\n      \"multiple\": false,\n      \"required\": false\n    },\n    \"columns\": {\n      \"type\": \"component\",\n      \"repeatable\": true,\n      \"component\": \"elements.footer-section\"\n    },\n    \"smallText\": {\n      \"type\": \"string\"\n    }\n  }\n}"
  },
  {
    "path": "packages/templates/corporate/template/src/components/layout/navbar.json",
    "content": "{\n  \"collectionName\": \"components_layout_navbars\",\n  \"info\": {\n    \"name\": \"Navbar\",\n    \"displayName\": \"Navbar\",\n    \"icon\": \"map-signs\",\n    \"description\": \"\"\n  },\n  \"options\": {},\n  \"attributes\": {\n    \"links\": {\n      \"type\": \"component\",\n      \"repeatable\": true,\n      \"component\": \"links.link\"\n    },\n    \"button\": {\n      \"type\": \"component\",\n      \"repeatable\": false,\n      \"component\": \"links.button-link\"\n    },\n    \"logo\": {\n      \"allowedTypes\": [\n        \"images\"\n      ],\n      \"type\": \"media\",\n      \"multiple\": false,\n      \"required\": true\n    }\n  }\n}\n"
  },
  {
    "path": "packages/templates/corporate/template/src/components/links/button-link.json",
    "content": "{\n  \"collectionName\": \"components_links_buttons\",\n  \"info\": {\n    \"name\": \"Button-link\",\n    \"displayName\": \"Button link\",\n    \"icon\": \"fingerprint\",\n    \"description\": \"\"\n  },\n  \"options\": {},\n  \"attributes\": {\n    \"url\": {\n      \"type\": \"string\"\n    },\n    \"newTab\": {\n      \"type\": \"boolean\",\n      \"default\": false\n    },\n    \"text\": {\n      \"type\": \"string\"\n    },\n    \"type\": {\n      \"type\": \"enumeration\",\n      \"enum\": [\n        \"primary\",\n        \"secondary\"\n      ]\n    }\n  }\n}\n"
  },
  {
    "path": "packages/templates/corporate/template/src/components/links/button.json",
    "content": "{\n  \"collectionName\": \"components_links_simple_buttons\",\n  \"info\": {\n    \"name\": \"Button\",\n    \"displayName\": \"Button\",\n    \"icon\": \"fingerprint\",\n    \"description\": \"\"\n  },\n  \"options\": {},\n  \"attributes\": {\n    \"text\": {\n      \"type\": \"string\"\n    },\n    \"type\": {\n      \"type\": \"enumeration\",\n      \"enum\": [\n        \"primary\",\n        \"secondary\"\n      ]\n    }\n  }\n}\n"
  },
  {
    "path": "packages/templates/corporate/template/src/components/links/link.json",
    "content": "{\n  \"collectionName\": \"components_links_links\",\n  \"info\": {\n    \"name\": \"Link\",\n    \"displayName\": \"Link\",\n    \"icon\": \"link\",\n    \"description\": \"\"\n  },\n  \"options\": {},\n  \"attributes\": {\n    \"url\": {\n      \"type\": \"string\",\n      \"required\": true\n    },\n    \"newTab\": {\n      \"type\": \"boolean\",\n      \"default\": false\n    },\n    \"text\": {\n      \"type\": \"string\",\n      \"required\": true\n    }\n  }\n}\n"
  },
  {
    "path": "packages/templates/corporate/template/src/components/meta/metadata.json",
    "content": "{\n  \"collectionName\": \"components_meta_metadata\",\n  \"info\": {\n    \"name\": \"Metadata\",\n    \"displayName\": \"Metadata\",\n    \"icon\": \"robot\"\n  },\n  \"options\": {},\n  \"attributes\": {\n    \"metaTitle\": {\n      \"type\": \"string\",\n      \"required\": true\n    },\n    \"metaDescription\": {\n      \"type\": \"text\",\n      \"required\": true\n    },\n    \"shareImage\": {\n      \"allowedTypes\": [\n        \"images\"\n      ],\n      \"type\": \"media\",\n      \"multiple\": false,\n      \"required\": false\n    },\n    \"twitterCardType\": {\n      \"type\": \"enumeration\",\n      \"enum\": [\n        \"summary\",\n        \"summary_large_image\",\n        \"app\",\n        \"player\"\n      ],\n      \"default\": \"summary\"\n    },\n    \"twitterUsername\": {\n      \"type\": \"string\"\n    }\n  }\n}\n"
  },
  {
    "path": "packages/templates/corporate/template/src/components/sections/bottom-actions.json",
    "content": "{\n  \"collectionName\": \"components_slices_bottom_actions\",\n  \"info\": {\n    \"name\": \"BottomActions\",\n    \"displayName\": \"Bottom actions\",\n    \"icon\": \"angle-double-right\"\n  },\n  \"options\": {},\n  \"attributes\": {\n    \"title\": {\n      \"type\": \"string\"\n    },\n    \"buttons\": {\n      \"type\": \"component\",\n      \"repeatable\": true,\n      \"component\": \"links.button-link\"\n    }\n  }\n}\n"
  },
  {
    "path": "packages/templates/corporate/template/src/components/sections/feature-columns-group.json",
    "content": "{\n  \"collectionName\": \"components_slices_feature_columns_groups\",\n  \"info\": {\n    \"name\": \"FeatureColumnsGroup\",\n    \"displayName\": \"Feature columns group\",\n    \"icon\": \"star-of-life\"\n  },\n  \"options\": {},\n  \"attributes\": {\n    \"features\": {\n      \"type\": \"component\",\n      \"repeatable\": true,\n      \"component\": \"elements.feature-column\"\n    }\n  }\n}\n"
  },
  {
    "path": "packages/templates/corporate/template/src/components/sections/feature-rows-group.json",
    "content": "{\n  \"collectionName\": \"components_slices_feature_rows_groups\",\n  \"info\": {\n    \"name\": \"FeatureRowsGroup\",\n    \"displayName\": \"Feaures row group\",\n    \"icon\": \"bars\"\n  },\n  \"options\": {},\n  \"attributes\": {\n    \"features\": {\n      \"type\": \"component\",\n      \"repeatable\": true,\n      \"component\": \"elements.feature-row\"\n    }\n  }\n}\n"
  },
  {
    "path": "packages/templates/corporate/template/src/components/sections/hero.json",
    "content": "{\n  \"collectionName\": \"components_slices_heroes\",\n  \"info\": {\n    \"name\": \"Hero\",\n    \"displayName\": \"Hero\",\n    \"icon\": \"heading\"\n  },\n  \"options\": {},\n  \"attributes\": {\n    \"title\": {\n      \"type\": \"string\"\n    },\n    \"label\": {\n      \"type\": \"string\"\n    },\n    \"description\": {\n      \"type\": \"string\"\n    },\n    \"picture\": {\n      \"allowedTypes\": [\n        \"images\"\n      ],\n      \"type\": \"media\",\n      \"multiple\": false,\n      \"required\": false\n    },\n    \"smallTextWithLink\": {\n      \"type\": \"richtext\"\n    },\n    \"buttons\": {\n      \"type\": \"component\",\n      \"repeatable\": true,\n      \"component\": \"links.button-link\"\n    }\n  }\n}\n"
  },
  {
    "path": "packages/templates/corporate/template/src/components/sections/large-video.json",
    "content": "{\n  \"collectionName\": \"components_slices_large_videos\",\n  \"info\": {\n    \"name\": \"LargeVideo\",\n    \"displayName\": \"Large video\",\n    \"icon\": \"play-circle\"\n  },\n  \"options\": {},\n  \"attributes\": {\n    \"title\": {\n      \"type\": \"string\"\n    },\n    \"description\": {\n      \"type\": \"string\"\n    },\n    \"video\": {\n      \"allowedTypes\": [\n        \"videos\"\n      ],\n      \"type\": \"media\",\n      \"multiple\": false,\n      \"required\": true\n    },\n    \"poster\": {\n      \"allowedTypes\": [\n        \"images\"\n      ],\n      \"type\": \"media\",\n      \"multiple\": false,\n      \"required\": false\n    }\n  }\n}\n"
  },
  {
    "path": "packages/templates/corporate/template/src/components/sections/lead-form.json",
    "content": "{\n  \"collectionName\": \"components_sections_lead_forms\",\n  \"info\": {\n    \"name\": \"Lead form\",\n    \"displayName\": \"Lead form\",\n    \"icon\": \"at\",\n    \"description\": \"\"\n  },\n  \"options\": {},\n  \"attributes\": {\n    \"title\": {\n      \"type\": \"string\"\n    },\n    \"emailPlaceholder\": {\n      \"type\": \"string\"\n    },\n    \"submitButton\": {\n      \"type\": \"component\",\n      \"repeatable\": false,\n      \"component\": \"links.button\"\n    },\n    \"location\": {\n      \"type\": \"string\"\n    }\n  }\n}\n"
  },
  {
    "path": "packages/templates/corporate/template/src/components/sections/pricing.json",
    "content": "{\n  \"collectionName\": \"components_sections_pricings\",\n  \"info\": {\n    \"name\": \"Pricing\",\n    \"displayName\": \"Pricing\",\n    \"icon\": \"dollar-sign\"\n  },\n  \"options\": {},\n  \"attributes\": {\n    \"title\": {\n      \"type\": \"string\"\n    },\n    \"plans\": {\n      \"type\": \"component\",\n      \"repeatable\": true,\n      \"component\": \"elements.plan\"\n    }\n  }\n}\n"
  },
  {
    "path": "packages/templates/corporate/template/src/components/sections/rich-text.json",
    "content": "{\n  \"collectionName\": \"components_sections_rich_texts\",\n  \"info\": {\n    \"name\": \"RichText\",\n    \"displayName\": \"Rich text\",\n    \"icon\": \"text-height\"\n  },\n  \"options\": {},\n  \"attributes\": {\n    \"content\": {\n      \"type\": \"richtext\"\n    }\n  }\n}\n"
  },
  {
    "path": "packages/templates/corporate/template/src/components/sections/testimonials-group.json",
    "content": "{\n  \"collectionName\": \"components_slices_testimonials_groups\",\n  \"info\": {\n    \"name\": \"TestimonialsGroup\",\n    \"displayName\": \"Testimonials group\",\n    \"icon\": \"user-friends\"\n  },\n  \"options\": {},\n  \"attributes\": {\n    \"title\": {\n      \"type\": \"string\"\n    },\n    \"description\": {\n      \"type\": \"text\"\n    },\n    \"link\": {\n      \"type\": \"component\",\n      \"repeatable\": false,\n      \"component\": \"links.link\"\n    },\n    \"logos\": {\n      \"type\": \"component\",\n      \"repeatable\": true,\n      \"component\": \"elements.logos\"\n    },\n    \"testimonials\": {\n      \"type\": \"component\",\n      \"repeatable\": true,\n      \"component\": \"elements.testimonial\"\n    }\n  }\n}\n"
  },
  {
    "path": "packages/templates/corporate/template/src/index.js",
    "content": "\"use strict\";\n\nconst boostrap = require('./bootstrap');\n\nmodule.exports = {\n  async bootstrap() {\n    await boostrap();\n  },\n}\n"
  },
  {
    "path": "packages/templates/corporate/template.json",
    "content": "{\n  \"package\": {\n    \"dependencies\": {\n      \"@strapi/plugin-graphql\": \"^4.0.0\",\n      \"lodash.set\": \"^4.3.2\"\n    }\n  }\n}\n"
  },
  {
    "path": "packages/templates/ecommerce/README.md",
    "content": "# strapi-template-ecommerce\n\nA Strapi template to create Strapi projects pre-configured for e-commerce apps.\n\n## Usage\n\n```bash\n# Using Yarn\nyarn create strapi-app my-app-name --template ecommerce\n\n# Or using NPM\nnpx create-strapi-app my-app-name --template ecommerce\n```\n\n## Starters\n\nThis template is used by the following starters:\n\n* [Strapi Starter Nuxt.js E-commerce](https://github.com/strapi/strapi-starter-nuxt-e-commerce)\n* [Strapi Starter Next.js E-commerce](https://github.com/strapi/strapi-starter-next-ecommerce)\n"
  },
  {
    "path": "packages/templates/ecommerce/package.json",
    "content": "{\n  \"name\": \"@strapi/template-ecommerce\",\n  \"version\": \"1.0.0\",\n  \"description\": \"Strapi ecommerce template\",\n  \"keywords\": [\n    \"strapi\",\n    \"template\",\n    \"ecommerce\"\n  ],\n  \"homepage\": \"https://github.com/strapi/starters-and-templates#readme\",\n  \"bugs\": {\n    \"url\": \"https://github.com/strapi/starters-and-templates/issues\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/strapi/starters-and-templates.git\"\n  },\n  \"license\": \"MIT\",\n  \"author\": {\n    \"name\": \"Strapi team\",\n    \"email\": \"hi@strapi.io\",\n    \"url\": \"https://strapi.io\"\n  },\n  \"maintainers\": [\n    {\n      \"name\": \"Strapi team\",\n      \"email\": \"hi@strapi.io\",\n      \"url\": \"https://strapi.io\"\n    }\n  ],\n  \"gitHead\": \"d6d5bd264d1df0a0f23b5234d84348f8d78882e3\"\n}\n"
  },
  {
    "path": "packages/templates/ecommerce/template/data/data.js",
    "content": "module.exports = {\n  categories: [\n    {\n      name: \"Back\",\n      slug: \"back\",\n    },\n    {\n      name: \"Front\",\n      slug: \"front\",\n    },\n    {\n      name: \"SSG\",\n      slug: \"ssg\",\n    },\n    {\n      name: \"Container\",\n      slug: \"container\",\n    },\n    {\n      name: \"Database\",\n      slug: \"database\",\n    },\n    {\n      name: \"Other\",\n      slug: \"other\",\n    },\n  ],\n  products: [\n    {\n      title: \"Gatsby\",\n      description:\n        \"Blazing fast modern site generator for React. Go beyond static sites: build blogs, e-commerce sites, full-blown apps, and more with Gatsby.\",\n      price: 1.84,\n      slug: \"gatsby\",\n      status: \"published\",\n      Custom_field: [\n        {\n          title: \"Select the size of your sticker\",\n          required: true,\n          options: \"Small[+0.00]|Medium[+0.40]|Large[+0.80]\",\n        },\n      ],\n      image: null,\n      categories: [\n        {\n          id: 3,\n        },\n      ],\n    },\n    {\n      title: \"Nuxt.js\",\n      description: \"The Intuitive Vue Framework\",\n      price: 1.84,\n      slug: \"nuxt-js\",\n      status: \"published\",\n      Custom_field: [\n        {\n          title: \"Select the size of your sticker\",\n          required: true,\n          options: \"Small[+0.00]|Medium[+0.40]|Large[+0.80]\",\n        },\n      ],\n      image: null,\n      categories: [\n        {\n          id: 3,\n        },\n        {\n          id: 2,\n        },\n      ],\n    },\n    {\n      title: \"Vue.js\",\n      description: \"The Progressive JavaScript Framework.\",\n      price: 1.84,\n      slug: \"vue-js\",\n      status: \"published\",\n      Custom_field: [\n        {\n          title: \"Select the size of your sticker\",\n          required: true,\n          options: \"Small[+0.00]|Medium[+0.40]|Large[+0.80]\",\n        },\n      ],\n      image: null,\n      categories: [\n        {\n          id: 2,\n        },\n      ],\n    },\n    {\n      title: \"Kubernetes\",\n      description: \"Production-Grade Container Orchestration\",\n      price: 1.84,\n      slug: \"kubernetes\",\n      status: \"published\",\n      Custom_field: [\n        {\n          title: \"Select the size of your sticker\",\n          required: true,\n          options: \"Small[+0.00]|Medium[+0.40]|Large[+0.80]\",\n        },\n      ],\n      image: null,\n      categories: [\n        {\n          id: 4,\n        },\n      ],\n    },\n    {\n      title: \"Docker\",\n      description: \"Empowering App Development for Developers\",\n      price: 1.84,\n      slug: \"docker\",\n      status: \"published\",\n      Custom_field: [\n        {\n          title: \"Select the size of your sticker\",\n          required: true,\n          options: \"Small[+0.00]|Medium[+0.40]|Large[+0.80]\",\n        },\n      ],\n      image: null,\n      categories: [\n        {\n          id: 4,\n        },\n      ],\n    },\n    {\n      title: \"Next.js\",\n      description: \"The React Framework\",\n      price: 1.84,\n      slug: \"next-js\",\n      status: \"published\",\n      Custom_field: [\n        {\n          title: \"Select the size of your sticker\",\n          required: true,\n          options: \"Small[+0.00]|Medium[+0.40]|Large[+0.80]\",\n        },\n      ],\n      image: null,\n      categories: [\n        {\n          id: 3,\n        },\n        {\n          id: 2,\n        },\n      ],\n    },\n    {\n      title: \"npm\",\n      description: \"Build amazing things\",\n      price: 1.84,\n      slug: \"npm\",\n      status: \"published\",\n      Custom_field: [\n        {\n          title: \"Select the size of your sticker\",\n          required: true,\n          options: \"Small[+0.00]|Medium[+0.40]|Large[+0.80]\",\n        },\n      ],\n      image: null,\n      categories: [\n        {\n          id: 6,\n        },\n      ],\n    },\n    {\n      title: \"Swift\",\n      description: \"Apple Developer\",\n      price: 1.84,\n      slug: \"swift\",\n      status: \"published\",\n      Custom_field: [\n        {\n          title: \"Select the size of your sticker\",\n          required: true,\n          options: \"Small[+0.00]|Medium[+0.40]|Large[+0.80]\",\n        },\n      ],\n      image: null,\n      categories: [\n        {\n          id: 2,\n        },\n        {\n          id: 1,\n        },\n      ],\n    },\n    {\n      title: \"Rust\",\n      description:\n        \"A language empowering everyone to build reliable and efficient software.\",\n      price: 1.84,\n      slug: \"rust\",\n      status: \"published\",\n      Custom_field: [\n        {\n          title: \"Select the size of your sticker\",\n          required: true,\n          options: \"Small[+0.00]|Medium[+0.40]|Large[+0.80]\",\n        },\n      ],\n      image: null,\n      categories: [\n        {\n          id: 1,\n        },\n      ],\n    },\n    {\n      title: \"SQLite\",\n      description: \"Small. Fast. Reliable. Choose any three\",\n      price: 1.84,\n      slug: \"sq-lite\",\n      status: \"published\",\n      Custom_field: [\n        {\n          title: \"Select the size of your sticker\",\n          required: true,\n          options: \"Small[+0.00]|Medium[+0.40]|Large[+0.80]\",\n        },\n      ],\n      image: null,\n      categories: [\n        {\n          id: 5,\n        },\n      ],\n    },\n    {\n      title: \"PostgreSQL\",\n      description: \"The world's most advanced open source database\",\n      price: 1.84,\n      slug: \"postgre-sql\",\n      status: \"published\",\n      Custom_field: [\n        {\n          title: \"Select the size of your sticker\",\n          required: true,\n          options: \"Small[+0.00]|Medium[+0.40]|Large[+0.80]\",\n        },\n      ],\n      image: null,\n      categories: [\n        {\n          id: 5,\n        },\n      ],\n    },\n    {\n      title: \"Ruby on Rails\",\n      description:\n        \"A web-application framework that includes everything needed to create database-backed web applications according to the Model-View-Controller \",\n      price: 1.84,\n      slug: \"ruby-on-rails\",\n      status: \"published\",\n      Custom_field: [\n        {\n          title: \"Select the size of your sticker\",\n          required: true,\n          options: \"Small[+0.00]|Medium[+0.40]|Large[+0.80]\",\n        },\n      ],\n      image: null,\n      categories: [\n        {\n          id: 2,\n        },\n        {\n          id: 1,\n        },\n      ],\n    },\n    {\n      title: \"MongoDB\",\n      description: \"The most popular database for modern apps\",\n      price: 1.84,\n      slug: \"mongo-db\",\n      status: \"published\",\n      Custom_field: [\n        {\n          title: \"Select the size of your sticker\",\n          required: true,\n          options: \"Small[+0.00]|Medium[+0.40]|Large[+0.80]\",\n        },\n      ],\n      image: null,\n      categories: [\n        {\n          id: 5,\n        },\n      ],\n    },\n    {\n      title: \"Laravel\",\n      description: \"The PHP Framework For Web Artisans\",\n      price: 1.84,\n      slug: \"laravel\",\n      status: \"published\",\n      Custom_field: [\n        {\n          title: \"Select the size of your sticker\",\n          required: true,\n          options: \"Small[+0.00]|Medium[+0.40]|Large[+0.80]\",\n        },\n      ],\n      image: null,\n      categories: [\n        {\n          id: 2,\n        },\n        {\n          id: 1,\n        },\n      ],\n    },\n    {\n      title: \"GraphQL\",\n      description: \"A query language for your API\",\n      price: 1.84,\n      slug: \"graph-ql\",\n      status: \"published\",\n      Custom_field: [\n        {\n          title: \"Select the size of your sticker\",\n          required: true,\n          options: \"Small[+0.00]|Medium[+0.40]|Large[+0.80]\",\n        },\n      ],\n      image: null,\n      categories: [\n        {\n          id: 6,\n        },\n      ],\n    },\n    {\n      title: \"Golang\",\n      description:\n        \"Go is an open source programming language that makes it easy to build simple, reliable, and efficient software.\",\n      price: 1.84,\n      slug: \"golang\",\n      status: \"published\",\n      Custom_field: [\n        {\n          title: \"Select the size of your sticker\",\n          required: true,\n          options: \"Small[+0.00]|Medium[+0.40]|Large[+0.80]\",\n        },\n      ],\n      image: null,\n      categories: [\n        {\n          id: 1,\n        },\n      ],\n    },\n    {\n      title: \"Google cloud\",\n      description: \"Cloud Computing Services\",\n      price: 1.84,\n      slug: \"google-cloud\",\n      status: \"published\",\n      Custom_field: [\n        {\n          title: \"Select the size of your sticker\",\n          required: true,\n          options: \"Small[+0.00]|Medium[+0.40]|Large[+0.80]\",\n        },\n      ],\n      image: null,\n      categories: [\n        {\n          id: 6,\n        },\n      ],\n    },\n    {\n      title: \"C++\",\n      description:\n        'C++ is a general-purpose programming language created by Bjarne Stroustrup as an extension of the C programming language, or \"C with Classes\"',\n      price: 1.84,\n      slug: \"c-plus-plus\",\n      status: \"published\",\n      Custom_field: [\n        {\n          title: \"Select the size of your sticker\",\n          required: true,\n          options: \"Small[+0.00]|Medium[+0.40]|Large[+0.80]\",\n        },\n      ],\n      image: null,\n      categories: [\n        {\n          id: 1,\n        },\n      ],\n    },\n    {\n      title: \"Kotlin\",\n      description:\n        \"A modern programming language that makes developers happier. Open source forever\",\n      price: 1.84,\n      slug: \"kotlin\",\n      status: \"published\",\n      Custom_field: [\n        {\n          title: \"Select the size of your sticker\",\n          required: true,\n          options: \"Small[+0.00]|Medium[+0.40]|Large[+0.80]\",\n        },\n      ],\n      image: null,\n      categories: [\n        {\n          id: 1,\n        },\n        {\n          id: 2,\n        },\n      ],\n    },\n    {\n      title: \"React\",\n      description: \"A JavaScript library for building user interfaces\",\n      price: 1.84,\n      slug: \"react\",\n      status: \"published\",\n      Custom_field: [\n        {\n          title: \"Select the size of your sticker\",\n          required: true,\n          options: \"Small[+0.00]|Medium[+0.40]|Large[+0.80]\",\n        },\n      ],\n      image: null,\n      categories: [\n        {\n          id: 2,\n        },\n      ],\n    },\n    {\n      title: \"Ruby\",\n      description:\n        \"A dynamic, open source programming language with a focus on simplicity and productivity. It has an elegant syntax that is natural to read and easy to write.\",\n      price: 1.84,\n      slug: \"ruby-1\",\n      status: \"published\",\n      Custom_field: [\n        {\n          title: \"Select the size of your sticker\",\n          required: true,\n          options: \"Small[+0.00]|Medium[+0.40]|Large[+0.80]\",\n        },\n      ],\n      image: null,\n      categories: [\n        {\n          id: 1,\n        },\n      ],\n    },\n    {\n      title: \"Python\",\n      description:\n        \"Python is a programming language that lets you work quickly and integrate systems more effectively.\",\n      price: 1.84,\n      slug: \"python\",\n      status: \"published\",\n      Custom_field: [\n        {\n          title: \"Select the size of your sticker\",\n          required: true,\n          options: \"Small[+0.00]|Medium[+0.40]|Large[+0.80]\",\n        },\n      ],\n      image: null,\n      categories: [\n        {\n          id: 1,\n        },\n      ],\n    },\n    {\n      title: \"PHP\",\n      description: \"Hypertext Preprocessor\",\n      price: 1.84,\n      slug: \"php\",\n      status: \"published\",\n      Custom_field: [\n        {\n          title: \"Select the size of your sticker\",\n          required: true,\n          options: \"Small[+0.00]|Medium[+0.40]|Large[+0.80]\",\n        },\n      ],\n      image: null,\n      categories: [\n        {\n          id: 1,\n        },\n      ],\n    },\n    {\n      title: \"Linux\",\n      description:\n        \"Linux is a family of open source Unix-like operating systems based on the Linux kernel, an operating system kernel first released on September 17, 1991, by Linus Torvalds.\",\n      price: 1.84,\n      slug: \"linux\",\n      status: \"published\",\n      Custom_field: [\n        {\n          title: \"Select the size of your sticker\",\n          required: true,\n          options: \"Small[+0.00]|Medium[+0.40]|Large[+0.80]\",\n        },\n      ],\n      image: null,\n      categories: [\n        {\n          id: 6,\n        },\n      ],\n    },\n    {\n      title: \"Java\",\n      description:\n        \"Java is a general-purpose programming language that is class-based, object-oriented, and designed to have as few implementation dependencies as possible.\",\n      price: 1.84,\n      slug: \"java\",\n      status: \"published\",\n      Custom_field: [\n        {\n          title: \"Select the size of your sticker\",\n          required: true,\n          options: \"Small[+0.00]|Medium[+0.40]|Large[+0.80]\",\n        },\n      ],\n      image: null,\n      categories: [\n        {\n          id: 1,\n        },\n      ],\n    },\n    {\n      title: \"Angular\",\n      description: \"One framework. Mobile & desktop.\",\n      price: 1.84,\n      slug: \"angular\",\n      status: \"published\",\n      Custom_field: [\n        {\n          title: \"Select the size of your sticker\",\n          required: true,\n          options: \"Small[+0.00]|Medium[+0.40]|Large[+0.80]\",\n        },\n      ],\n      image: null,\n      categories: [\n        {\n          id: 2,\n        },\n      ],\n    },\n    {\n      title: \"Strapi\",\n      description: \"Design APIs fast, manage content easily.\",\n      price: 1.84,\n      slug: \"strapi\",\n      status: \"published\",\n      Custom_field: [\n        {\n          title: \"Select the size of your sticker\",\n          required: true,\n          options: \"Small[+0.00]|Medium[+0.40]|Large[+0.80]\",\n        },\n      ],\n      image: null,\n      categories: [\n        {\n          id: 1,\n        },\n      ],\n    },\n  ],\n};\n"
  },
  {
    "path": "packages/templates/ecommerce/template/src/api/.gitkeep",
    "content": ""
  },
  {
    "path": "packages/templates/ecommerce/template/src/api/category/content-types/category/schema.json",
    "content": "{\n  \"kind\": \"collectionType\",\n  \"collectionName\": \"categories\",\n  \"info\": {\n    \"singularName\": \"category\",\n    \"pluralName\": \"categories\",\n    \"displayName\": \"Category\",\n    \"name\": \"category\"\n  },\n  \"options\": {\n    \"increments\": true,\n    \"timestamps\": true\n  },\n  \"attributes\": {\n    \"name\": {\n      \"type\": \"string\"\n    },\n    \"slug\": {\n      \"type\": \"uid\",\n      \"targetField\": \"name\"\n    },\n    \"products\": {\n      \"type\": \"relation\",\n      \"relation\": \"manyToMany\",\n      \"target\": \"api::product.product\",\n      \"inversedBy\": \"categories\"\n    }\n  }\n}\n"
  },
  {
    "path": "packages/templates/ecommerce/template/src/api/category/controllers/category.js",
    "content": "'use strict';\n\n/**\n *  category controller\n */\n\nconst { createCoreController } = require('@strapi/strapi').factories;\n\nmodule.exports = createCoreController('api::category.category');\n"
  },
  {
    "path": "packages/templates/ecommerce/template/src/api/category/routes/category.js",
    "content": "'use strict';\n\n/**\n * category router.\n */\n\nconst { createCoreRouter } = require('@strapi/strapi').factories;\n\nmodule.exports = createCoreRouter('api::category.category');\n"
  },
  {
    "path": "packages/templates/ecommerce/template/src/api/category/services/category.js",
    "content": "'use strict';\n\n/**\n * category service.\n */\n\nconst { createCoreService } = require('@strapi/strapi').factories;\n\nmodule.exports = createCoreService('api::category.category');\n"
  },
  {
    "path": "packages/templates/ecommerce/template/src/api/product/content-types/product/schema.json",
    "content": "{\n  \"kind\": \"collectionType\",\n  \"collectionName\": \"products\",\n  \"info\": {\n    \"singularName\": \"product\",\n    \"pluralName\": \"products\",\n    \"displayName\": \"Product\",\n    \"name\": \"product\"\n  },\n  \"options\": {\n    \"increments\": true,\n    \"timestamps\": true\n  },\n  \"attributes\": {\n    \"title\": {\n      \"type\": \"string\",\n      \"required\": true\n    },\n    \"description\": {\n      \"type\": \"string\",\n      \"required\": true\n    },\n    \"price\": {\n      \"type\": \"float\"\n    },\n    \"image\": {\n      \"allowedTypes\": [\n        \"images\"\n      ],\n      \"type\": \"media\",\n      \"multiple\": false\n    },\n    \"slug\": {\n      \"type\": \"uid\",\n      \"targetField\": \"title\"\n    },\n    \"categories\": {\n      \"type\": \"relation\",\n      \"relation\": \"manyToMany\",\n      \"target\": \"api::category.category\",\n      \"inversedBy\": \"products\"\n    },\n    \"Custom_field\": {\n      \"type\": \"component\",\n      \"repeatable\": true,\n      \"component\": \"custom.custom-field\"\n    },\n    \"status\": {\n      \"type\": \"enumeration\",\n      \"enum\": [\n        \"draft\",\n        \"published\"\n      ],\n      \"default\": \"published\",\n      \"required\": true\n    }\n  }\n}\n"
  },
  {
    "path": "packages/templates/ecommerce/template/src/api/product/controllers/product.js",
    "content": "'use strict';\n\n/**\n *  product controller\n */\n\nconst { createCoreController } = require('@strapi/strapi').factories;\n\nmodule.exports = createCoreController('api::product.product');\n"
  },
  {
    "path": "packages/templates/ecommerce/template/src/api/product/routes/product.js",
    "content": "'use strict';\n\n/**\n * product router.\n */\n\nconst { createCoreRouter } = require('@strapi/strapi').factories;\n\nmodule.exports = createCoreRouter('api::product.product');\n"
  },
  {
    "path": "packages/templates/ecommerce/template/src/api/product/services/product.js",
    "content": "'use strict';\n\n/**\n * product service.\n */\n\nconst { createCoreService } = require('@strapi/strapi').factories;\n\nmodule.exports = createCoreService('api::product.product');\n"
  },
  {
    "path": "packages/templates/ecommerce/template/src/bootstrap.js",
    "content": "\"use strict\";\n\nconst fs = require(\"fs\");\nconst path = require(\"path\");\nconst mime = require(\"mime-types\");\nconst set = require(\"lodash.set\");\n\nconst { categories, products } = require(\"../data/data\");\n\nasync function setPublicPermissions(newPermissions) {\n  // Find the ID of the public role\n  const publicRole = await strapi\n    .query(\"plugin::users-permissions.role\")\n    .findOne({\n      where: {\n        type: \"public\",\n      },\n    });\n\n  // Create the new permissions and link them to the public role\n  const allPermissionsToCreate = [];\n  Object.keys(newPermissions).map((controller) => {\n    const actions = newPermissions[controller];\n    const permissionsToCreate = actions.map((action) => {\n      return strapi.query(\"plugin::users-permissions.permission\").create({\n        data: {\n          action: `api::${controller}.${controller}.${action}`,\n          role: publicRole.id,\n        },\n      });\n    });\n    allPermissionsToCreate.push(...permissionsToCreate);\n  });\n  await Promise.all(allPermissionsToCreate);\n}\n\nasync function isFirstRun() {\n  const pluginStore = strapi.store({\n    environment: strapi.config.environment,\n    type: \"type\",\n    name: \"setup\",\n  });\n  const initHasRun = await pluginStore.get({ key: \"initHasRun\" });\n  await pluginStore.set({ key: \"initHasRun\", value: true });\n  return !initHasRun;\n}\n\nfunction getFileSizeInBytes(filePath) {\n  const stats = fs.statSync(filePath);\n  const fileSizeInBytes = stats[\"size\"];\n  return fileSizeInBytes;\n}\n\nfunction getFileData(fileName) {\n  const filePath = `./data/uploads/${fileName}`;\n\n  // Parse the file metadata\n  const size = getFileSizeInBytes(filePath);\n  const ext = fileName.split(\".\").pop();\n  const mimeType = mime.lookup(ext);\n\n  return {\n    path: filePath,\n    name: fileName,\n    size,\n    type: mimeType,\n  };\n}\n\n// Create an entry and attach files if there are any\nasync function createEntry({ model, entry, files }) {\n  try {\n    if (files) {\n      for (const [key, file] of Object.entries(files)) {\n        // Get file name without the extension\n        const [fileName] = file.name.split(\".\");\n        // Upload each individual file\n        const uploadedFile = await strapi\n          .plugin(\"upload\")\n          .service(\"upload\")\n          .upload({\n            files: file,\n            data: {\n              fileInfo: {\n                alternativeText: fileName,\n                caption: fileName,\n                name: fileName,\n              },\n            },\n          });\n\n        // Attach each file to its entry\n        set(entry, key, uploadedFile[0].id);\n      }\n    }\n\n    // Actually create the entry in Strapi\n    const createdEntry = await strapi.entityService.create(\n      `api::${model}.${model}`,\n      {\n        data: entry,\n      }\n    );\n  } catch (e) {\n    console.log(\"model\", entry, e);\n  }\n}\n\nasync function importCategories() {\n  return Promise.all(\n    categories.map((category) => {\n      return createEntry({ model: \"category\", entry: category });\n    })\n  );\n}\n\nasync function importProducts() {\n  return Promise.all(\n    products.map(async (product) => {\n      const files = {\n        image: getFileData(`${product.slug}.png`),\n      };\n      return createEntry({\n        model: \"product\",\n        entry: product,\n        files,\n      });\n    })\n  );\n}\n\nasync function importSeedData() {\n  // Allow read of application content types\n  await setPublicPermissions({\n    category: [\"find\", \"findOne\"],\n    product: [\"find\", \"findOne\"],\n  });\n\n  // Create all entries\n  await importCategories(categories);\n  await importProducts(products);\n}\n\nmodule.exports = async () => {\n  const shouldImportSeedData = await isFirstRun();\n  if (shouldImportSeedData) {\n    try {\n      await importSeedData();\n    } catch (error) {\n      console.log(\"Could not import seed data\");\n      console.error(error);\n    }\n  }\n};\n"
  },
  {
    "path": "packages/templates/ecommerce/template/src/components/custom/custom-field.json",
    "content": "{\n  \"collectionName\": \"components_custom_custom_fields\",\n  \"info\": {\n    \"name\": \"Custom_field\",\n    \"displayName\": \"Custom field\",\n    \"icon\": \"archway\"\n  },\n  \"options\": {},\n  \"attributes\": {\n    \"title\": {\n      \"type\": \"string\"\n    },\n    \"required\": {\n      \"type\": \"boolean\"\n    },\n    \"options\": {\n      \"type\": \"string\"\n    }\n  }\n}\n"
  },
  {
    "path": "packages/templates/ecommerce/template/src/index.js",
    "content": "\"use strict\";\n\nconst bootstrap = require('./bootstrap');\n\nmodule.exports = {\n  async bootstrap() {\n    await bootstrap();\n  },\n}\n"
  },
  {
    "path": "packages/templates/ecommerce/template.json",
    "content": "{\n  \"package\": {\n    \"dependencies\": {\n      \"@strapi/plugin-graphql\": \"^4.0.0\",\n      \"lodash.set\": \"^4.3.2\",\n      \"mime-types\": \"^2.1.27\"\n    }\n  }\n}\n"
  }
]