├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── author.md ├── favicon.ico ├── index.md ├── package-lock.json ├── package.json ├── projects ├── api │ ├── commands.md │ ├── config.md │ ├── data-design.md │ ├── faq.md │ ├── folder-structure.md │ ├── index.md │ ├── index.yml │ └── technologies.md ├── apps │ ├── TWA │ │ ├── index.md │ │ └── index.yml │ ├── index.md │ └── index.yml ├── cms │ ├── commands.md │ ├── config.md │ ├── folder-structure.md │ ├── index.md │ ├── index.yml │ └── technologies.md ├── doc │ └── index.md ├── index.md ├── index.yml └── website │ ├── analytics.md │ ├── build-strategy.md │ ├── commands.md │ ├── config.md │ ├── faq.md │ ├── folder-structure.md │ ├── index.md │ ├── index.yml │ ├── payment.md │ ├── pwa.md │ ├── seo.md │ ├── technologies.md │ └── theme.md ├── retype.yml └── static ├── appsco-splashscreens.png ├── favicon-generator.png ├── logo.png ├── product-properties-combo-product.png ├── product-properties-individual-product.png └── pwa-builder.png /.gitattributes: -------------------------------------------------------------------------------- 1 | * text eol=lf 2 | *.png binary 3 | *.jpg binary 4 | *.jpeg binary 5 | *.ico binary 6 | *.icns binary 7 | 8 | # Web fonts are binary 9 | *.otf binary 10 | *.eot binary 11 | *.svg binary 12 | *.ttf binary 13 | *.woff binary 14 | *.woff2 binary 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | dist/ 3 | 4 | # misc 5 | .DS_Store 6 | *.pem 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 OwnStore 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This project is part of OwnStore suite. Learn more here: https://ownstore.dev 2 | 3 | The suite contains the following projects: 4 | - [Website](https://github.com/OwnStoreOrg/ownstore-website) 5 | - [API](https://github.com/OwnStoreOrg/ownstore-api) 6 | - [CMS](https://github.com/OwnStoreOrg/ownstore-cms) 7 | - [Doc](https://github.com/OwnStoreOrg/ownstore-doc) 8 | - Apps 9 | - [TWA](https://github.com/OwnStoreOrg/ownstore-app-twa) 10 | 11 | **Note:** This project is a clone of a privately-managed repo by the author. The original repo has the complete Git tree, few additional technologies and a CI/CD setup. Changes are initially made in the private repo and then synced here through CLI commands. This is done for security reasons. 12 | 13 | Additional updates present in the original repos 14 | - Cypress (for end-to-end testing) 15 | - Storybook (for UI components explorer) 16 | - Docker files (for containerization) 17 | - Jest (for functional testing of business logic) 18 | - Local https setup for development (Eg. https://local.ownstore-demo.com) 19 | - AWS cloud migration (not pushed to prod yet) 20 | - Cloudflare as a CDN and custom name server (not pushed to prod yet) 21 | - Sub-domain for serving static files (not pushed to prod yet) 22 | - Algolia and ElasticSearch experiments for searching (not pushed to prod yet) 23 | - Sentry end-to-end tracking experiment (not pushed to prod yet) 24 | - Token-based color theming for dark mode and better DX support (not pushed to prod yet) 25 | - Modular folder structure 26 | - Native app with Flutter (Work in progress) 27 | - Kubernetes config to manage multiple app instances with ease (not pushed to prod yet) 28 | - Monitoring and alerting with Prometheus and Grafana (not pushed to prod yet) 29 | - Continuous deployment with GitHub actions, Docker, AWS and Kubernetes (not pushed to prod yet) 30 | - Lighhouse score checker for PRs through GitHub actions 31 | 32 | -------------------------------------------------------------------------------- /author.md: -------------------------------------------------------------------------------- 1 | --- 2 | label: Author 3 | icon: person 4 | --- 5 | 6 | Faiyaz (-looking for a change -) 7 | 8 | Few links to follow the author: 9 | - [LinkedIn](https://www.linkedin.com/in/faiyaz-s-413450118/) 10 | - [GitHub](https://github.com/yTakkar) 11 | - [Twitter](https://twitter.com/shtakkar) 12 | - [Email](mailto:www.shtakkar@gmail.com) 13 | -------------------------------------------------------------------------------- /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OwnStoreOrg/ownstore-doc/d4df9c89aef1c4199e5c9739e76a81fb57591bbe/favicon.ico -------------------------------------------------------------------------------- /index.md: -------------------------------------------------------------------------------- 1 | --- 2 | label: Welcome 3 | icon: home 4 | order: 4 5 | --- 6 | 7 | OwnStore is a configurable online store for your bussiness. Can be customized according to your needs. Packed with bundle of modern web features and a easy-to-use UI for users. 8 | 9 | Few features: 10 | - Mobile-first rendering 11 | - Minimalistic UI 12 | - Advanced SEO practices 13 | - PWA enabled 14 | - Ready to incorporate in a webview for native apps 15 | - Contract based communication between layers 16 | - Advanced analytics for product and engineering folks :) 17 | - Easy-to-use CMS for admins 18 | - Secure user login and data management 19 | - Ultra-fast pages rendering (even for dynamic pages) 20 | - Highly configurable 21 | - 90+ average lighthouse score across all segments even for a full-fledged dynamic webapp like this 22 | - Advanced caching strategies at multiple layers from API to web 23 | - Smooth ordering and payment experience for users 24 | - Pattern-based engineering practices 25 | - Engineering practices from 26 | - [ESPNcricinfo](https://www.espncricinfo.com/) 27 | - [Disney+Hotstar](https://www.hotstar.com/) 28 | - [Proximity](https://www.proximity.tech/) 29 | - [Shaadi.com](https://www.shaadi.com/) 30 | - [Sangam.com](https://www.sangam.com) 31 | - [Delta Exchange](https://www.delta.exchange/) 32 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "own-store-doc", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "retypeapp": { 8 | "version": "1.10.0", 9 | "resolved": "https://registry.npmjs.org/retypeapp/-/retypeapp-1.10.0.tgz", 10 | "integrity": "sha512-TyGnSl1e29yXW42c5VXrkeXKHavkmaRjwQydlkZTdA2p8fHSKEQS7FGYW4OOSjK/tPQ1FTfX7lmogF+4KEZZ4g==" 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "own-store-doc", 3 | "version": "1.0.0", 4 | "description": "Summary", 5 | "private": true, 6 | "scripts": { 7 | "dev": "retype watch", 8 | "build": "retype build" 9 | }, 10 | "keywords": [], 11 | "dependencies": { 12 | "retypeapp": "^1.10.0" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /projects/api/commands.md: -------------------------------------------------------------------------------- 1 | --- 2 | label: Commands 3 | title: API Commands 4 | icon: gear 5 | order: 1 6 | --- 7 | 8 | Let's discuss the possible commands this project supports 9 | 10 | ## lint:fix 11 | This command will fix the linting issues. And report if not fixed. 12 | 13 | ```bash 14 | npm run lint:fix 15 | ``` 16 | 17 | ## start:local 18 | Starts the server on `3001` port using `local` env. 19 | 20 | If both MySQL and Redis connections are successful, then you will see a `running...` message. 21 | 22 | ```bash 23 | npm run start:local 24 | ``` 25 | 26 | ## start:prod 27 | Starts the server using `production` env. 28 | 29 | ```bash 30 | npm run start:prod 31 | ``` 32 | 33 | ## build 34 | To build the project 35 | 36 | ```bash 37 | npm run build 38 | ``` 39 | 40 | ## start 41 | This command can be used to start with build files. Note: You will have to pass the ENV to start. 42 | ```bash 43 | STORE_API_ENV=local npm run start 44 | STORE_API_ENV=production npm run start 45 | ``` 46 | -------------------------------------------------------------------------------- /projects/api/config.md: -------------------------------------------------------------------------------- 1 | --- 2 | label: Config 3 | title: API Config and ENVs 4 | icon: note 5 | order: 2 6 | --- 7 | 8 | Before deep-diving into the app config, let's first through the ENVs. 9 | 10 | ## ENV 11 | This project has multiple environments and can be found under `/env` directory. These envs help us to develop, test and deploy. Currently, we have only 2: 12 | - `local` - For development 13 | - `production` - For production 14 | 15 | An ENV file contains values which you need to be different on different environments. For eg. You need a local DB connection while developing, but a prod connection for online users. 16 | 17 | ## Config 18 | App config can be found in `src/appConfig.ts` file. 19 | 20 | ### config.global 21 | Key | Description 22 | --- | --- 23 | `global.app` | Few app related information. 24 | `global.domain` | Domain. For eg. `your-store-api.herokuapp.com` OR `localhost:3000` 25 | `global.baseUrl` | Base URL. For eg. `https://your-store-api.herokuapp.com` or `http://localhost:3000` 26 | `global.clientOrigins` | A list of origins where API will be used. This is for security. Check the supported origins [here](http://localhost:3003/projects/api/faq/#which-origins-are-supported) 27 | `global.pageSectionItemsLimit` | A number to limit the count of items fetched for a section. For eg. If 15, then only 15 products will be returned for the `Products` section. 28 | `global.showDoc` | Control if you want to show swagger API doc. 29 | 30 | ### config.admin 31 | Key | Description 32 | --- | --- 33 | `admin.email` | Email of the admin. Used as a support email for swagger doc. 34 | `admin.cacheClearKey` | This key will be used when an admin tries to clear the redis cache. The operation will be validated against this key. **Very private** 35 | `admin.auth.key` | This key will be used to validate an admin from CMS. **Very private** 36 | `admin.auth.tokenSecret` | To prepare a unique and safe admin token with `admin.auth.key` for the CMS, a secret key is needed. **Very private** 37 | `admin.auth.tokenExpiry` | This controls how long the admin should stay logged in. 38 | 39 | ### config.errors 40 | Key | Description 41 | --- | --- 42 | `errors.report4xxErrors` | Set this to true if you want to report 4xx errors to Sentry. 43 | 44 | ### config.userAuth 45 | Key | Description 46 | --- | --- 47 | `userAuth.tokenSecret` | To prepare a unique and safe user token for users, a secret key is needed. **Very private** 48 | `userAuth.tokenExpiry.default` | This controls how long the user should stay logged in. 49 | `userAuth.tokenExpiry.extended` | This controls how long the user should stay logged in if they opt-in to `Remember me`. 50 | 51 | ### config.order 52 | Key | Description 53 | --- | --- 54 | `order.status` | There's a dependency in code for 2 order statuses: `RECEIVED` & `CANCELLED`. Map these statuses with correct DB ID here. 55 | `order.cancellationReasons` | This is a list of pre-defined reasons shown to users when they cancel the order. 56 | 57 | ### config.payment 58 | Key | Description 59 | --- | --- 60 | `payment.refundAmountPercent` | The amount in percentage of order total to refund. 61 | `payment.tax` | Total tax on cart total. First priority is given to `percent`. If null, considers 'flat' key. 62 | `payment.tax.percent` | Tax amount in percentage 63 | `payment.tax.flat` | Tax amount in flat number 64 | `payment.tax.decimalPrecision` | This controls the number of decimals after calculation of tax. For eg. 17.23491 => 17.23 65 | `payment.extraCharges` | Total extra charges on cart total. First priority is given to `percent`. If null, considers 'flat' key. 66 | `payment.extraCharges.percent` | Extra charge amount in percentage 67 | `payment.extraCharges.flat` | Extra charge amount in flat number 68 | `payment.extraCharges.decimalPrecision` | This controls the number of decimals after calculation of tax. For eg. 17.23491 => 17.23 69 | `payment.smallestCurrencyUnit` | Smallest currency unit to form 1 amount. For eg. 1 dollar = 100 cents, 1 rupee = 100 paisa. Used by Stripe to process payments. 70 | `payment.deliveryPriceMapping` | A mapping of [whenAmountAbove]: [deliveryPrice]. Should be in ascending order of whenAmountAbove. 71 | 72 | ### config.database 73 | Key | Description 74 | --- | --- 75 | `database.url` | DB connection URL. If provided, other credentials such host, password, etc.. will be ignored. 76 | `database.host` | DB host for connection 77 | `database.post` | DB post for connection 78 | `database.name` | DB name for connection 79 | `database.user` | DB user for connection 80 | `database.password` | DB password for connection 81 | `database.enableLogging` | If set to true, `TypeORM` logging will be enabled. 82 | `database.enableSync` | If set to true, any changes in models will be reflected in DB on save. Also if a table is not present in DB but present as a model, TypeORM will create one table. So DB and its tables are eligible for auto sync. 83 | 84 | ### config.cache 85 | Key | Description 86 | --- | --- 87 | `cache.redis` | Redis is used to cache data at service method level. This is done offload DB operations. 88 | `cache.redis.enabled` | To disable redis cache, set it to `false` 89 | `cache.redis.mode` | Redis mode (`standalone` / `cluster`). [Redis connection reference](https://github.com/luin/ioredis/blob/HEAD/API.md#Redis) 90 | `cache.redis.url` | Redis connection URL. IF provided, other credentials will be ignored. [Redis connection reference](https://github.com/luin/ioredis/blob/HEAD/API.md#Redis) 91 | `cache.redis.host` | [Redis connection reference](https://github.com/luin/ioredis/blob/HEAD/API.md#Redis) 92 | `cache.redis.port` | [Redis connection reference](https://github.com/luin/ioredis/blob/HEAD/API.md#Redis) 93 | `cache.redis.dbIndex` | [Redis connection reference](https://github.com/luin/ioredis/blob/HEAD/API.md#Redis) 94 | `cache.redis.password` | [Redis connection reference](https://github.com/luin/ioredis/blob/HEAD/API.md#Redis) 95 | `cache.redis.keyPrefix` | Used as a prefix for all Redis cache keys. 96 | `cache.redis.maxTtl` | How long the cache should be kept at max. 97 | `cache.httpResponse` | We have caching at HTTP response level too. 98 | `cache.httpResponse.enabled` | To disable HTTP response cache, set it to `false` 99 | 100 | ### config.allow 101 | 102 | There are 2 ways you can disable: 103 | - directly from project ENV files by setting the value as `false` 104 | - or expose these ENV variables separately and keep values here as blank. You can avoid a build with this approach and change will be applied immediately. 105 | 106 | Key | Description 107 | --- | --- 108 | `allow.newRegisterations` | Allow new registerations. 109 | `allow.newOrders` | Allow new orders. 110 | 111 | ### config.integrations 112 | Key | Description 113 | --- | --- 114 | `integrations.stripePayment` | Stripe is used to process payments 115 | `integrations.stripePayment.secretKey` | Get secret key from Stripe dashboard 116 | `integrations.sentryErrorReporting` | Sentry is used to monitor app health 117 | `integrations.sentryErrorReporting.enabled` | To disable this integration 118 | `integrations.sentryErrorReporting.dsn` | Get DSN from Sentry's dashboard 119 | -------------------------------------------------------------------------------- /projects/api/data-design.md: -------------------------------------------------------------------------------- 1 | --- 2 | label: Data Design 3 | title: API Data Design 4 | icon: cpu 5 | order: 4 6 | --- 7 | 8 | The database used here is MySQL. So let's go through each table and understand its responsibility. 9 | 10 | ## User 11 | 12 | User-related tables. 13 | 14 | ```mermaid 15 | graph LR; 16 | A[user] --> B(user_address) 17 | A --> C(user_login_history) 18 | A --> D(user_security_question_answer) 19 | A --> F(user_wish) 20 | A --> G(user_cart_item) 21 | ``` 22 | 23 | Table | Responsibility 24 | --- | --- 25 | user | Holds user information such as `id`, `name`, etc... 26 | user_address | Holds user addresses. There can be multiple addresses associated with a user. 27 | user_login_history | Maintains a record of user logins. 28 | user_security_question_answer | Holds answers of user's selected security questions. Looks up to `security_question` for question reference. 29 | user_password_hint | Holds user's password hints. *This feature was deactivated due to security concerns.* 30 | user_wish | Holds user's product wishlist. 31 | user_cart_item | Holds user's cart products. 32 | 33 | ## Order 34 | 35 | Order-related tables. 36 | 37 | ```mermaid 38 | graph LR; 39 | A[order] --> B(order_status_history) --> C(order_status_type) 40 | A --> D(order_cancellation) 41 | ``` 42 | 43 | Table | Responsibility 44 | --- | --- 45 | order_status_type | Holdes status list supported by an order. This is a lookup table. For eg. You can add you own mapping here. 46 | order_status_history | Each order has a lifecycle from receiving an order to getting cancelled. This table maintains the very same lifecycle and uses status for it. Looks up to `order_status_type` for status reference. 47 | order | Holds all the information of an order. Few top-level data points: 48 | order_cancellation | Holds order's cancellation reason. 49 | 50 | ## Catalogue 51 | Table | Responsibility 52 | --- | --- 53 | catalogue | All individual products are associated with a catalogue. This table holds catalogue information such `name`, `images`, etc... 54 | 55 | ## Product 56 | A product can be of 2 types: 57 | - Individual 58 | - Combo 59 | 60 | ### Individual Product 61 | 62 | ![A screenshot of product properties on an individual product page](/static/product-properties-individual-product.png) 63 | 64 | ```mermaid 65 | graph LR; 66 | A[individual_product] --> B(catalogue) 67 | A --> C(product_attribute) --> D(product_attribute_key) 68 | A --> E(product_brand) 69 | A --> F(product_tag) 70 | A --> G(product_feature_section) 71 | A --> H(products_relation) 72 | ``` 73 | 74 | Table | Responsibility 75 | --- | --- 76 | individual_product | Holds individual product information. Few data points: 77 | 78 | ### Combo Product 79 | Combo products are made up of multiple products. For eg. A `Men winter suit` combo product can have the following product items 80 | - `Sweatshirt` 81 | - `Jacket` 82 | - `Beanie` 83 | - `Gloves ` 84 | 85 | Combo products dont' have 86 | - catalogue 87 | - brand 88 | 89 | ![A screenshot of product properties on a combo product page](/static/product-properties-combo-product.png) 90 | 91 | ```mermaid 92 | graph LR; 93 | A[combo_product] --> B(combo_product_item) 94 | A --> C(product_attribute) --> D(product_attribute_key) 95 | A --> E(product_tag) 96 | A --> F(product_feature_section) 97 | A --> G(products_relation) 98 | ``` 99 | 100 | Table | Responsibility 101 | --- | --- 102 | combo_product | Holds combo product information. Few data points: 103 | combo_product_item | Holds product items for a combo product. 104 | 105 | ### Product properties 106 | 107 | Table | Responsibility 108 | --- | --- 109 | product_attribute_key | A list of all attribute keys supported by the app. Few attribute key examples for a car: This is a lookup table. 110 | product_attribute | Maintains a list of all attributes for a **product**. Looks up to `product_attribute_key` for reference. 111 | product_brand | Holds the list of all the supported brands. This is a lookup table. 112 | product_tag | Holds tags for a product. Few tag examples: 113 | product_feature_section | A feature section is an expandable section with some detailed information about a product. This table holds feature sections for a product. 114 | products_relation | This table holds product relations. A relation can have both individual and combo products. 115 | 116 | ## Section 117 | A section is a component which can have multiple items. There can be different types of sections. 118 | - Products section (For products) 119 | - Catalogues section (For catalogues) 120 | - Blogs section (For blogs) 121 | - Full-width slides section (For slides which will cover the full screen. Will be used as slides) 122 | - Strict-width slides section (For slides which will have 640x300 size. Will be used as slides) 123 | - USPs section (To showcase app USPs. **Note:** Max 4) 124 | - Procedures section (To showcase the process) 125 | - Customer feedbacks section (For displaying customer feedbacks) 126 | - Share section (For asking users to share the app) 127 | - Custom section (For rendering any custom section. Accepts raw HTML) 128 | 129 | For eg. If `type` of a section is `PRODUCTS`, it will lookup products from `section_product` table. 130 | 131 | ```mermaid 132 | graph LR; 133 | A[section] --> B(section_product) --> J(individual_product OR combo_product) 134 | A[section] --> C(section_catalogue) --> K(catalogue) 135 | A[section] --> D(section_blog) --> L(blog) 136 | A[section] --> E(section_slide) 137 | A[section] --> F(section_usp) 138 | A[section] --> G(section_procedure) 139 | A[section] --> H(section_customer_feedback) 140 | A[section] --> I(section_custom) 141 | ``` 142 | 143 | Table | Responsibility 144 | --- | --- 145 | section | This is where all the sections are stored. Few data points: 146 | section_product | Holds products for `Products` section 147 | section_catalogue | Holds catalogues for `Catalogues` section 148 | section_blog | Holds blogs for `Blogs` section 149 | section_slide | Holds slides for both `Full-width slides` and `Strict-width slides` sections. 150 | section_usp | Holds data for `USPs` section 151 | section_procedure | Holds data for `Procedures` section 152 | section_customer_feedback | Holds data for `Customer Feedbacks` section 153 | section_custom | Holds data for `Custom` section 154 | 155 | ## Page Sections 156 | You can use multiple sections on a page. Also you can override the default properties of a section such as `title`, `subTitle`, etc... only for a page. 157 | 158 | Table | Responsibility 159 | --- | --- 160 | section_page_home | Sections to show on home page 161 | section_page_explore | Sections to show on explore page 162 | section_page_error | Sections to show on error page 163 | section_page_individual_product | Sections to show on individual product page 164 | section_page_combo_product | Sections to show on combo product page 165 | section_page_search | Sections to show on search page 166 | 167 | ## Supported Regions 168 | **Note:** There's no validation of city-to-country mapping at system-level, so pls add cities only of the supported countries. For eg. don't add `Mumbai` if `India` country is not supported. 169 | 170 | Table | Responsibility 171 | --- | --- 172 | supported_country | Holds the list of countries the app supports. 173 | supported_city | Holds the list of cities the app supports. If the app is servicable only in `Mumbai`, then kindly add `India` as country and `Mumbai` as city. 174 | 175 | ## FAQ 176 | ```mermaid 177 | graph LR; 178 | A[faq_topic] --> B(faq) 179 | ``` 180 | 181 | Table | Responsibility 182 | --- | --- 183 | faq_topic | Each FAQ is grouped with a topic such as `general`, `payment`, etc.. This table holds the very same topics. 184 | faq | All the FAQs are stored here with corresponding topic ID. 185 | 186 | ## HTML Pages 187 | Table | Responsibility 188 | --- | --- 189 | page_tn_c | Content for the `Terms and Conditions page`. Purely raw HTML. 190 | page_privacy_policy | Content for the `Privacy Policy page`. Purely raw HTML. 191 | page_refund_policy | Content for the `Refund Policy page`. Purely raw HTML. 192 | 193 | ## Others 194 | Table | Responsibility 195 | --- | --- 196 | currency | Holds currency supported by the product. There can be only 1 row. 197 | security_question | Holds all the security questions. This is a lookup table. 198 | blog | Holds all the information for a blog. 199 | image | This is a global table which holds all the dynamic images from product to slides. Few top-level data: 200 | 201 | 202 | -------------------------------------------------------------------------------- /projects/api/faq.md: -------------------------------------------------------------------------------- 1 | --- 2 | label: FAQs 3 | title: API FAQs 4 | icon: question 5 | order: -1 6 | --- 7 | 8 | ## Which additional headers are supported? 9 | Apart from the standard HTTP headers, we accept a few more 10 | 11 | This mapping can be found in `src/app/constants/index.ts` 12 | 13 | ```ts 14 | export const VALID_EXTERNAL_HEADERS = { 15 | ACCESS_TOKEN: 'x-access-token', 16 | ADMIN_ACCESS_TOKEN: 'x-admin-access-token', 17 | SENTRY_TRACE: 'sentry-trace', 18 | RESPONSE_CACHE_TIME: `${appConfig.global.app.key.toLowerCase()}-cache-time`, 19 | } 20 | ``` 21 | 22 | ## Which origins are supported? 23 | 24 | This list can be found in `src/appConfig.ts`. 25 | 26 | ```ts 27 | [ 28 | 'http://localhost:3000', // website local 29 | 'http://localhost:3002', // cms local 30 | 'https://own-store-demo.vercel.app', // website prod 31 | 'https://own-store-demo-cms.vercel.app', // cms prod 32 | ] 33 | ``` 34 | 35 | ## What are the supported cache TTLs? 36 | 37 | We have caching at 2 layers: 38 | - service 39 | - controller 40 | 41 | Service-level cache time should be 80% of controller's 42 | 43 | ```ts 44 | export const CACHE_MULTIPLIER = 0.8 45 | 46 | // Ideally service-level cache time should be 80% of controller's 47 | // in seconds 48 | export const SERVICE_CACHE_TTL = { 49 | LIVE: 30 * CACHE_MULTIPLIER, 50 | SHORT: 2 * 60 * CACHE_MULTIPLIER, 51 | DEFAULT: 5 * 60 * CACHE_MULTIPLIER, 52 | LONG: 10 * 60 * CACHE_MULTIPLIER, 53 | } 54 | 55 | // in seconds 56 | export const CONTROLLER_CACHE_TTL = { 57 | LIVE: 30, 58 | SHORT: 2 * 60, 59 | DEFAULT: 5 * 60, 60 | LONG: 10 * 60, 61 | } 62 | ``` 63 | -------------------------------------------------------------------------------- /projects/api/folder-structure.md: -------------------------------------------------------------------------------- 1 | --- 2 | label: Folder Structure 3 | title: API Folder Structure 4 | icon: file-directory 5 | order: 3 6 | --- 7 | 8 | ## Directories 9 | A description of top-level directories. 10 | 11 | Directory | Description 12 | --- | --- 13 | `/env` | For ENV files. 14 | `/src/server` | Only for server related handling. 15 | `/src/server/controller` | Holds all the supported HTTP endpoints. 16 | `/src/app` | This directory has all app-related files. 17 | `/src/app/constants` | For app constants. 18 | `/src/app/contract` | A contract consumers will use for business logic. 19 | `/src/app/decorators` | We use few decorators to enhance a method without modifying its interface. 20 | `src/app/errors` | We have extended `Error` class for more custom handling. 21 | `src/app/models` | These are TypeORM models. Basically a representation of a MySQL table. 22 | `src/app/repositories` | Contains repositories which are used to perform DB operations through TypeORM. 23 | `src/app/services` | For all services. 24 | `src/app/transformers` | Holds methods which transform a data into a contract. 25 | `src/app/utils` | Contains both contextual as well as pure methods. 26 | `/dist` | For build files. 27 | 28 | ## Core Layers 29 | There are 5 core layers in this project and each layer has a responsibility. 30 | - Controller 31 | - Service 32 | - Repository 33 | - Model 34 | - Transformer 35 | 36 | ```mermaid 37 | %%{init: {} }%% 38 | graph LR 39 | A[Controller] --> B[Service] 40 | B[Service] --> C[Repository] 41 | C[Repository] --> D[Model] --> C --> B --> E[Transformer] --> B --> A 42 | ``` 43 | 44 | Kindly go through this [blog](https://softwareontheroad.com/ideal-nodejs-project-structure/). 45 | 46 | ==- Controller 47 | Handles incoming request and only interacts with service. 48 | Can be found in `src/server/controller` 49 | ==- 50 | 51 | ==- Service 52 | These are pure methods and don't accept any global or contextual values/params. Their responsibility is to return the required data to any consumer not specifically just the controller. Services interact with 53 | - repositories for fetching data 54 | - transformers for tranforming data into a contract 55 | 56 | Can be found in `src/app/services` 57 | ==- 58 | 59 | ==- Repository 60 | Their responsibility is to do DB-related operations. 61 | Can be found in `src/app/repositories` 62 | ==- 63 | 64 | ==- Model 65 | Specifies DB table contract. 66 | Can be found in `src/app/models` 67 | ==- 68 | 69 | ==- Transformers 70 | Since each endpoint returns data in form of a contract, it is transformer's responsibility is to prepare that contract. 71 | Can be found in `src/app/transformers` 72 | ==- 73 | -------------------------------------------------------------------------------- /projects/api/index.md: -------------------------------------------------------------------------------- 1 | This project is responsible for communicating with the database and exposes REST API endpoints. This is a dependancy of both user-facing website UI and CMS. 2 | 3 | - [Technologies](./technologies) 4 | - [Data Design](./data-design) 5 | - [Folder Structure](./folder-structure) 6 | - [Config](./config) 7 | - [Commands](./commands) 8 | - [FAQs](./faq) 9 | -------------------------------------------------------------------------------- /projects/api/index.yml: -------------------------------------------------------------------------------- 1 | label: API 2 | icon: plug 3 | order: 3 4 | -------------------------------------------------------------------------------- /projects/api/technologies.md: -------------------------------------------------------------------------------- 1 | --- 2 | label: Technologies 3 | title: API Tech stack 4 | icon: rocket 5 | order: 5 6 | --- 7 | 8 | Top level technologies used in this project. 9 | 10 | === NodeJs 11 | To keep language consistent throughout all 3 projects, we decided to use JS here as well. [Ref](https://nodejs.org/en) 12 | === 13 | 14 | === TypeScript 15 | Used for type safety, and creating a contract between consumers and the the service provider that is API. [Ref](https://www.typescriptlang.org) 16 | === 17 | 18 | === HapiJs 19 | For better dev experience while building an HTTP server. [Ref](https://hapi.dev/) 20 | === 21 | 22 | === Inversify 23 | For dependency injection. [Ref](https://www.npmjs.com/package/inversify) 24 | === 25 | 26 | === TypeORM 27 | For communicating with MySQL database. [Ref](https://typeorm.io/#/) 28 | === 29 | 30 | === Redis 31 | To cache response at service method level. [Ref](https://www.npmjs.com/package/ioredis) 32 | === 33 | -------------------------------------------------------------------------------- /projects/apps/TWA/index.md: -------------------------------------------------------------------------------- 1 | Using TWA, you can serve your PWA web application to users as an Android app. Learn more about about this [here](https://developer.chrome.com/docs/android/trusted-web-activity/). 2 | 3 | 4 | ## Setup 5 | 6 | ### Install 7 | We will be using [Bubblewrap](https://github.com/GoogleChromeLabs/bubblewrap) to generate our project. So first install it 8 | ```bash 9 | npm i -g @bubblewrap/cli 10 | ``` 11 | 12 | ### Directory 13 | Create a directory to init 14 | ```bash 15 | mkdir ownstore-app-twa 16 | ``` 17 | 18 | ### Initialize 19 | Now let's initialize our project with a manifest file. Using OwnStore's demo manifest as an example here. 20 | ```bash 21 | bubblewrap init --manifest=https://own-store-demo.vercel.app/json/manifest.json 22 | ``` 23 | 24 | This command will install 25 | - JDK 8 if not found. I had to say for this. 26 | - Android SDK if not found. I had previously installed the SDK here: `/Users/faiyazshaikh/Library/Android/sdk`. 27 | 28 | You will also be asked with a few questions. 29 | 30 | ### Build 31 | Once the project is initialized, we can build and generate our APK. 32 | ```bash 33 | bubblewrap build 34 | ``` 35 | 36 | You can also provide `----skipPwaValidation` to bypass PWA validation checks. 37 | 38 | ### Fingerprint 39 | Next step is to generate SHA256 fingerprints. Run this command 40 | ```bash 41 | keytool -printcert -jarfile app-release-signed.apk | grep SHA256 42 | ``` 43 | 44 | Copy the output. 45 | 46 | ### Web asset linking 47 | Create a file named `/public/.well-known/assetlinks.json` in website project. This will be served as `/.well-known/assetlinks.json`. Paste the following content in this JSON file. 48 | ```json 49 | [{ 50 | "relation": ["delegate_permission/common.handle_all_urls"], 51 | "target": { 52 | "namespace": "android_app", 53 | "package_name": "app.own_store_demo.twa", 54 | "sha256_cert_fingerprints": [ 55 | "" 56 | ] 57 | } 58 | }] 59 | 60 | ``` 61 | 62 | 63 | Go through this [doc](https://developer.chrome.com/docs/android/trusted-web-activity/quick-start/) for more detailed instructions. 64 | -------------------------------------------------------------------------------- /projects/apps/TWA/index.yml: -------------------------------------------------------------------------------- 1 | icon: diamond 2 | order: 3 3 | expanded: false 4 | -------------------------------------------------------------------------------- /projects/apps/index.md: -------------------------------------------------------------------------------- 1 | Mobile apps for OwnStore. 2 | - [TWA - Only Android](/projects/apps/twa/) 3 | - React Native (Coming soon) 4 | - Flutter (Coming soon) 5 | -------------------------------------------------------------------------------- /projects/apps/index.yml: -------------------------------------------------------------------------------- 1 | icon: device-mobile 2 | order: 1 3 | expanded: false 4 | -------------------------------------------------------------------------------- /projects/cms/commands.md: -------------------------------------------------------------------------------- 1 | --- 2 | label: Commands 3 | title: CMS Commands 4 | icon: gear 5 | order: 1 6 | --- 7 | 8 | Let's discuss the possible commands this project supports 9 | 10 | ## sync:contract 11 | This command assumes both CMS and API projects are siblings. Syncs API contracts. 12 | ```bash 13 | npm run sync:contract 14 | ``` 15 | 16 | ## lint 17 | This command analyze the codebase for any linting or formatting issues. 18 | ```bash 19 | npm run lint 20 | ``` 21 | 22 | ## lint:fix 23 | Same as `lint` but fixes the issues 24 | ```bash 25 | npm run lint:fix 26 | ``` 27 | 28 | ## start:local 29 | Starts the server on `3002` port using `local` env. Also syncs the contract. 30 | ```bash 31 | npm run start:local 32 | ``` 33 | 34 | ## start:localprod 35 | Starts the server on `3002` port using `localproduction` env. Also syncs the contract. 36 | ```bash 37 | npm run start:localprod 38 | ``` 39 | 40 | ## build 41 | Builds the entire project. 42 | ```bash 43 | npm run build 44 | ``` 45 | 46 | ## start 47 | This command can be used to start with build files. Note: You will have to pass the ENV to start. 48 | ```bash 49 | STORE_CMS_ENV=local npm run start 50 | STORE_CMS_ENV=production npm run start 51 | ``` 52 | -------------------------------------------------------------------------------- /projects/cms/config.md: -------------------------------------------------------------------------------- 1 | --- 2 | label: Config 3 | title: CMS Config and ENVs 4 | icon: note 5 | order: 2 6 | --- 7 | 8 | ## ENV 9 | CMS project currently supports 3 environments: 10 | - `local` - For development 11 | - `localproduction` - For development but with prod API 12 | - `production` - For production 13 | 14 | ## Config 15 | App config can be found in `config/appConfig.ts` file. 16 | 17 | ### config.global 18 | Key | Description 19 | --- | --- 20 | `global.app` | Few app related information. 21 | `global.domain` | Domain. For eg. `own-store-demo-cms.vercel.app` OR `localhost:3002` 22 | `global.baseUrl` | Base URL. For eg. `https://own-store-demo-cms.vercel.app` or `http://localhost:3002` 23 | `global.imageBaseUrl` | Cloudinary image base URL. For eg. `https://res.cloudinary.com/your-store/image/upload`. If you have mapped AWS S3 with Cloudinary, put that URL here. `imageBaseUrl + image path (from DB)` will render the image. **Note:** This is not for static images. 24 | `global.apiBaseUrl` | API base URL to connect to. For eg. `http://localhost:3001`. 25 | `global.webBaseUrl` | User-facing website URL. For eg. `http://localhost:3000` or `https://own-store-demo.vercel.app`. 26 | `global.redirectToIndexViewAfterUpdate` | Whenever an entity is successfuly updated, do you want to redirect to its index page? This flag controls exactly that. 27 | `global.redirectToIndexViewAfterDelete` | Whenever an entity is successfuly deleted, do you want to redirect to its index page? This flag controls exactly that. 28 | `global.paginationFetchLimit` | How many items should be shown be fetched and shown per page during pagination. 29 | 30 | ### config.order 31 | Key | Description 32 | --- | --- 33 | `order.recentOrders` | Config for recent orders page. 34 | `order.autoRefresh` | Enable auto refresh of new orders. 35 | `order.refreshIntervalInSeconds` | Control the refresh interval of recent orders. 36 | 37 | ### config.search 38 | Key | Description 39 | --- | --- 40 | `search.limit` | How many results should be fetched and shown on search pages? 41 | 42 | ### config.image 43 | Key | Description 44 | --- | --- 45 | `image.imageUploadDirectory` | While uploading images, a dropdown is shown to pick a directory. This mapping controls exactly the same. 46 | 47 | ### config.integrations 48 | 49 | #### config.integrations.cloudinary 50 | Config for Cloudinary integration. 51 | 52 | Key | Description 53 | --- | --- 54 | `integrations.cloudinary.cloudName` | Cloudinary clound name. Get this from dashboard. Eg. `your-store`. 55 | `integrations.cloudinary.uploadPresetName` | We are doing unsigned image upload, so we have to create a preset. Steps: 56 | 57 | #### config.integrations.googleAnalytics 58 | For Google analytics integration. 59 | 60 | Key | Description 61 | --- | --- 62 | `integrations.googleAnalytics.enabled` | Use this flag to disable. 63 | `integrations.googleAnalytics.code` | GA code. Get this from dashboard. 64 | -------------------------------------------------------------------------------- /projects/cms/folder-structure.md: -------------------------------------------------------------------------------- 1 | --- 2 | label: Folder Structure 3 | title: CMS Folder Structure 4 | icon: file-directory 5 | order: 3 6 | --- 7 | 8 | Directory | Description 9 | --- | --- 10 | `/components` | Contains page-level as well as pure components. 11 | `/config` | Contains app-level config. 12 | `/constants` | For global constants. 13 | `/contract` | API contracts are stored here. 14 | `/env` | For ENV files. 15 | `/hooks` | Contains global React hooks. 16 | `/http` | Responsible for connecting with API. 17 | `/pages` | All NextJs pages are kept in this folder. 18 | `/public` | Has all the static files from fonts to images. 19 | `/scriptTemplates` | Contains script templates for for all HTML pages. 20 | `/styles` | Holds all styles-related SCSS files. 21 | `/utils` | Contains page-level as well as pure methods. 22 | `/.next` | Contains build files. 23 | -------------------------------------------------------------------------------- /projects/cms/index.md: -------------------------------------------------------------------------------- 1 | This project is responsible for providing a UI to update database entries. 2 | 3 | - [Technologies](./technologies) 4 | - [Folder Structure](./folder-structure) 5 | - [Config](./config) 6 | - [Commands](./commands) 7 | -------------------------------------------------------------------------------- /projects/cms/index.yml: -------------------------------------------------------------------------------- 1 | icon: pencil 2 | label: CMS 3 | order: 2 -------------------------------------------------------------------------------- /projects/cms/technologies.md: -------------------------------------------------------------------------------- 1 | --- 2 | label: Technologies 3 | title: CMS Tech stack 4 | icon: rocket 5 | order: 5 6 | --- 7 | 8 | Top level technologies used in this project. 9 | 10 | === NextJs 11 | React framework for better dev experience, optimized build, etc... [Ref](https://nextjs.org/) 12 | === 13 | 14 | === TypeScript 15 | Used for type safety. [Ref](https://www.typescriptlang.org) 16 | === 17 | 18 | === TailwindCSS 19 | Utility-based CSS framework for faster prototyping. [Ref](https://tailwindcss.com/) 20 | === 21 | 22 | -------------------------------------------------------------------------------- /projects/doc/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | label: Documentation 3 | title: Documentation 4 | icon: paste 5 | --- 6 | 7 | This doc project is created with the help of Retype open-source project. 8 | 9 | Few links: 10 | - [Retype](https://retype.com/) 11 | - [Retype website code](https://github.com/retypeapp/retype) 12 | -------------------------------------------------------------------------------- /projects/index.md: -------------------------------------------------------------------------------- 1 | So we have the following projects that enable this product to function 2 | - [API](/projects/api) 3 | - [CMS](/projects/CMS) 4 | - [Website](/projects/website) 5 | - [Documentation](/projects/doc) 6 | -------------------------------------------------------------------------------- /projects/index.yml: -------------------------------------------------------------------------------- 1 | icon: package 2 | order: 3 3 | expanded: true 4 | -------------------------------------------------------------------------------- /projects/website/analytics.md: -------------------------------------------------------------------------------- 1 | --- 2 | label: Analytics 3 | title: Website Analytics 4 | icon: graph 5 | order: 0 6 | --- 7 | 8 | ## Google Analytics 9 | GA is added as part of an integration but can be disabled. E-Commerce events have also been added for advanced analytics. 10 | 11 | The following events have been added: 12 | ```ts 13 | LOGIN = 'login', 14 | LOGOUT = 'logout', 15 | SIGNUP = 'sign_up', 16 | SEARCH = 'search', 17 | VIEW_SEARCH_RESULTS = 'view_search_results', 18 | SHARE = 'share', 19 | VIEW_CART = 'view_cart', 20 | ADD_TO_CART = 'add_to_cart', 21 | REMOVE_FROM_CART = 'remove_from_cart', 22 | DELETE_CART_ITEM = 'delete_cart_item', 23 | ADD_TO_WISHLIST = 'add_to_wishlist', 24 | REMOVE_FROM_WISHLIST = 'remove_from_wishlist', 25 | MOVE_TO_CART = 'move_to_cart', 26 | BEGIN_CHECKOUT = 'begin_checkout', 27 | PURCHASE = 'purchase', 28 | FAILED_PAYMENT = 'failed_payment', 29 | ADD_ADDRESS = 'add_address', 30 | EDIT_ADDRESS = 'edit_address', 31 | DEACTIVATE_ADDRESS = 'deactivate_address', 32 | SELECT_ADDRESS_FOR_DELIVERY = 'select_address_for_delivery', 33 | SELECT_PRODUCT = 'select_content', 34 | VIEW_PRODUCT = 'view_item', 35 | PWA_INSTALL_SUCCESS = 'pwa_install_success', 36 | PWA_INSTALL_FAILED = 'pwa_install_failed', 37 | EXCEPTION = 'exception', 38 | CHANGE_PASSWORD = 'change_password', 39 | UPDATE_PASSWORD_HINT = 'update_password_hint', 40 | UPDATE_SECURITY_QUESTIONS = 'update_security_questions', 41 | ANSWER_SECURITY_QUESTIONS = 'answer_security_questions', 42 | RESET_PASSWORD = 'reset_password', 43 | EDIT_PROFILE = 'edit_profile', 44 | SCROLL_TO_TOP = 'scroll_to_top', 45 | ``` 46 | 47 | Can be found in `app/constants/analytics.ts`. 48 | 49 | -------------------------------------------------------------------------------- /projects/website/build-strategy.md: -------------------------------------------------------------------------------- 1 | --- 2 | label: Build Strategy 3 | title: Website Build Strategy 4 | icon: container 5 | order: -2 6 | --- 7 | 8 | Since we use NextJs as a framework, we have used 3 build strategies here depending upon the use-case. Used strategies: 9 | - SSG (Static Generation) 10 | - CSR (Client-side Rendering) 11 | - SSR (Server-side Rendering) 12 | 13 | Kindly go through this article to [learn more](https://vercel.com/blog/nextjs-server-side-rendering-vs-static-generation) in detail. 14 | 15 | ## SSG 16 | This strategy enables us to build a dynamic page initially and update its content after an interval. This is much faster, widely adopted and optimized than other strategies. For eg. for a product page... 17 | 18 | There can be 1000+ products, so we will build 50 product pages initially and if user visits a page that is not built, NextJs will build the page on the fly, serve that page and update the build mapping so the next time you visit, you'll see a static page with no server lookup. 19 | 20 | Also we can define when an SSG page should be re-validated. 21 | 22 | ## CSR 23 | Initially a skeleton is served and once the page has finished loading, you will fetch the data and show a corresponding UI. CSR pages: 24 | - Account pages 25 | 26 | ## SSR 27 | This will build the page everytime you visit. SSR pages 28 | - Search results page 29 | -------------------------------------------------------------------------------- /projects/website/commands.md: -------------------------------------------------------------------------------- 1 | --- 2 | label: Commands 3 | title: Website Commands 4 | icon: gear 5 | order: 4 6 | --- 7 | 8 | Let's discuss the possible commands this project supports 9 | 10 | ## sync:contract 11 | This command assumes both CMS and API projects are siblings. Syncs API contracts. 12 | ```bash 13 | npm run sync:contract 14 | ``` 15 | 16 | ## lint 17 | This command analyze the codebase for any linting or formatting issues. 18 | ```bash 19 | npm run lint 20 | ``` 21 | 22 | ## lint:fix 23 | Same as `lint` but fixes the issues 24 | ```bash 25 | npm run lint:fix 26 | ``` 27 | 28 | ## script:generate-manifest 29 | Generates a manifest file for the project. 30 | ```bash 31 | npm run script:generate-manifest 32 | ``` 33 | 34 | ## start:local 35 | Starts the server on `3002` port using `local` env. Also syncs the contract. 36 | ```bash 37 | npm run start:local 38 | ``` 39 | 40 | ## start:localprod 41 | Starts the server on `3002` port using `localproduction` env. Also syncs the contract. 42 | ```bash 43 | npm run start:localprod 44 | ``` 45 | 46 | ## build 47 | Builds the entire project. 48 | ```bash 49 | npm run build 50 | ``` 51 | 52 | ## build:analyze 53 | Builds the project and opens a UI to visualize the build size. Note: You will have to pass the ENV to start. 54 | ```bash 55 | STORE_WEB_ENV=production npm run build:analyze 56 | ``` 57 | 58 | ## start 59 | This command can be used to start with build files. Note: You will have to pass the ENV to start. 60 | ```bash 61 | STORE_WEB_ENV=local npm run start 62 | STORE_WEB_ENV=production npm run start 63 | ``` 64 | -------------------------------------------------------------------------------- /projects/website/config.md: -------------------------------------------------------------------------------- 1 | --- 2 | label: Config 3 | title: Website Config and ENVs 4 | icon: note 5 | order: 5 6 | --- 7 | 8 | ## ENV 9 | This project currently supports 3 environments: 10 | - `local` - For development 11 | - `localproduction` - For development but with prod API 12 | - `production` - For production 13 | 14 | ## Config 15 | App config can be found in `config/appConfig.ts` file. 16 | 17 | 18 | ### config.global 19 | Key | Description 20 | --- | --- 21 | `global.app` | Few app related information. 22 | `global.domain` | Domain. For eg. `own-store-demo.vercel.app` OR `localhost:3000` 23 | `global.baseUrl` | Base URL. For eg. `https://own-store-demo.vercel.app` or `http://localhost:3000` 24 | `global.assetBaseUrl` | Base URL of static assets. If you want to push your static assets (stored in `/public`) and build files generated by NextJs to a CDN, update this URL. If you have a CDN for production, this is how you will map: 25 | `global.imageBaseUrl` | Cloudinary image base URL. For eg. `https://res.cloudinary.com/your-store/image/upload`. If you have mapped AWS S3 with Cloudinary, put that URL here. `imageBaseUrl + image path (from DB)` will render the image. **Note:** This is not for static images. 26 | `global.apiBaseUrl` | API base URL to connect to. For eg. `http://localhost:3001`. 27 | `global.infiniteScrollFetchLimit` | How many items should be shown be fetched and shown per page during infinite scroll. 28 | `global.scrollToTopDisplayThreshold` | When to show scroll-to-top UI. 29 | `global.minSecurityQuestions` | How many security questions should be shown to user to set? 30 | `global.openBlogsInNewTab` | Should blogs open in a new tab? 31 | 32 | ### config.seo 33 | Learn more [here](./seo.md) 34 | 35 | Key | Description 36 | --- | --- 37 | `seo.facebook.pageId` | Facebook page ID. For eg. `1223xxxx`. Get this from page settings. 38 | `seo.twitter.username` | Twitter page username. 39 | 40 | ### config.pwa 41 | Learn more [here](./pwa.md) 42 | 43 | Key | Description 44 | --- | --- 45 | `pwa.shortcuts[]` | Users can directly open a page through shortcuts. Few properties are required: 46 | `pwa.startUrl` | PWA's start URL. Eg. `/?utm_source=pwa&utm_medium=homescreen` 47 | `pwa.icons.maskable` | Set this to true if PWA icons can be masked. **Note:** Make sure icons can be masked before setting to true. Otherwise, the installability criteria will not be met. [Masked icons ref](https://web.dev/maskable-icon/) 48 | `pwa.preferNativeAppOverPWA` | If you have a native app in store and want the app to have a higher preference, set this to true. 49 | 50 | ### config.app 51 | Key | Description 52 | --- | --- 53 | `app.android` | Android app information 54 | `app.android.name` | App name. 55 | `app.android.id` | App ID. For eg. (`com.android.chrome`) 56 | `app.android.storeUrl` | Store URL for the app. For eg. (`https://play.google.com/store/apps/details?id=com.android.chrome`) 57 | `app.ios` | iOS app information 58 | `app.android.name` | App name. 59 | `app.android.id` | App ID. For eg. (`535886823`) 60 | `app.android.storeUrl` | Store URL for the app. For eg. (`https://apps.apple.com/in/app/google-chrome/id535886823`) 61 | 62 | ### config.search 63 | Key | Description 64 | --- | --- 65 | `search.placeholder.header` | Search placeholder on the header. 66 | `search.placeholder.page` | Search placeholder on the page. 67 | `search.sectionFetchLimit` | Results count to show for each section. For eg. if set 10, show 10 products, catalogues and combos. 68 | 69 | ### config.features 70 | Key | Description 71 | --- | --- 72 | `features.enableLandscapeMode` | Enable landscape mode. 73 | `features.enablePageTransition` | Enable transition when navigation changes. 74 | `features.enableScrollToTop` | Show scroll to top UI. 75 | `features.enablePWAPromotions` | Enable PWA promotions. 76 | `features.enableAppPromotions` | Enable app promotions. 77 | `features.enablePagesPrefetching` | If enabled, link pages will be prefetched. [Reference](https://web.dev/route-prefetching-in-nextjs/) 78 | 79 | ### config.build 80 | Learn more [here](./build-strategy.md) 81 | 82 | Key | Description 83 | --- | --- 84 | `build.pageRevalidateTimeInSec` | When should the pages be re-validated? This is mapping for each static page. 85 | `build.initialPageBuildCount` | For pages that are public and dynamic, how many versions should be built initially? 86 | 87 | ### config.integrations 88 | 89 | #### config.integrations.googleOneTapLogIn 90 | For Google one-tap login integration. 91 | 92 | Key | Description 93 | --- | --- 94 | `integrations.googleOneTapLogIn.enabled` | Use this flag to disable. 95 | `integrations.googleOneTapLogIn.code` | Get this code from your app's credentials page from Google cloud console. 96 | 97 | #### config.integrations.googleLogIn 98 | For Google login integration. 99 | 100 | Key | Description 101 | --- | --- 102 | `integrations.googleLogIn.enabled` | Use this flag to disable. 103 | `integrations.googleLogIn.code` | Get this code from your app's credentials page from Google cloud console. 104 | 105 | #### config.integrations.facebookLogIn 106 | For Facebook login integration. 107 | 108 | Key | Description 109 | --- | --- 110 | `integrations.facebookLogIn.enabled` | Use this flag to disable. 111 | `integrations.facebookLogIn.code` | Get this code from Facebook's developer page. 112 | 113 | #### config.integrations.stripePayment 114 | For Stripe payments integration. 115 | 116 | Key | Description 117 | --- | --- 118 | `integrations.stripePayment.code` | Stripe secret key. Get this from dashboard. 119 | `integrations.stripePayment.publicCode` | Stripe public key. Get this from dashboard. 120 | 121 | #### config.integrations.googleAnalytics 122 | For Google analytics integration. 123 | 124 | Key | Description 125 | --- | --- 126 | `integrations.googleAnalytics.enabled` | Use this flag to disable. 127 | `integrations.googleAnalytics.webCode` | GA code. Get this from dashboard. 128 | 129 | #### config.integrations.googleSiteVerification 130 | For site verification on Google search console. 131 | 132 | Key | Description 133 | --- | --- 134 | `integrations.googleSiteVerification.enabled` | Use this flag to disable. 135 | `integrations.googleSiteVerification.code` | Get this from search console. 136 | 137 | #### config.integrations.sentryErrorReporting 138 | For Sentry integration. 139 | 140 | Key | Description 141 | --- | --- 142 | `integrations.googleSiteVerification.enabled` | Use this flag to disable. 143 | `integrations.googleSiteVerification.dsn` | Get this from dashboard. 144 | 145 | #### config.integrations.imageTransformation 146 | Image transformation integration. For eg. if you use `Cloudinary` and want to use their transformation parameters. 147 | 148 | Key | Description 149 | --- | --- 150 | `integrations.imageTransformation.enabled` | Use this flag to disable. 151 | `integrations.imageTransformation.variants` | A mapping of app-specific key and transform parameters. 152 | 153 | ### config.company 154 | Key | Description 155 | --- | --- 156 | `company.name` | Legal company name. 157 | `company.contactNumber` | Company's contact number. 158 | `company.contactEmail` | Company's contact email. 159 | `company.address` | Company address. 160 | `company.socialLinks[]` | Company social links. Following properties are required: 161 | 162 | ### config.footer 163 | Key | Description 164 | --- | --- 165 | `footer.links` | Links to show on footer. 166 | `footer.copyrightText` | Copyright text on footer. 167 | 168 | ### config.share 169 | Key | Description 170 | --- | --- 171 | `share.section.title` | Title to show when user shares the app from section. 172 | `share.product.title` | Title to show when user shares the product. Dynamic keywords allowed: `PRODUCT_NAME` and `PRODUCT_URL`. 173 | -------------------------------------------------------------------------------- /projects/website/faq.md: -------------------------------------------------------------------------------- 1 | --- 2 | label: FAQs 3 | title: Website FAQs 4 | icon: question 5 | order: -3 6 | --- 7 | 8 | ## Which social icons are supported? 9 | ```ts 10 | [ 11 | APPLE, 12 | ANDROID, 13 | ALIBABA, 14 | AMAZON, 15 | EMAIL, 16 | APPSTORE, 17 | BLOGGER, 18 | DISCORD, 19 | DRIVE, 20 | EBAY, 21 | FACEBOOK, 22 | GITHUB, 23 | GITLAB, 24 | MAIL, 25 | GOOGLE, 26 | GOOGLE_PLAY, 27 | INSTAGRAM, 28 | ITUNES, 29 | LINKEDIN, 30 | MEDIUM, 31 | PAYPAL, 32 | PHONE, 33 | PHOTOS, 34 | PINTEREST, 35 | QUORA, 36 | REDDIT, 37 | RSS, 38 | SINA_WEIBO, 39 | SLACK, 40 | SNAPCHAT, 41 | SPOTIFY, 42 | TELEGRAM, 43 | TIKTOK, 44 | TUMBLR, 45 | TWITCH, 46 | TWITTER, 47 | VIMEO, 48 | WECHAT, 49 | WHATSAPP, 50 | WIKIPEDIA, 51 | YELP, 52 | YOUTUBE, 53 | GLOBE, 54 | PWA, 55 | FORM, 56 | REFRESH, 57 | ] 58 | ``` 59 | 60 | ## Which icon library is used? 61 | [Heroicons](https://heroicons.com/) is used. 62 | 63 | ## How can we check debug logs? 64 | We have added grouped logging using [debug](https://www.npmjs.com/package/debug) library. Let's take an example... 65 | 66 | To check `http` logs 67 | ```ts http/httpClient.ts 68 | const log = debug('http') 69 | ``` 70 | 71 | - On CLI: 72 | Use `DEBUG` environment variable. For eg. 73 | ```bash 74 | DEBUG='http' npm run start:local 75 | ``` 76 | 77 | - On Browser: 78 | Use `localStorage.debug` to control. For eg. 79 | ```js 80 | localStorage.debug = 'http' 81 | ``` 82 | -------------------------------------------------------------------------------- /projects/website/folder-structure.md: -------------------------------------------------------------------------------- 1 | --- 2 | label: Folder Structure 3 | title: Website Folder Structure 4 | icon: file-directory 5 | order: 6 6 | --- 7 | 8 | Directory | Description 9 | --- | --- 10 | `/components` | Contains page-level as well as pure components. 11 | `/config` | Contains app-level config. 12 | `/constants` | For global constants. 13 | `/contract` | API contracts are stored here. 14 | `/env` | For ENV files. 15 | `/hooks` | Contains global React hooks. 16 | `/http` | Responsible for connecting with API. 17 | `/pages` | All NextJs pages are kept in this folder. 18 | `/public` | Has all the static files from fonts to images. 19 | `/scriptTemplates` | Contains script templates for for all HTML pages. 20 | `/styles` | Holds all styles-related SCSS files. 21 | `/utils` | Contains page-level as well as pure methods. 22 | -------------------------------------------------------------------------------- /projects/website/index.md: -------------------------------------------------------------------------------- 1 | This is the user-facing website project. 2 | 3 | - [Technologies](./technologies) 4 | - [Folder Structure](./folder-structure) 5 | - [Config](./config) 6 | - [Commands](./commands) 7 | - [Theme](./theme) 8 | - [SEO](./seo) 9 | - [PWA](./pwa) 10 | - [Analytics](./analytics) 11 | - [Payment](./payment) 12 | - [Build Strategy](./build-strategy) 13 | - [FAQs](./faq) 14 | -------------------------------------------------------------------------------- /projects/website/index.yml: -------------------------------------------------------------------------------- 1 | icon: globe 2 | label: Website 3 | order: 2 4 | -------------------------------------------------------------------------------- /projects/website/payment.md: -------------------------------------------------------------------------------- 1 | --- 2 | label: Payment 3 | title: Website Payment 4 | icon: shield-check 5 | order: -1 6 | --- 7 | 8 | Stripe is added as an integration for processing seamless payments. Integration is added at both website and API layer for security. 9 | -------------------------------------------------------------------------------- /projects/website/pwa.md: -------------------------------------------------------------------------------- 1 | --- 2 | label: PWA 3 | title: Website PWA 4 | icon: download 5 | order: 1 6 | --- 7 | 8 | PWA along with offline support is supported in this. There are 2 major components here 9 | - Service Worker 10 | - manifest.json 11 | 12 | # Service Worker 13 | A SW is file is required for PWA to initialize. We have a PWA file in `pwa/service-worker.js`. What does it do? 14 | - Caches all the static files 15 | - Caches the offline page (can be found here: `public/html/offline.html`). Also adds a strategy to show the same HTML file when there's no network. 16 | 17 | !!! 18 | Note: Whenever you make a change in `offline.html`, make sure to update the `VERSION` variable stored in `pwa/service-worker.js`. Else users will keep seeing an outdated offline page. 19 | !!! 20 | 21 | !!! 22 | Also note PWA is disabled on local ENVs 23 | - `local` 24 | - `localproduction` 25 | !!! 26 | 27 | A minified SW file is created while building the project using `pwa/service-worker.js` as a source file. 28 | 29 | ## Manifest 30 | We don't have a static manifest JSON file, because we want values such as theme color, name, summary, etc... to be fetched from config or theme file. Don't want to hardcode here. These values should be defined only once in their config files. 31 | 32 | We have a script which fetches values from config files and prepares a `manifest.json` under `public/json` directory. Before preparing the file, let's first go step-by-step and update the current config for your needs. 33 | 34 | ### Basic Information 35 | Config defined in `appConfig.ts` will be used as manifest's basic information. So this is covered. 36 | 37 | ### Logos 38 | Let's discuss how to add your PWA icons... 39 | 40 | We'll use a third-party online tool for this. 41 | 42 | - Go to [pwabuilder.com](https://www.pwabuilder.com/imageGenerator) 43 | - Upload your logo, update settings (if required) and download the generated zip file. Settings I used: ![](/static/pwa-builder.png) 44 | - Move contents of the downloaded folder except `icons.json` to our logos folder (`public/images/logos/`). 45 | - Copy everything of `icons.json` and paste in `public/json/manifest-icons.json` file. 46 | - Now we need to update the `src` property of each icon in `manifest-icons.json` file. Add `/images/logos/` at the beginning. For eg. 47 | 48 | ```git 49 | - windows10/SmallTile.scale-100.png 50 | + /images/logos/windows10/SmallTile.scale-100.png 51 | ``` 52 | 53 | In the end, an icon object should look something like this... 54 | ```json 55 | { 56 | "src": "/images/logos/windows10/SmallTile.scale-100.png", 57 | "sizes": "71x71" 58 | } 59 | ``` 60 | 61 | - You're all set now for logos. 62 | 63 | ### Favicons 64 | Let's discuss how to add your favicons using a third-party online tool. 65 | 66 | - Go to [favicon-generator.org](https://www.favicon-generator.org/) 67 | - Upload your logo and update settings (if required). Settings I used: ![](/static/favicon-generator.png) 68 | - Once processed, you'll be prompted with 2 resources: 69 | - A zip file with all the required favicons. 70 | - A code snippet. 71 | - Move all the contents of the downloaded file to `public/images/logos` folder. **Note:** Only move image files. 72 | - Copy the code snippet. Only copy the line which has `+` marked against it. 73 | ```git 74 | + 75 | + 76 | + 77 | + 78 | + 79 | + 80 | + 81 | + 82 | + 83 | + 84 | + 85 | + 86 | + 87 | - 88 | - 89 | + 90 | - 91 | ``` 92 | 93 | - Now navigate to `scriptTemplates/meta.tsx`, you will find a function there named `AppIcons`. Paste the copied snippet. The function will now look something like this... 94 | ```tsx 95 | const AppIcons: React.FC = () => { 96 | return ( 97 | <> 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | ) 114 | } 115 | ``` 116 | 117 | - At last, we need to update `href` attribute of each line. Observe the following diff... 118 | ```git 119 | - 120 | + 121 | ``` 122 | 123 | What did we do in this diff? 124 | - Prefix href with `/images/logos` 125 | - Wrap the entire href value with `prepareAssetUrl` method. 126 | - Properly close the tag for JSX. Replaced `>` with `/>`. 127 | 128 | 129 | ### Splashscreens 130 | We'll use a third-party online tool here as well. 131 | 132 | - Goto [appsco.pe](https://appsco.pe/developer/splash-screens) 133 | - Upload your screen, and update settings (if required). Settings I used: ![](/static/appsco-splashscreens.png) 134 | - Download the zip and move contents to `public/images/splashscreens` folder. 135 | - Copy the provided code snippet. 136 | - Navigate to `scriptTemplates/meta.tsx`, you will find a function there named `AppSplashScreens`. Paste the copied snippet. The function will now look something like this... 137 | ```tsx 138 | const AppSplashScreens: React.FC = props => { 139 | return ( 140 | <> 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | ) 153 | } 154 | ``` 155 | 156 | - At last, we need to update `href` attribute of each line. Observe the following diff... 157 | ```git 158 | - 159 | + 160 | ``` 161 | 162 | What did we do in this diff? 163 | - Prefix href with `/images` 164 | - Wrap the entire href value with `prepareAssetUrl` method. 165 | 166 | ### Screenshots 167 | You can also show screenshots to the user when they try to install the PWA. Here's how you can add your own: 168 | - Put your screenshots under `public/images/screenshots` folder. 169 | - Not define your screenshots in `public/json/manifest-screenshots.json` file. Eg. 170 | ```json 171 | { 172 | "src": "/images/screenshots/screenshot-1.png", 173 | "type": "image/png", 174 | "sizes": "750x1364" 175 | }, 176 | ``` 177 | You need to specify 178 | - screenshot location source 179 | - type of the image 180 | - image size 181 | 182 | Eventually the file should look something like this: 183 | ```json 184 | { 185 | "screenshots": [ 186 | { 187 | "src": "/images/screenshots/screenshot-1.png", 188 | "type": "image/png", 189 | "sizes": "750x1364" 190 | } 191 | ] 192 | } 193 | ``` 194 | 195 | You can add multiple screenshots. 196 | 197 | ### Generate manifest.json 198 | Now let's finally generate a manifest file for testing. 199 | ```bash 200 | STORE_WEB_ENV=local npm run script:generate-manifest 201 | ``` 202 | 203 | !!! 204 | Note: Manifest file will be automatically generated whenever you 205 | - run the server locally for development 206 | - build the project 207 | !!! 208 | 209 | !!! 210 | Also note that the `public/json/manifest.json` file ignore by Git since it will generated automatically. 211 | !!! 212 | -------------------------------------------------------------------------------- /projects/website/seo.md: -------------------------------------------------------------------------------- 1 | --- 2 | label: SEO 3 | title: Website SEO 4 | icon: codescan-checkmark 5 | order: 2 6 | --- 7 | 8 | App is very well optimized with industry-standard SEO practices. There are 2 layers for SEO: 9 | - AppSeo 10 | - Page-level SEO 11 | 12 | ## App SEO 13 | App SEO is basically the global file for everything related to SEO. Can be found in `app/components/seo/AppSeo.tsx`. This file contains 14 | - global values 15 | - incoming page-level values 16 | 17 | ## Page-level SEO 18 | Now each page can have few SEO entires that can be dynamic. For eg. title of a page can be dynamic. 19 | 20 | Each page has a method to prepare page-level SEO data. Can be found in `app/utils/seo`. Let's take home page as an example... 21 | 22 | If you goto `app/utils/seo/home.ts`, you will find a method returning few entries. 23 | 24 | ```ts 25 | // http://localhost:3000/ 26 | export const prepareHomePageSeo = (): IAppSeoProps => { 27 | return { 28 | title: 'Home', 29 | description: 'Home', 30 | canonical: `${appConfig.global.baseUrl}${getHomePageUrl()}`, 31 | keywords: [''], 32 | } 33 | } 34 | ``` 35 | 36 | Now these entries will be passed on to `AppSeo.tsx`, that component will create a final SEO package and dump at the head of the page. 37 | 38 | What else can a page SEO method return? 39 | ```ts 40 | title: string 41 | description: string 42 | canonical: string 43 | keywords: string[] 44 | noIndex?: boolean 45 | noFollow?: boolean 46 | openGraph?: { 47 | title?: string 48 | description?: string 49 | } 50 | twitter?: { 51 | card?: 'summary' | 'summary_large_image' 52 | title?: string 53 | description?: string 54 | } 55 | imageUrl?: string 56 | structuredData?: { 57 | breadcrumbList?: { [key: string]: any } 58 | FAQs?: { [key: string]: any } 59 | product?: { [key: string]: any } 60 | } 61 | ``` 62 | 63 | Let's understand what these keys mean 64 | 65 | Key | Description 66 | --- | --- 67 | `title` | Title of the page 68 | `description` | Page description 69 | `canonical` | Page's canonical URL. Should be absolute. 70 | `keywords` | A list of keywords for the page. 71 | `noIndex` | Should the page be indexed? If no, set this to true. 72 | `noFollow` | Should the page be followed? If no, set this to true. 73 | `openGraph` | This is to control page's meta information for OpenGraph/Facebook. For eg. you might want page `title` to be different when link is previewed. 74 | `openGraph.title` | Title for OpenGraph. 75 | `openGraph.description` | Description for OpenGraph. 76 | `twitter` | This is to control page's meta information for Twitter. For eg. you might want page `title` to be different when link is previewed. 77 | `twitter.card` | Should the card be of default size or large? Possible values: `'summary' | 'summary_large_image'` 78 | `twitter.title` | Title for Twitter. 79 | `twitter.description` | Description for Twitter. 80 | `imageUrl` | A page can also have a image URL. For eg. on product page. Should be absolute. 81 | `structuredData` | By default, each page will have the following structured data: 82 | `structuredData.breadcrumbList` | For eg. on individual catalogue page, you might need to show the catalogue index page. [Reference](https://developers.google.com/search/docs/advanced/structured-data/breadcrumb) 83 | `structuredData.FAQs` | Added on FAQ page. [Reference](https://developers.google.com/search/docs/advanced/structured-data/faqpage) 84 | `structuredData.product` | Added for product pages. [Reference](https://developers.google.com/search/docs/advanced/structured-data/product) 85 | -------------------------------------------------------------------------------- /projects/website/technologies.md: -------------------------------------------------------------------------------- 1 | --- 2 | label: Technologies 3 | title: Website Tech stack 4 | icon: rocket 5 | order: 7 6 | --- 7 | 8 | Top level technologies used in this project. 9 | 10 | === NextJs 11 | React framework for better dev experience, optimized build, etc... [Ref](https://nextjs.org/) 12 | === 13 | 14 | === TypeScript 15 | Used for type safety. [Ref](https://www.typescriptlang.org) 16 | === 17 | 18 | === TailwindCSS 19 | Utility-based CSS framework for faster prototyping. [Ref](https://tailwindcss.com/) 20 | === 21 | -------------------------------------------------------------------------------- /projects/website/theme.md: -------------------------------------------------------------------------------- 1 | --- 2 | label: Theme 3 | title: Website Theme 4 | icon: light-bulb 5 | order: 3 6 | --- 7 | 8 | You can learn more about theming in `tailwind.config.js`. This is the only file for everything related to theming. 9 | 10 | There are few theme color variables we should talk about: 11 | 12 | Key | Description 13 | --- | --- 14 | `primary` | App's main theme color. 15 | `lightPrimary` | Used along with `primary` primarily only for hover effects. 16 | `primaryText` | Primary color used for texts. 17 | `primaryTextBold` | Used as dark primary color for texts. 18 | -------------------------------------------------------------------------------- /retype.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 3003 3 | 4 | input: . 5 | output: dist 6 | url: https://own-store-doc.vercel.app 7 | 8 | branding: 9 | title: OwnStore 10 | label: Docs 11 | logo: /static/logo.png 12 | logoDark: /static/logo.png 13 | colors: 14 | label: 15 | text: #1f7aff 16 | background: #e1edff 17 | 18 | links: 19 | - text: Website 20 | link: https://own-store-demo.vercel.app 21 | target: blank 22 | icon: globe 23 | 24 | - text: Twitter 25 | link: https://twitter.com/ownStore_ 26 | target: blank 27 | icon: person-add 28 | 29 | - text: Facebook 30 | link: https://www.facebook.com/ownStoreFB 31 | target: blank 32 | icon: thumbsup 33 | 34 | - text: Instagram 35 | link: https://www.instagram.com/ownStore__/ 36 | target: blank 37 | icon: heart 38 | 39 | - text: Email 40 | link: mailto:ownstoreonlinee@gmail.com 41 | target: blank 42 | icon: mention 43 | 44 | meta: 45 | title: " | OwnStore Documentation" 46 | 47 | footer: 48 | copyright: "© {{ year }} OwnStore. All rights reserved" 49 | 50 | integrations: 51 | googleAnalytics: 52 | id: G-HHSJ80HWKF 53 | -------------------------------------------------------------------------------- /static/appsco-splashscreens.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OwnStoreOrg/ownstore-doc/d4df9c89aef1c4199e5c9739e76a81fb57591bbe/static/appsco-splashscreens.png -------------------------------------------------------------------------------- /static/favicon-generator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OwnStoreOrg/ownstore-doc/d4df9c89aef1c4199e5c9739e76a81fb57591bbe/static/favicon-generator.png -------------------------------------------------------------------------------- /static/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OwnStoreOrg/ownstore-doc/d4df9c89aef1c4199e5c9739e76a81fb57591bbe/static/logo.png -------------------------------------------------------------------------------- /static/product-properties-combo-product.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OwnStoreOrg/ownstore-doc/d4df9c89aef1c4199e5c9739e76a81fb57591bbe/static/product-properties-combo-product.png -------------------------------------------------------------------------------- /static/product-properties-individual-product.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OwnStoreOrg/ownstore-doc/d4df9c89aef1c4199e5c9739e76a81fb57591bbe/static/product-properties-individual-product.png -------------------------------------------------------------------------------- /static/pwa-builder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OwnStoreOrg/ownstore-doc/d4df9c89aef1c4199e5c9739e76a81fb57591bbe/static/pwa-builder.png --------------------------------------------------------------------------------