From 5ba5100a524cdcb45189f118033bcc075bf95d01 Mon Sep 17 00:00:00 2001 From: cleverbeagle Date: Tue, 30 May 2017 00:28:34 -0500 Subject: [PATCH] add static pages --- imports/api/Utility/server/methods.js | 11 +++++ imports/modules/parse-markdown.js | 8 ++++ imports/modules/server/get-private-file.js | 3 ++ imports/startup/server/api.js | 2 + imports/ui/components/Content/Content.js | 16 +++++++ imports/ui/components/Content/Content.scss | 36 ++++++++++++++ imports/ui/components/Footer/Footer.scss | 4 +- .../ui/components/PageHeader/PageHeader.js | 24 ++++++++++ .../ui/components/PageHeader/PageHeader.scss | 47 +++++++++++++++++++ imports/ui/layouts/App/App.js | 4 ++ imports/ui/pages/Index/Index.js | 3 ++ imports/ui/pages/Index/Index.scss | 29 +++++++++--- imports/ui/pages/Page/Page.js | 44 +++++++++++++++++ imports/ui/pages/Page/Page.scss | 11 +++++ imports/ui/pages/Privacy/Privacy.js | 14 ++++++ imports/ui/pages/Terms/Terms.js | 14 ++++++ package.json | 1 + private/pages/privacy.md | 18 +++++++ private/pages/terms.md | 38 +++++++++++++++ 19 files changed, 320 insertions(+), 7 deletions(-) create mode 100644 imports/api/Utility/server/methods.js create mode 100644 imports/modules/parse-markdown.js create mode 100644 imports/modules/server/get-private-file.js create mode 100644 imports/ui/components/Content/Content.js create mode 100644 imports/ui/components/Content/Content.scss create mode 100644 imports/ui/components/PageHeader/PageHeader.js create mode 100644 imports/ui/components/PageHeader/PageHeader.scss create mode 100644 imports/ui/pages/Page/Page.js create mode 100644 imports/ui/pages/Page/Page.scss create mode 100644 imports/ui/pages/Privacy/Privacy.js create mode 100644 imports/ui/pages/Terms/Terms.js create mode 100644 private/pages/privacy.md create mode 100644 private/pages/terms.md diff --git a/imports/api/Utility/server/methods.js b/imports/api/Utility/server/methods.js new file mode 100644 index 0000000..f424e84 --- /dev/null +++ b/imports/api/Utility/server/methods.js @@ -0,0 +1,11 @@ +import { Meteor } from 'meteor/meteor'; +import { check } from 'meteor/check'; +import getPrivateFile from '../../../modules/server/get-private-file'; +import parseMarkdown from '../../../modules/parse-markdown'; + +Meteor.methods({ + 'utility.getPage': function utilityReadFileAsText(fileName) { + check(fileName, String); + return parseMarkdown(getPrivateFile(`pages/${fileName}.md`)); + }, +}); diff --git a/imports/modules/parse-markdown.js b/imports/modules/parse-markdown.js new file mode 100644 index 0000000..9ccaffd --- /dev/null +++ b/imports/modules/parse-markdown.js @@ -0,0 +1,8 @@ +import { Parser, HtmlRenderer } from 'commonmark'; + +export default (markdown, options) => { + const reader = new Parser(); + const writer = options ? new HtmlRenderer(options) : new HtmlRenderer(); + const parsed = reader.parse(markdown); + return writer.render(parsed); +}; diff --git a/imports/modules/server/get-private-file.js b/imports/modules/server/get-private-file.js new file mode 100644 index 0000000..d2204d8 --- /dev/null +++ b/imports/modules/server/get-private-file.js @@ -0,0 +1,3 @@ +import fs from 'fs'; + +export default path => fs.readFileSync(`assets/app/${path}`, 'utf8'); diff --git a/imports/startup/server/api.js b/imports/startup/server/api.js index 18a93a0..e351317 100644 --- a/imports/startup/server/api.js +++ b/imports/startup/server/api.js @@ -5,3 +5,5 @@ import '../../api/OAuth/server/methods'; import '../../api/Users/server/methods'; import '../../api/Users/server/publications'; + +import '../../api/Utility/server/methods'; diff --git a/imports/ui/components/Content/Content.js b/imports/ui/components/Content/Content.js new file mode 100644 index 0000000..b38d9ca --- /dev/null +++ b/imports/ui/components/Content/Content.js @@ -0,0 +1,16 @@ +/* eslint-disable react/no-danger */ + +import React from 'react'; +import PropTypes from 'prop-types'; + +import './Content.scss'; + +const Content = ({ content }) => ( +
+); + +Content.propTypes = { + content: PropTypes.string.isRequired, +}; + +export default Content; diff --git a/imports/ui/components/Content/Content.scss b/imports/ui/components/Content/Content.scss new file mode 100644 index 0000000..a1573ea --- /dev/null +++ b/imports/ui/components/Content/Content.scss @@ -0,0 +1,36 @@ +@import '../../stylesheets/mixins'; + +.Content { + max-width: 700px; + margin: 0 auto; + font-size: 14px; + line-height: 22px; + + h1, + h2, + h3, + h4, + h5, + h6 { + margin: 30px 0 20px; + } + + p { + margin-bottom: 20px; + } + + > *:first-child { + margin-top: 0px; + } + + > *:last-child { + margin-bottom: 0px; + } +} + +@include breakpoint(tablet) { + .Content { + font-size: 16px; + line-height: 22px; + } +} diff --git a/imports/ui/components/Footer/Footer.scss b/imports/ui/components/Footer/Footer.scss index 11110bc..d0cb740 100644 --- a/imports/ui/components/Footer/Footer.scss +++ b/imports/ui/components/Footer/Footer.scss @@ -39,7 +39,9 @@ body { color: $gray-light; } - a:hover { + a:hover, + a:active, + a:focus { text-decoration: none; color: $gray; } diff --git a/imports/ui/components/PageHeader/PageHeader.js b/imports/ui/components/PageHeader/PageHeader.js new file mode 100644 index 0000000..93b5c61 --- /dev/null +++ b/imports/ui/components/PageHeader/PageHeader.js @@ -0,0 +1,24 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +import './PageHeader.scss'; + +const PageHeader = ({ title, subtitle }) => ( +
+
+

{title}

+ {subtitle ?

{subtitle}

: ''} +
+
+); + +PageHeader.defaultProps = { + subtitle: '', +}; + +PageHeader.propTypes = { + title: PropTypes.string.isRequired, + subtitle: PropTypes.string, +}; + +export default PageHeader; diff --git a/imports/ui/components/PageHeader/PageHeader.scss b/imports/ui/components/PageHeader/PageHeader.scss new file mode 100644 index 0000000..3f7216f --- /dev/null +++ b/imports/ui/components/PageHeader/PageHeader.scss @@ -0,0 +1,47 @@ +@import '../../stylesheets/mixins'; +@import '../../stylesheets/colors'; + +.PageHeader { + border-bottom: 1px solid $gray-lighter; + padding: 0px 0 20px; + margin-bottom: 20px; + + h1 { + font-size: 20px; + font-weight: 600; + margin: 0; + } + + p { + font-size: 14px; + margin-top: 10px; + margin-bottom: 0; + color: $gray-light; + } +} + +.PageHeader-container { + text-align: center; +} + +@include breakpoint(tablet) { + .PageHeader { + padding: 10px 0 30px; + margin-bottom: 30px; + + h1 { + font-size: 24px; + } + + p { + font-size: 16px; + } + } +} + +@include breakpoint(desktop) { + .PageHeader { + padding: 20px 0 40px; + margin-bottom: 40px; + } +} diff --git a/imports/ui/layouts/App/App.js b/imports/ui/layouts/App/App.js index e3f1986..da65b43 100644 --- a/imports/ui/layouts/App/App.js +++ b/imports/ui/layouts/App/App.js @@ -21,6 +21,8 @@ import ResetPassword from '../../pages/ResetPassword/ResetPassword'; import Profile from '../../pages/Profile/Profile'; import NotFound from '../../pages/NotFound/NotFound'; import Footer from '../../components/Footer/Footer'; +import Terms from '../../pages/Terms/Terms'; +import Privacy from '../../pages/Privacy/Privacy'; import './App.scss'; @@ -41,6 +43,8 @@ const App = props => ( + + diff --git a/imports/ui/pages/Index/Index.js b/imports/ui/pages/Index/Index.js index 7db421b..e6baf4a 100644 --- a/imports/ui/pages/Index/Index.js +++ b/imports/ui/pages/Index/Index.js @@ -15,6 +15,9 @@ const Index = () => (
+ ); diff --git a/imports/ui/pages/Index/Index.scss b/imports/ui/pages/Index/Index.scss index 218d2ab..0e397a2 100644 --- a/imports/ui/pages/Index/Index.scss +++ b/imports/ui/pages/Index/Index.scss @@ -33,14 +33,23 @@ .btn { border: none; } + } - .btn:last-child { - background: darken($cb-blue, 20%); - color: lighten($cb-blue, 30%); + footer { + margin: 20px -20px -20px; + border-top: 1px solid darken($cb-blue, 10%); + padding: 20px; - &:hover { - color: #fff; - } + p { + font-size: 14px; + line-height: 22px; + color: lighten($cb-blue, 35%); + margin: 0; + } + + p a { + color: lighten($cb-blue, 35%); + text-decoration: underline; } } } @@ -48,11 +57,19 @@ @include breakpoint(tablet) { .Index { padding: 30px; + + footer { + margin: 30px -30px -30px; + } } } @include breakpoint(desktop) { .Index { padding: 40px; + + footer { + margin: 40px -40px -40px; + } } } diff --git a/imports/ui/pages/Page/Page.js b/imports/ui/pages/Page/Page.js new file mode 100644 index 0000000..41d6b4b --- /dev/null +++ b/imports/ui/pages/Page/Page.js @@ -0,0 +1,44 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { Meteor } from 'meteor/meteor'; +import { createContainer } from 'meteor/react-meteor-data'; +import { ReactiveVar } from 'meteor/reactive-var'; +import PageHeader from '../../components/PageHeader/PageHeader'; +import Content from '../../components/Content/Content'; + +import './Page.scss'; + +const Page = ({ title, subtitle, content }) => ( +
+ + +
+); + +Page.defaultProps = { + subtitle: '', +}; + +Page.propTypes = { + title: PropTypes.string.isRequired, + subtitle: PropTypes.string, + content: PropTypes.string.isRequired, +}; + +const pageContent = new ReactiveVar(''); + +export default createContainer(({ content, page }) => { + window.scrollTo(0, 0); // Force window to top of page. + + Meteor.call('utility.getPage', page, (error, response) => { + if (error) { + console.warn(error); + } else { + pageContent.set(response); + } + }); + + return { + content: content || pageContent.get(), + }; +}, Page); diff --git a/imports/ui/pages/Page/Page.scss b/imports/ui/pages/Page/Page.scss new file mode 100644 index 0000000..183bf23 --- /dev/null +++ b/imports/ui/pages/Page/Page.scss @@ -0,0 +1,11 @@ +@import '../../stylesheets/mixins'; + +.Page { + margin-bottom: 0px; +} + +@include breakpoint(tablet) { + .Page { + margin-bottom: 30px; + } +} diff --git a/imports/ui/pages/Privacy/Privacy.js b/imports/ui/pages/Privacy/Privacy.js new file mode 100644 index 0000000..4cb33d4 --- /dev/null +++ b/imports/ui/pages/Privacy/Privacy.js @@ -0,0 +1,14 @@ +import React from 'react'; +import Page from '../Page/Page'; + +const Privacy = () => ( +
+ +
+); + +export default Privacy; diff --git a/imports/ui/pages/Terms/Terms.js b/imports/ui/pages/Terms/Terms.js new file mode 100644 index 0000000..2aebfdf --- /dev/null +++ b/imports/ui/pages/Terms/Terms.js @@ -0,0 +1,14 @@ +import React from 'react'; +import Page from '../Page/Page'; + +const Terms = () => ( +
+ +
+); + +export default Terms; diff --git a/package.json b/package.json index ca9fff6..71c4670 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "@cleverbeagle/strings": "^0.1.0", "babel-runtime": "^6.20.0", "bcrypt": "^1.0.2", + "commonmark": "^0.27.0", "jquery": "^2.2.4", "jquery-validation": "^1.16.0", "meteor-node-stubs": "~0.2.4", diff --git a/private/pages/privacy.md b/private/pages/privacy.md new file mode 100644 index 0000000..7f31547 --- /dev/null +++ b/private/pages/privacy.md @@ -0,0 +1,18 @@ +Your privacy is important to us. + +It is Clever Beagle's policy to respect your privacy regarding any information we may collect while operating our website. Accordingly, we have developed this privacy policy in order for you to understand how we collect, use, communicate, disclose and otherwise make use of personal information. We have outlined our privacy policy below. + +We will collect personal information by lawful and fair means and, where appropriate, with the knowledge or consent of the individual concerned. +Before or at the time of collecting personal information, we will identify the purposes for which information is being collected. + +We will collect and use personal information solely for fulfilling those purposes specified by us and for other ancillary purposes, unless we obtain the consent of the individual concerned or as required by law. + +Personal data should be relevant to the purposes for which it is to be used, and, to the extent necessary for those purposes, should be accurate, complete, and up-to-date. + +We will protect personal information by using reasonable security safeguards against loss or theft, as well as unauthorized access, disclosure, copying, use or modification. + +We will make readily available to customers information about our policies and practices relating to the management of personal information. + +We will only retain personal information for as long as necessary for the fulfillment of those purposes. + +We are committed to conducting our business in accordance with these principles in order to ensure that the confidentiality of personal information is protected and maintained. Clever Beagle may change this privacy policy from time to time at Clever Beagle's sole discretion. diff --git a/private/pages/terms.md b/private/pages/terms.md new file mode 100644 index 0000000..79757b6 --- /dev/null +++ b/private/pages/terms.md @@ -0,0 +1,38 @@ +#### Terms + +By accessing the website at https://pup.cleverbeagle.com, you are agreeing to be bound by these terms of service, all applicable laws and regulations, and agree that you are responsible for compliance with any applicable local laws. If you do not agree with any of these terms, you are prohibited from using or accessing this site. The materials contained in this website are protected by applicable copyright and trademark law. + +#### Use License + +Permission is granted to temporarily download one copy of the materials (information or software) on Clever Beagle's website for personal, non-commercial transitory viewing only. This is the grant of a license, not a transfer of title, and under this license you may not: +modify or copy the materials; +use the materials for any commercial purpose, or for any public display (commercial or non-commercial); +attempt to decompile or reverse engineer any software contained on Clever Beagle's website; +remove any copyright or other proprietary notations from the materials; or +transfer the materials to another person or "mirror" the materials on any other server. +This license shall automatically terminate if you violate any of these restrictions and may be terminated by Clever Beagle at any time. Upon terminating your viewing of these materials or upon the termination of this license, you must destroy any downloaded materials in your possession whether in electronic or printed format. + +#### Disclaimer + +The materials on Clever Beagle's website are provided on an 'as is' basis. Clever Beagle makes no warranties, expressed or implied, and hereby disclaims and negates all other warranties including, without limitation, implied warranties or conditions of merchantability, fitness for a particular purpose, or non-infringement of intellectual property or other violation of rights. +Further, Clever Beagle does not warrant or make any representations concerning the accuracy, likely results, or reliability of the use of the materials on its website or otherwise relating to such materials or on any sites linked to this site. + +#### Limitations + +In no event shall Clever Beagle or its suppliers be liable for any damages (including, without limitation, damages for loss of data or profit, or due to business interruption) arising out of the use or inability to use the materials on Clever Beagle's website, even if Clever Beagle or a Clever Beagle authorized representative has been notified orally or in writing of the possibility of such damage. Because some jurisdictions do not allow limitations on implied warranties, or limitations of liability for consequential or incidental damages, these limitations may not apply to you. + +#### Accuracy of materials + +The materials appearing on Clever Beagle's website could include technical, typographical, or photographic errors. Clever Beagle does not warrant that any of the materials on its website are accurate, complete or current. Clever Beagle may make changes to the materials contained on its website at any time without notice. However Clever Beagle does not make any commitment to update the materials. + +#### Links + +Clever Beagle has not reviewed all of the sites linked to its website and is not responsible for the contents of any such linked site. The inclusion of any link does not imply endorsement by Clever Beagle of the site. Use of any such linked website is at the user's own risk. + +#### Modifications + +Clever Beagle may revise these terms of service for its website at any time without notice. By using this website you are agreeing to be bound by the then current version of these terms of service. + +#### Governing Law + +These terms and conditions are governed by and construed in accordance with the laws of Illinois and you irrevocably submit to the exclusive jurisdiction of the courts in that State or location.