@bigbinary/neeto-seo-frontend 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,170 @@
1
+ # neeto-seo-nano
2
+
3
+ The `neeto-seo-nano` manages SEO settings across Neeto applications.
4
+
5
+ ## Contents
6
+
7
+ 1. [Development with Host Application](#development-with-host-application)
8
+ 2. [Instructions for Publishing](#instructions-for-publishing)
9
+
10
+ ## Development with Host Application
11
+
12
+ #### Installation
13
+
14
+ 1. Add this line to your application's Gemfile:
15
+
16
+ ```ruby
17
+ source "NEETO_GEM_SERVER_URL" do
18
+ # ..existing gems
19
+
20
+ gem "neeto-seo-engine"
21
+ end
22
+ ```
23
+
24
+ 2. And then execute:
25
+
26
+ ```shell
27
+ bundle install
28
+ ```
29
+
30
+ 3. Run migrations
31
+
32
+ ```shell
33
+ bundle exec rails neeto_seo_engine:install:migrations
34
+ bundle exec rails db:migrate
35
+ ```
36
+
37
+ #### Usage
38
+
39
+ 1. Add association
40
+
41
+ ```ruby
42
+ class BlogPost > ApplicationRecord
43
+ has_one :seo_setting, as: :entity, class_name: "NeetoSeoEngine::SeoSetting", dependent: :destroy
44
+ end
45
+ ```
46
+
47
+ 2. Add controller
48
+
49
+ All common controller logic is extracted to the engine. However, we still
50
+ need to load some records from the host app controller.
51
+
52
+ ```ruby
53
+ class Api::V1::Admin::BlogPosts::SeoSettingsController < NeetoSeoEngine::Api::V1::SeoSettingsController
54
+ private
55
+
56
+ def load_entity!
57
+ @entity = @organization.blog_posts.find(params[:blog_post_id])
58
+ end
59
+ end
60
+ ```
61
+
62
+ 3. Include the following module to your application's `config/routes.rb` file:
63
+
64
+ ```ruby
65
+ include NeetoSeoEngine::Routes::Draw
66
+
67
+ ```
68
+
69
+ 4. Define required routes.
70
+
71
+ ```ruby
72
+
73
+ # routes/admin.rb
74
+ seo_setting_routes
75
+ ```
76
+
77
+ 5. Create a file in the host app under `app/lib/neeto_seo_engine/callbacks.rb` &
78
+ provide the permission (If you don't have permissions, then simply return
79
+ true)
80
+
81
+ ```ruby
82
+ module NeetoSeoEngine
83
+ class Callbacks
84
+ def self.can_manage_seo_settings?(user)
85
+ user.has_permission?("neeto_seo_engine.manage_seo_settings")
86
+ end
87
+ end
88
+ end
89
+ ```
90
+
91
+ Note: If this implementation is missing in the host application, the policy
92
+ will default to checking
93
+ `user.has_permission?("neeto_seo_engine.manage_seo_settings")`.
94
+
95
+ 6. Add the frontend component to your desired path. There are two components in
96
+ the package:
97
+
98
+ 1. [SeoSettingsPane](app/javascript/src/components/SeoSettings/Pane/index.jsx)
99
+ 2. [SeoSettingsPage](app/javascript/src/components/SeoSettings/Page.jsx)
100
+
101
+ The `SeoSettingsPage` includes the `SeoSettingsPane` within it. The
102
+ `SeoSettingsPage` is used for entities that require a full-page component,
103
+ while the `SeoSettingsPane` is intended for cases where only a pane is
104
+ required.
105
+
106
+ Here is an example of the usage of `SeoSettingsPane`:
107
+
108
+ ```jsx
109
+ const SeoSettings = ({ isOpen, onClose }) => {
110
+ const { blogPostId } = useParams();
111
+
112
+ const SEO_FORM_FIELDS = [
113
+ {
114
+ name: "title",
115
+ helpDocUrl: "https://help.neetopublish.com/#1-page-title",
116
+ maxLength: 80,
117
+ },
118
+ {
119
+ name: "description",
120
+ altLabel: "Meta Description",
121
+ altPlaceholder: "Enter meta Description",
122
+ helpDocUrl: "https://help.neetopublish.com/#2-meta-description",
123
+ maxLength: 150,
124
+ },
125
+ {
126
+ name: "keywords",
127
+ helpDocUrl: "https://help.neetopublish.com/#3-keywords",
128
+ maxLength: 150,
129
+ popOverDescription: "Some description about Meta keywords",
130
+ },
131
+ { name: "allowSearchEngineCrawl" },
132
+ { name: "googleAnalyticsTrackingId" },
133
+ { name: "coverImage" },
134
+ ];
135
+
136
+ return (
137
+ <SeoSettingsPane
138
+ {...{ isOpen, onClose }}
139
+ apiEndpointUrl={`${BASE_API_V1_URL}/admin/blog_posts/${blogPostId}/seo_setting`}
140
+ defaultHelpDocUrl="https://help.neetopublish.com/search-engine-optimization"
141
+ entity="Page"
142
+ seoFormFields={SEO_FORM_FIELDS}
143
+ additionalComponent={<ImageUploader />}
144
+ />
145
+ );
146
+ };
147
+ ```
148
+
149
+ You need to build SEO form fields with the desired fields required in your
150
+ engine. The allowed fields are `title`, `titleSuffix`, `keywords`, and
151
+ `description`, `allowSearchEngineToCrawl`, `googleAnalyticsTrackingId`, and
152
+ `coverImage`. These components will be generated accordingly. You can pass
153
+ the corresponding `helpDocUrl`, `popOverDescription`, and `maxLength`. The
154
+ `name` is mandatory to generate the component.
155
+
156
+ You can also pass an `additionalComponent` prop to append an additional
157
+ component of your choice in the `SeoSettingsPane`.
158
+
159
+ You must pass the `entity` prop, which can be **Page**, **Article**,
160
+ **Site**, or any other entity of your choice.
161
+
162
+ The [SeoSettingsPage](app/javascript/src/components/SeoSettings/Page.jsx) has
163
+ similar props. Additionally, you can pass the `breadcrumbs` and
164
+ `helpPopoverDescription` of the page.
165
+
166
+ ## Instructions for Publishing
167
+
168
+ Consult the
169
+ [building and releasing packages](https://neeto-engineering.neetokb.com/articles/building-and-releasing-packages)
170
+ guide for details on how to publish.
@@ -0,0 +1,45 @@
1
+ {
2
+ "neetoSeo": {
3
+ "title": "Search engine optimization",
4
+ "manageSeoSettings": "Manage SEO settings",
5
+ "readHelpArticle": "<Link>Read more</Link> to learn more about configuring the SEO settings",
6
+ "buttons": {
7
+ "configure": "Configure"
8
+ },
9
+ "helpPopovers": {
10
+ "seo": "You can configure SEO settings by using the meta description, keywords to improve visibility, and more.",
11
+ "title": "Specifies the title of the web page. It is displayed as part of the search snippet on the search engine results page. It appears as a clickable headline for the search result.",
12
+ "titleSuffix": "It is added as the suffix to page titles on all the pages.",
13
+ "description": "Meta description provides a summary of the web page. It is displayed as part of the search snippet on the search engine results page. It is meant to give the user a brief idea of what the {{entity}} is about and how it relates to their search query.",
14
+ "keywords": "Keywords assist search engines in understanding the content of the {{entity}}, thereby enhancing its ranking in search results.",
15
+ "googleAnalytics": "Connect your Google Analytics account & monitor the performance of your {{entity}} using Google Analytics.",
16
+ "coverImage": "Cover image is the image that appears on the top of the web page. It is displayed as part of search snippet in search engine results page.",
17
+ "allowSearchEngineCrawl": "Enable search engine indexing, helping them appear in search results. "
18
+ },
19
+ "labels": {
20
+ "viewHelpArticle": "View help article",
21
+ "title": "{{entity}} title",
22
+ "titleSuffix": "{{entity}} title suffix",
23
+ "description": "Meta description",
24
+ "keywords": "Keywords",
25
+ "allowSearchEngineCrawl": "Allow search engines to index this {{entity}}",
26
+ "googleAnalytics": "Google analytics",
27
+ "googleAnalyticsTrackingId": "Google analytics tracking ID",
28
+ "coverImage": "Cover image"
29
+ },
30
+ "placeholders": {
31
+ "title": "Enter {{entity}} title",
32
+ "titleSuffix": "Enter {{entity}} title suffix",
33
+ "description": "Enter meta description",
34
+ "keywords": "Enter keywords",
35
+ "googleAnalyticsTrackingId": "UA-000000-2"
36
+ },
37
+ "validations": {
38
+ "valid": "{{what}} must be valid",
39
+ "required": "{{what}} is required"
40
+ },
41
+ "subLabels": {
42
+ "keywords": "Keywords need to be comma separated"
43
+ }
44
+ }
45
+ }
@@ -0,0 +1,3 @@
1
+ import en from "./en.json";
2
+
3
+ export { en };
@@ -0,0 +1,132 @@
1
+ import _slicedToArray from '@babel/runtime/helpers/slicedToArray';
2
+ import { memo, useState } from 'react';
3
+ import Container from '@bigbinary/neeto-molecules/Container';
4
+ import Typography from '@bigbinary/neetoui/Typography';
5
+ import Button from '@bigbinary/neetoui/Button';
6
+ import { useTranslation, Trans } from 'react-i18next';
7
+ import NeetoHeader from '@bigbinary/neeto-molecules/Header';
8
+ import HelpPopover from '@bigbinary/neeto-molecules/HelpPopover';
9
+ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
10
+ import Pane from './SeoSettingsPane.js';
11
+ import '@bigbinary/neeto-molecules/PageLoader';
12
+ import '@bigbinary/neetoui/Pane';
13
+ import '@bigbinary/neetoui/formik/Form';
14
+ import '@bigbinary/neetoui/formik/ActionBlock';
15
+ import 'ramda';
16
+ import '@babel/runtime/helpers/defineProperty';
17
+ import '@tanstack/react-query';
18
+ import '@bigbinary/neeto-commons-frontend/react-utils/useMutationWithInvalidation';
19
+ import 'axios';
20
+ import 'i18next';
21
+ import 'yup';
22
+ import '@bigbinary/neeto-image-uploader-frontend/ImageUploader';
23
+ import 'classnames';
24
+ import 'formik';
25
+ import '@bigbinary/neeto-cist';
26
+ import '@bigbinary/neetoui/Label';
27
+ import '@bigbinary/neetoui/formik/Switch';
28
+ import '@bigbinary/neetoui/formik/Input';
29
+ import '@bigbinary/neeto-commons-frontend/react-utils';
30
+ import '@bigbinary/neetoui/formik/Textarea';
31
+
32
+ var Header = function Header(_ref) {
33
+ var breadcrumbs = _ref.breadcrumbs,
34
+ helpDocUrl = _ref.helpDocUrl,
35
+ _ref$helpPopoverDescr = _ref.helpPopoverDescription,
36
+ helpPopoverDescription = _ref$helpPopoverDescr === void 0 ? "" : _ref$helpPopoverDescr;
37
+ var _useTranslation = useTranslation(),
38
+ t = _useTranslation.t;
39
+ var title = /*#__PURE__*/jsxs("div", {
40
+ className: "flex items-center gap-x-2",
41
+ children: [/*#__PURE__*/jsx(Typography, {
42
+ style: "h2",
43
+ children: t("neetoSeo.title")
44
+ }), /*#__PURE__*/jsx(HelpPopover, {
45
+ description: helpPopoverDescription || t("neetoSeo.helpPopovers.seo"),
46
+ helpLinkProps: {
47
+ href: helpDocUrl
48
+ },
49
+ popoverProps: {
50
+ position: "top"
51
+ },
52
+ title: t("neetoSeo.title")
53
+ })]
54
+ });
55
+ return /*#__PURE__*/jsx(NeetoHeader, {
56
+ breadcrumbs: breadcrumbs,
57
+ title: title,
58
+ size: "large"
59
+ });
60
+ };
61
+ var Header$1 = /*#__PURE__*/memo(Header);
62
+
63
+ var Page = function Page(_ref) {
64
+ var breadcrumbs = _ref.breadcrumbs,
65
+ helpDocUrl = _ref.helpDocUrl,
66
+ entity = _ref.entity,
67
+ seoFormFields = _ref.seoFormFields,
68
+ apiEndpointUrl = _ref.apiEndpointUrl,
69
+ _ref$helpPopoverDescr = _ref.helpPopoverDescription,
70
+ helpPopoverDescription = _ref$helpPopoverDescr === void 0 ? "" : _ref$helpPopoverDescr,
71
+ _ref$additionalCompon = _ref.additionalComponent,
72
+ additionalComponent = _ref$additionalCompon === void 0 ? null : _ref$additionalCompon;
73
+ var _useTranslation = useTranslation(),
74
+ t = _useTranslation.t;
75
+ var _useState = useState(false),
76
+ _useState2 = _slicedToArray(_useState, 2),
77
+ isPaneOpen = _useState2[0],
78
+ setIsPaneOpen = _useState2[1];
79
+ var openPane = function openPane() {
80
+ return setIsPaneOpen(true);
81
+ };
82
+ var closePane = function closePane() {
83
+ return setIsPaneOpen(false);
84
+ };
85
+ return /*#__PURE__*/jsxs(Fragment, {
86
+ children: [/*#__PURE__*/jsxs(Container, {
87
+ children: [/*#__PURE__*/jsx(Header$1, {
88
+ breadcrumbs: breadcrumbs,
89
+ helpDocUrl: helpDocUrl,
90
+ helpPopoverDescription: helpPopoverDescription
91
+ }), /*#__PURE__*/jsx("div", {
92
+ className: "mt-10 flex w-full justify-center",
93
+ children: /*#__PURE__*/jsxs("div", {
94
+ className: "neeto-ui-border-gray-300 neeto-ui-rounded w-1/2 space-y-4 border p-6",
95
+ children: [/*#__PURE__*/jsx(Typography, {
96
+ style: "h2",
97
+ children: t("neetoSeo.manageSeoSettings")
98
+ }), /*#__PURE__*/jsx(Button, {
99
+ label: t("neetoSeo.buttons.configure"),
100
+ onClick: openPane
101
+ }), /*#__PURE__*/jsx("div", {
102
+ className: "space-y-1"
103
+ }), /*#__PURE__*/jsx(Typography, {
104
+ style: "body2",
105
+ children: /*#__PURE__*/jsx(Trans, {
106
+ i18nKey: "neetoSeo.readHelpArticle",
107
+ components: {
108
+ Link: /*#__PURE__*/jsx(Button, {
109
+ href: helpDocUrl,
110
+ rel: "noreferrer",
111
+ style: "link",
112
+ target: "_blank"
113
+ })
114
+ }
115
+ })
116
+ })]
117
+ })
118
+ })]
119
+ }), /*#__PURE__*/jsx(Pane, {
120
+ additionalComponent: additionalComponent,
121
+ apiEndpointUrl: apiEndpointUrl,
122
+ entity: entity,
123
+ seoFormFields: seoFormFields,
124
+ defaultHelpDocUrl: helpDocUrl,
125
+ isOpen: isPaneOpen,
126
+ onClose: closePane
127
+ })]
128
+ });
129
+ };
130
+
131
+ export { Page as default };
132
+ //# sourceMappingURL=SeoSettingsPage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SeoSettingsPage.js","sources":["../app/javascript/src/components/SeoSettings/Header.jsx","../app/javascript/src/components/SeoSettings/Page.jsx"],"sourcesContent":["import { memo } from \"react\";\n\nimport NeetoHeader from \"neetomolecules/Header\";\nimport HelpPopover from \"neetomolecules/HelpPopover\";\nimport { Typography } from \"neetoui\";\nimport { useTranslation } from \"react-i18next\";\n\nconst Header = ({ breadcrumbs, helpDocUrl, helpPopoverDescription = \"\" }) => {\n const { t } = useTranslation();\n\n const title = (\n <div className=\"flex items-center gap-x-2\">\n <Typography style=\"h2\">{t(\"neetoSeo.title\")}</Typography>\n <HelpPopover\n description={helpPopoverDescription || t(\"neetoSeo.helpPopovers.seo\")}\n helpLinkProps={{ href: helpDocUrl }}\n popoverProps={{ position: \"top\" }}\n title={t(\"neetoSeo.title\")}\n />\n </div>\n );\n\n return <NeetoHeader {...{ breadcrumbs, title }} size=\"large\" />;\n};\n\nexport default memo(Header);\n","import { useState } from \"react\";\n\nimport Container from \"neetomolecules/Container\";\nimport { Typography, Button } from \"neetoui\";\nimport { useTranslation, Trans } from \"react-i18next\";\n\nimport Header from \"./Header\";\nimport Pane from \"./Pane\";\n\nconst Page = ({\n breadcrumbs,\n helpDocUrl,\n entity,\n seoFormFields,\n apiEndpointUrl,\n helpPopoverDescription = \"\",\n additionalComponent = null,\n}) => {\n const { t } = useTranslation();\n\n const [isPaneOpen, setIsPaneOpen] = useState(false);\n\n const openPane = () => setIsPaneOpen(true);\n const closePane = () => setIsPaneOpen(false);\n\n return (\n <>\n <Container>\n <Header {...{ breadcrumbs, helpDocUrl, helpPopoverDescription }} />\n <div className=\"mt-10 flex w-full justify-center\">\n <div className=\"neeto-ui-border-gray-300 neeto-ui-rounded w-1/2 space-y-4 border p-6\">\n <Typography style=\"h2\">\n {t(\"neetoSeo.manageSeoSettings\")}\n </Typography>\n <Button\n label={t(\"neetoSeo.buttons.configure\")}\n onClick={openPane}\n />\n <div className=\"space-y-1\" />\n <Typography style=\"body2\">\n <Trans\n i18nKey=\"neetoSeo.readHelpArticle\"\n components={{\n Link: (\n <Button\n href={helpDocUrl}\n rel=\"noreferrer\"\n style=\"link\"\n target=\"_blank\"\n />\n ),\n }}\n />\n </Typography>\n </div>\n </div>\n </Container>\n <Pane\n {...{ additionalComponent, apiEndpointUrl, entity, seoFormFields }}\n defaultHelpDocUrl={helpDocUrl}\n isOpen={isPaneOpen}\n onClose={closePane}\n />\n </>\n );\n};\n\nexport default Page;\n"],"names":["Header","_ref","breadcrumbs","helpDocUrl","_ref$helpPopoverDescr","helpPopoverDescription","_useTranslation","useTranslation","t","title","_jsxs","className","children","_jsx","Typography","style","HelpPopover","description","helpLinkProps","href","popoverProps","position","NeetoHeader","size","memo","Page","entity","seoFormFields","apiEndpointUrl","_ref$additionalCompon","additionalComponent","_useState","useState","_useState2","_slicedToArray","isPaneOpen","setIsPaneOpen","openPane","closePane","_Fragment","Container","Button","label","onClick","Trans","i18nKey","components","Link","rel","target","Pane","defaultHelpDocUrl","isOpen","onClose"],"mappings":"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"}