├── .gitignore ├── LICENSE ├── README.md ├── backend ├── api │ ├── .babelrc │ ├── .dockerignore │ ├── .env.dev.example │ ├── .env.prod.example │ ├── .gitignore │ ├── .prettierrc │ ├── Dockerfile │ ├── nodemon.json │ ├── package-lock.json │ ├── package.json │ ├── public │ │ └── images │ │ │ └── user │ │ │ └── default.jpg │ ├── src │ │ ├── index.js │ │ ├── modules │ │ │ ├── email │ │ │ │ ├── model.js │ │ │ │ ├── send.js │ │ │ │ └── template │ │ │ │ │ ├── Layout.js │ │ │ │ │ └── view.js │ │ │ ├── note │ │ │ │ ├── index.js │ │ │ │ ├── model.js │ │ │ │ ├── mutation.js │ │ │ │ ├── query.js │ │ │ │ └── seed.js │ │ │ └── user │ │ │ │ ├── email │ │ │ │ └── Signup.js │ │ │ │ ├── index.js │ │ │ │ ├── model.js │ │ │ │ ├── mutation.js │ │ │ │ ├── query.js │ │ │ │ └── seed.js │ │ └── setup │ │ │ ├── config │ │ │ ├── env.js │ │ │ └── params.json │ │ │ ├── helpers │ │ │ ├── utils.js │ │ │ └── validation.js │ │ │ ├── server │ │ │ ├── authentication.js │ │ │ ├── database.js │ │ │ ├── endpoint.js │ │ │ ├── language.js │ │ │ ├── middlewares.js │ │ │ ├── modules.js │ │ │ ├── seeder.js │ │ │ ├── start.js │ │ │ └── upload.js │ │ │ └── translate │ │ │ ├── en.json │ │ │ └── index.js │ └── uploads │ │ └── .gitkeep ├── database │ ├── .dockerignore │ ├── .gitignore │ ├── Dockerfile │ └── data │ │ └── .gitkeep └── proxy │ └── nginx.conf ├── deployment └── docker-compose.yml └── frontend ├── app ├── mobile │ ├── .buckconfig │ ├── .flowconfig │ ├── .gitattributes │ ├── .gitignore │ ├── .prettierrc.js │ ├── .watchmanconfig │ ├── android │ │ ├── app │ │ │ ├── BUCK │ │ │ ├── build.gradle │ │ │ ├── build_defs.bzl │ │ │ ├── debug.keystore │ │ │ ├── proguard-rules.pro │ │ │ └── src │ │ │ │ ├── debug │ │ │ │ ├── AndroidManifest.xml │ │ │ │ ├── ReactNativeFlipper.java │ │ │ │ └── java │ │ │ │ │ └── com │ │ │ │ │ └── example │ │ │ │ │ └── ReactNativeFlipper.java │ │ │ │ └── main │ │ │ │ ├── AndroidManifest.xml │ │ │ │ ├── assets │ │ │ │ └── fonts │ │ │ │ │ ├── AntDesign.ttf │ │ │ │ │ ├── Entypo.ttf │ │ │ │ │ ├── EvilIcons.ttf │ │ │ │ │ ├── Feather.ttf │ │ │ │ │ ├── FontAwesome.ttf │ │ │ │ │ ├── FontAwesome5_Brands.ttf │ │ │ │ │ ├── FontAwesome5_Regular.ttf │ │ │ │ │ ├── FontAwesome5_Solid.ttf │ │ │ │ │ ├── Fontisto.ttf │ │ │ │ │ ├── Foundation.ttf │ │ │ │ │ ├── Ionicons.ttf │ │ │ │ │ ├── MaterialCommunityIcons.ttf │ │ │ │ │ ├── MaterialIcons.ttf │ │ │ │ │ ├── Octicons.ttf │ │ │ │ │ ├── SimpleLineIcons.ttf │ │ │ │ │ └── Zocial.ttf │ │ │ │ ├── java │ │ │ │ └── com │ │ │ │ │ └── example │ │ │ │ │ ├── MainActivity.java │ │ │ │ │ └── MainApplication.java │ │ │ │ └── res │ │ │ │ ├── drawable-hdpi │ │ │ │ └── screen.png │ │ │ │ ├── drawable-ldpi │ │ │ │ └── screen.png │ │ │ │ ├── drawable-mdpi │ │ │ │ └── screen.png │ │ │ │ ├── drawable-xhdpi │ │ │ │ └── screen.png │ │ │ │ ├── drawable-xxhdpi │ │ │ │ └── screen.png │ │ │ │ ├── drawable-xxxhdpi │ │ │ │ └── screen.png │ │ │ │ ├── drawable │ │ │ │ ├── screen.png │ │ │ │ └── splash.xml │ │ │ │ ├── mipmap-hdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ │ ├── mipmap-ldpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-mdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ │ ├── mipmap-xhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ │ ├── playstore-icon.png │ │ │ │ └── values │ │ │ │ ├── colors.xml │ │ │ │ ├── strings.xml │ │ │ │ └── styles.xml │ │ ├── build.gradle │ │ ├── gradle.properties │ │ ├── gradle │ │ │ └── wrapper │ │ │ │ ├── gradle-wrapper.jar │ │ │ │ └── gradle-wrapper.properties │ │ ├── gradlew │ │ ├── gradlew.bat │ │ └── settings.gradle │ ├── app.json │ ├── assets │ │ └── logo.png │ ├── babel.config.js │ ├── index.js │ ├── ios │ │ ├── File.swift │ │ ├── Podfile │ │ ├── Podfile.lock │ │ ├── example-Bridging-Header.h │ │ ├── example-tvOS │ │ │ └── Info.plist │ │ ├── example-tvOSTests │ │ │ └── Info.plist │ │ ├── example.xcodeproj │ │ │ ├── project.pbxproj │ │ │ ├── project.xcworkspace │ │ │ │ ├── contents.xcworkspacedata │ │ │ │ └── xcshareddata │ │ │ │ │ └── IDEWorkspaceChecks.plist │ │ │ └── xcshareddata │ │ │ │ └── xcschemes │ │ │ │ ├── example-tvOS.xcscheme │ │ │ │ └── example.xcscheme │ │ ├── example.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata │ │ │ │ └── IDEWorkspaceChecks.plist │ │ ├── example │ │ │ ├── AppDelegate.h │ │ │ ├── AppDelegate.m │ │ │ ├── Base.lproj │ │ │ │ └── LaunchScreen.xib │ │ │ ├── Images.xcassets │ │ │ │ ├── AppIcon.appiconset │ │ │ │ │ ├── Contents.json │ │ │ │ │ ├── Icon-App-20x20@1x.png │ │ │ │ │ ├── Icon-App-20x20@2x.png │ │ │ │ │ ├── Icon-App-20x20@3x.png │ │ │ │ │ ├── Icon-App-29x29@1x.png │ │ │ │ │ ├── Icon-App-29x29@2x.png │ │ │ │ │ ├── Icon-App-29x29@3x.png │ │ │ │ │ ├── Icon-App-40x40@1x.png │ │ │ │ │ ├── Icon-App-40x40@2x.png │ │ │ │ │ ├── Icon-App-40x40@3x.png │ │ │ │ │ ├── Icon-App-60x60@2x.png │ │ │ │ │ ├── Icon-App-60x60@3x.png │ │ │ │ │ ├── Icon-App-76x76@1x.png │ │ │ │ │ ├── Icon-App-76x76@2x.png │ │ │ │ │ ├── Icon-App-83.5x83.5@2x.png │ │ │ │ │ └── ItunesArtwork@2x.png │ │ │ │ ├── Contents.json │ │ │ │ ├── iTunesArtwork@1x.png │ │ │ │ ├── iTunesArtwork@2x.png │ │ │ │ └── iTunesArtwork@3x.png │ │ │ ├── Info.plist │ │ │ ├── LaunchScreen.storyboard │ │ │ └── main.m │ │ └── exampleTests │ │ │ ├── Info.plist │ │ │ └── exampleTests.m │ ├── metro.config.js │ ├── package-lock.json │ ├── package.json │ └── src │ │ ├── modules │ │ ├── common │ │ │ ├── Body │ │ │ │ ├── index.js │ │ │ │ └── styles.js │ │ │ ├── Centerize │ │ │ │ ├── index.js │ │ │ │ └── styles.js │ │ │ ├── EmptyMessage │ │ │ │ ├── index.js │ │ │ │ └── styles.js │ │ │ ├── Loading │ │ │ │ ├── index.js │ │ │ │ └── styles.js │ │ │ ├── Logo │ │ │ │ ├── index.js │ │ │ │ └── styles.js │ │ │ ├── Message │ │ │ │ └── index.js │ │ │ ├── NavigationTop │ │ │ │ ├── ActionBack │ │ │ │ │ └── index.js │ │ │ │ ├── ActionClose │ │ │ │ │ └── index.js │ │ │ │ ├── ActionDrawer │ │ │ │ │ └── index.js │ │ │ │ ├── index.js │ │ │ │ └── styles.js │ │ │ ├── NavigationTopInner │ │ │ │ ├── index.js │ │ │ │ └── styles.js │ │ │ ├── NavigationTopTransparent │ │ │ │ ├── index.js │ │ │ │ └── styles.js │ │ │ ├── api │ │ │ │ ├── actions.js │ │ │ │ ├── state.js │ │ │ │ └── types.js │ │ │ └── translations │ │ │ │ └── en.json │ │ ├── note │ │ │ ├── Create │ │ │ │ ├── index.js │ │ │ │ └── styles.js │ │ │ ├── Detail │ │ │ │ ├── index.js │ │ │ │ └── styles.js │ │ │ ├── Item │ │ │ │ ├── index.js │ │ │ │ └── styles.js │ │ │ ├── List │ │ │ │ ├── index.js │ │ │ │ └── styles.js │ │ │ ├── api │ │ │ │ ├── actions │ │ │ │ │ ├── cache-keys.js │ │ │ │ │ ├── mutation.js │ │ │ │ │ ├── query.js │ │ │ │ │ └── types.js │ │ │ │ └── state │ │ │ │ │ ├── index.js │ │ │ │ │ └── list.js │ │ │ └── translations │ │ │ │ └── en.json │ │ ├── pages │ │ │ ├── Entry │ │ │ │ ├── index.js │ │ │ │ └── styles.js │ │ │ ├── Help │ │ │ │ ├── index.js │ │ │ │ └── styles.js │ │ │ └── translations │ │ │ │ └── en.json │ │ └── user │ │ │ ├── Login │ │ │ ├── index.js │ │ │ └── styles.js │ │ │ ├── Profile │ │ │ ├── MyInfo │ │ │ │ ├── Form │ │ │ │ │ └── index.js │ │ │ │ ├── index.js │ │ │ │ └── styles.js │ │ │ ├── index.js │ │ │ └── styles.js │ │ │ ├── Signup │ │ │ ├── index.js │ │ │ └── styles.js │ │ │ ├── Start │ │ │ ├── index.js │ │ │ └── styles.js │ │ │ ├── api │ │ │ ├── actions │ │ │ │ ├── cache-keys.js │ │ │ │ ├── mutation.js │ │ │ │ ├── query.js │ │ │ │ └── types.js │ │ │ └── state │ │ │ │ ├── auth.js │ │ │ │ ├── index.js │ │ │ │ └── list.js │ │ │ └── translations │ │ │ └── en.json │ │ ├── setup │ │ ├── config │ │ │ ├── env.js │ │ │ └── params.json │ │ ├── helpers │ │ │ └── utils.js │ │ ├── index.js │ │ ├── routes │ │ │ ├── index.js │ │ │ ├── names.js │ │ │ ├── postLogin │ │ │ │ ├── index.js │ │ │ │ ├── note.js │ │ │ │ └── user.js │ │ │ └── preLogin │ │ │ │ └── index.js │ │ ├── store.js │ │ └── translate │ │ │ ├── en.js │ │ │ └── index.js │ │ └── ui │ │ ├── Toast │ │ └── index.js │ │ ├── Typography │ │ └── index.js │ │ ├── button │ │ ├── Button.js │ │ └── Fab.js │ │ ├── common │ │ ├── colors.js │ │ ├── responsive.js │ │ └── styles.js │ │ ├── divider │ │ └── Item.js │ │ ├── icon │ │ ├── ActionIcon.js │ │ └── Icon.js │ │ └── input │ │ ├── ButtonGroup.js │ │ ├── Switch.js │ │ ├── Text.js │ │ └── TextWithLabel.js └── web │ ├── .dockerignore │ ├── .env.dev.example │ ├── .env.prod.example │ ├── .eslintcache │ ├── .gitignore │ ├── .prettierrc.js │ ├── Dockerfile │ ├── package-lock.json │ ├── package.json │ ├── public │ ├── images │ │ ├── favicon │ │ │ ├── android-chrome-192x192.png │ │ │ ├── apple-touch-icon.png │ │ │ ├── browserconfig.xml │ │ │ ├── favicon-16x16.png │ │ │ ├── favicon-32x32.png │ │ │ ├── favicon.ico │ │ │ ├── mstile-150x150.png │ │ │ └── site.webmanifest │ │ └── logo.png │ ├── index.html │ └── manifest.json │ └── src │ ├── index.js │ ├── modules │ ├── admin │ │ ├── dashboard │ │ │ └── Dashboard │ │ │ │ ├── index.js │ │ │ │ └── styles.js │ │ └── user │ │ │ └── List │ │ │ ├── Item │ │ │ └── index.js │ │ │ ├── index.js │ │ │ └── styles.js │ ├── auth │ │ ├── AuthCheck.js │ │ └── RoutePrivate.js │ ├── common │ │ ├── EmptyMessage │ │ │ ├── index.js │ │ │ └── styles.js │ │ ├── Header │ │ │ ├── index.js │ │ │ └── styles.js │ │ ├── Layout │ │ │ └── index.js │ │ ├── Loading │ │ │ ├── index.js │ │ │ └── styles.js │ │ ├── Message │ │ │ ├── index.js │ │ │ └── styles.js │ │ ├── Redirector │ │ │ └── index.js │ │ ├── Section │ │ │ ├── index.js │ │ │ └── styles.js │ │ └── api │ │ │ ├── actions.js │ │ │ └── state.js │ ├── note │ │ ├── Create │ │ │ ├── index.js │ │ │ └── styles.js │ │ ├── List │ │ │ ├── Item │ │ │ │ ├── index.js │ │ │ │ └── styles.js │ │ │ ├── index.js │ │ │ └── styles.js │ │ └── api │ │ │ ├── actions │ │ │ ├── cache-keys.js │ │ │ ├── mutation.js │ │ │ ├── query.js │ │ │ └── types.js │ │ │ └── state │ │ │ ├── index.js │ │ │ └── list.js │ ├── pages │ │ └── Home │ │ │ ├── index.js │ │ │ └── styles.js │ └── user │ │ ├── Dashboard │ │ ├── index.js │ │ └── styles.js │ │ ├── Login │ │ ├── index.js │ │ └── styles.js │ │ ├── Profile │ │ ├── index.js │ │ └── styles.js │ │ ├── Signup │ │ ├── index.js │ │ └── styles.js │ │ └── api │ │ ├── actions │ │ ├── cache-keys.js │ │ ├── mutation.js │ │ ├── query.js │ │ └── types.js │ │ └── state │ │ ├── auth.js │ │ ├── index.js │ │ └── list.js │ ├── serviceWorker.js │ └── setup │ ├── config │ ├── env.js │ └── params.json │ ├── helpers.js │ ├── routes │ ├── admin │ │ ├── dashboard.js │ │ ├── index.js │ │ └── user.js │ ├── index.js │ ├── note.js │ ├── pages.js │ └── user.js │ ├── store.js │ └── theme.js └── landing ├── .env.development ├── .env.production ├── .gitignore ├── .prettierrc ├── .prettierrc.js ├── README.md ├── jsconfig.json ├── next.config.js ├── package-lock.json ├── package.json ├── public ├── images │ ├── favicon │ │ ├── android-chrome-192x192.png │ │ ├── apple-touch-icon.png │ │ ├── browserconfig.xml │ │ ├── favicon-16x16.png │ │ ├── favicon-32x32.png │ │ ├── favicon.ico │ │ ├── mstile-150x150.png │ │ └── site.webmanifest │ └── logo.png ├── reset.css └── robots.txt └── src ├── modules └── common │ ├── Header │ ├── index.js │ └── styles.js │ ├── Layout │ └── index.js │ ├── Scroll │ └── index.js │ └── Section │ ├── index.js │ └── styles.js ├── pages ├── 404 │ ├── index.js │ └── styles.js ├── _app.js ├── _document.js ├── contact │ └── index.js ├── home │ ├── index.js │ └── styles.js ├── index.js ├── privacy │ ├── index.js │ └── styles.js ├── reset.css └── terms │ ├── index.js │ └── styles.js └── setup ├── analytics.js ├── config ├── env.js └── params.json ├── helpers.js ├── routes.js └── theme.js /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Atul Yadav 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 | -------------------------------------------------------------------------------- /backend/api/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "@babel/preset-env", 4 | "@babel/preset-react" 5 | ], 6 | 7 | "plugins": [ 8 | "@babel/plugin-transform-runtime" 9 | ] 10 | } -------------------------------------------------------------------------------- /backend/api/.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | build 3 | public 4 | -------------------------------------------------------------------------------- /backend/api/.env.dev.example: -------------------------------------------------------------------------------- 1 | PORT=8000 2 | NODE_ENV=development 3 | 4 | SECURITY_SECRET= 5 | SECURITY_SALT_ROUNDS=10 6 | 7 | MONGO_URL=mongodb://localhost:27017/example 8 | 9 | LANDING_URL=http://localhost:3000 10 | WEB_URL=http://localhost:5000 11 | API_URL=http://localhost:8000 12 | 13 | EMAIL_ON=0 14 | EMAIL_TEST=someone@example.com 15 | EMAIL_HOST=smtp.mailgun.org 16 | EMAIL_USER= 17 | EMAIL_PASSWORD= 18 | -------------------------------------------------------------------------------- /backend/api/.env.prod.example: -------------------------------------------------------------------------------- 1 | PORT=8000 2 | NODE_ENV=production 3 | 4 | SECURITY_SECRET= 5 | SECURITY_SALT_ROUNDS=10 6 | 7 | MONGO_URL=mongodb://database:27017/example 8 | 9 | LANDING_URL=http://example.com 10 | WEB_URL=http://app.example.com 11 | API_URL=http://api.example.com 12 | 13 | EMAIL_ON=0 14 | EMAIL_TEST=someone@example.com 15 | EMAIL_HOST=smtp.mailgun.org 16 | EMAIL_USER= 17 | EMAIL_PASSWORD= 18 | -------------------------------------------------------------------------------- /backend/api/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | build/ 3 | 4 | .env.local 5 | 6 | uploads/* 7 | !uploads/.gitkeep 8 | 9 | public/images/user/* 10 | !public/images/user/default.jpg 11 | -------------------------------------------------------------------------------- /backend/api/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "all", 3 | "semi": false, 4 | "tabWidth": 2, 5 | "useTabs": false, 6 | "arrowParens": "always", 7 | "singleQuote": true, 8 | "jsxSingleQuote": true 9 | } 10 | -------------------------------------------------------------------------------- /backend/api/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:10 2 | RUN mkdir -p /user/src/app 3 | WORKDIR /user/src/app 4 | COPY ./package*.json ./ 5 | RUN npm install --quiet 6 | COPY . ./ 7 | RUN npm run build:prod 8 | EXPOSE 8000 9 | ENTRYPOINT ["node", "build/index.js"] 10 | -------------------------------------------------------------------------------- /backend/api/nodemon.json: -------------------------------------------------------------------------------- 1 | { 2 | "ignore": [ 3 | "build/*", 4 | "node_modules/**/node_modules" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /backend/api/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "api", 3 | "version": "1.0.0", 4 | "description": "API application", 5 | "main": "index.js", 6 | "scripts": { 7 | "seed": "babel-node src/setup/server/seeder.js", 8 | "setup": "npm install && npm run seed", 9 | "start": "nodemon src/index.js --exec babel-node", 10 | "start:prod": "npm run build:prod && npm run start:server", 11 | "build:prod": "babel src -s -D -d build", 12 | "start:server": "node build/index.js" 13 | }, 14 | "husky": { 15 | "hooks": { 16 | "pre-commit": "pretty-quick --staged" 17 | } 18 | }, 19 | "keywords": [], 20 | "author": "", 21 | "license": "ISC", 22 | "dependencies": { 23 | "axios": "0.21.1", 24 | "bcrypt": "5.0.0", 25 | "body-parser": "1.19.0", 26 | "cors": "2.8.5", 27 | "dotenv": "8.2.0", 28 | "express": "4.17.1", 29 | "fullstack-validator": "1.0.0", 30 | "i18n-js": "3.8.0", 31 | "ip": "1.1.5", 32 | "jsonwebtoken": "8.5.1", 33 | "lodash": "4.17.21", 34 | "moment": "^2.29.1", 35 | "mongoose": "5.11.18", 36 | "morgan": "1.10.0", 37 | "multer": "1.4.2", 38 | "nodemailer": "6.4.18", 39 | "prop-types": "15.7.2", 40 | "react": "17.0.1", 41 | "react-dom": "17.0.1", 42 | "sharp": "0.27.2" 43 | }, 44 | "devDependencies": { 45 | "@babel/cli": "7.13.0", 46 | "@babel/core": "7.13.1", 47 | "@babel/node": "7.13.0", 48 | "@babel/plugin-transform-runtime": "7.13.7", 49 | "@babel/preset-env": "7.13.5", 50 | "@babel/preset-react": "7.12.13", 51 | "@babel/runtime": "7.13.7", 52 | "husky": "^4.3.8", 53 | "nodemon": "2.0.7", 54 | "prettier": "2.2.1", 55 | "pretty-quick": "^3.1.0" 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /backend/api/public/images/user/default.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atulmy/fullstack-javascript-architecture/8f4fa626bb9d3fe8210ad601698ac166f15f2363/backend/api/public/images/user/default.jpg -------------------------------------------------------------------------------- /backend/api/src/index.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import express from 'express' 3 | 4 | // App Imports 5 | import database from './setup/server/database' 6 | import middlewares from './setup/server/middlewares' 7 | import upload from './setup/server/upload' 8 | import endpoint from './setup/server/endpoint' 9 | import start from './setup/server/start' 10 | 11 | // Create express server 12 | const server = express() 13 | 14 | // Connect database 15 | database() 16 | 17 | // Setup middlewares 18 | middlewares(server) 19 | 20 | // Setup uploads 21 | upload(server) 22 | 23 | // Setup endpoint 24 | endpoint(server) 25 | 26 | // Start server 27 | start(server) 28 | -------------------------------------------------------------------------------- /backend/api/src/modules/email/model.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import mongoose, { Schema } from 'mongoose' 3 | 4 | // App Imports 5 | import { collection as User } from '../user/model' 6 | 7 | // Collection name 8 | export const collection = 'Email' 9 | 10 | // Schema 11 | const schema = new Schema( 12 | { 13 | userId: { 14 | type: Schema.Types.ObjectId, 15 | ref: User, 16 | }, 17 | toName: { 18 | type: String, 19 | }, 20 | toEmail: { 21 | type: String, 22 | required: true, 23 | }, 24 | fromName: { 25 | type: String, 26 | required: true, 27 | }, 28 | fromEmail: { 29 | type: String, 30 | required: true, 31 | }, 32 | subject: { 33 | type: String, 34 | required: true, 35 | }, 36 | body: { 37 | type: String, 38 | required: true, 39 | }, 40 | }, 41 | { timestamps: true }, 42 | ) 43 | 44 | // Model 45 | export default mongoose.model(collection, schema, collection) 46 | -------------------------------------------------------------------------------- /backend/api/src/modules/email/template/Layout.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import React from 'react' 3 | 4 | // App Imports 5 | import params from '../../../setup/config/params' 6 | 7 | // Component 8 | const Layout = ({ translate, children }) => ( 9 | 10 | {children} 11 | 12 |

{translate.t('common.email.footer.message')}

13 | 14 |

15 | {translate.t('common.email.footer.thanks')},
16 | {params.site.name} 17 |

18 |
19 | ) 20 | 21 | export default Layout 22 | -------------------------------------------------------------------------------- /backend/api/src/modules/email/template/view.js: -------------------------------------------------------------------------------- 1 | const view = (html) => html 2 | 3 | export default view 4 | -------------------------------------------------------------------------------- /backend/api/src/modules/note/index.js: -------------------------------------------------------------------------------- 1 | // App Imports 2 | import * as query from './query' 3 | import * as mutation from './mutation' 4 | 5 | export default { 6 | ...query, 7 | ...mutation, 8 | } 9 | -------------------------------------------------------------------------------- /backend/api/src/modules/note/model.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import mongoose, { Schema } from 'mongoose' 3 | 4 | // App Imports 5 | import { collection as User } from '../user/model' 6 | 7 | // Collection name 8 | export const collection = 'Note' 9 | 10 | // Schema 11 | const schema = new Schema( 12 | { 13 | userId: { 14 | type: mongoose.Schema.Types.ObjectId, 15 | required: true, 16 | ref: User, 17 | index: true, 18 | }, 19 | 20 | note: { 21 | type: String, 22 | required: true, 23 | }, 24 | 25 | isDeleted: { 26 | type: Boolean, 27 | required: true, 28 | default: false, 29 | }, 30 | }, 31 | { timestamps: true }, 32 | ) 33 | 34 | // Model 35 | export default mongoose.model(collection, schema, collection) 36 | -------------------------------------------------------------------------------- /backend/api/src/modules/note/mutation.js: -------------------------------------------------------------------------------- 1 | // App Imports 2 | import { authCheck } from '../../setup/helpers/utils' 3 | import v from '../../setup/helpers/validation' 4 | import Note from './model' 5 | 6 | // Create 7 | export async function noteCreate({ params: { note }, auth, translate }) { 8 | if (authCheck(auth)) { 9 | // Validation rules 10 | const rules = [ 11 | { 12 | data: { value: note }, 13 | check: 'isNotEmpty', 14 | message: translate.t('note.messages.fields.note'), 15 | }, 16 | ] 17 | 18 | // Validate 19 | try { 20 | v.validate(rules) 21 | } catch (error) { 22 | throw new Error(error.message) 23 | } 24 | 25 | // Create or Update 26 | try { 27 | const data = await Note.create({ 28 | userId: auth.user._id, 29 | note, 30 | isDeleted: false, 31 | }) 32 | 33 | return { 34 | data, 35 | message: translate.t('note.messages.create.success'), 36 | } 37 | } catch (error) { 38 | throw new Error(translate.t('common.messages.error.server')) 39 | } 40 | } 41 | 42 | throw new Error(translate.t('common.messages.error.unauthorized')) 43 | } 44 | 45 | // Delete 46 | export async function noteDelete({ params: { noteId }, auth, translate }) { 47 | if (authCheck(auth)) { 48 | // Validation rules 49 | const rules = [ 50 | { 51 | data: { value: noteId }, 52 | check: 'isNotEmpty', 53 | message: translate.t('note.messages.remove.error'), 54 | }, 55 | ] 56 | 57 | // Validate 58 | try { 59 | v.validate(rules) 60 | } catch (error) { 61 | throw new Error(error.message) 62 | } 63 | 64 | try { 65 | const data = await Note.updateOne( 66 | { _id: noteId }, 67 | { $set: { isDeleted: true } }, 68 | ) 69 | 70 | return { 71 | data, 72 | message: translate.t('note.messages.remove.success'), 73 | } 74 | } catch (error) { 75 | throw new Error(translate.t('common.messages.error.server')) 76 | } 77 | } 78 | 79 | throw new Error(translate.t('common.messages.error.unauthorized')) 80 | } 81 | -------------------------------------------------------------------------------- /backend/api/src/modules/note/query.js: -------------------------------------------------------------------------------- 1 | // App Imports 2 | import { authCheck } from '../../setup/helpers/utils' 3 | import Note from './model' 4 | 5 | // Get all 6 | export async function noteList({ auth, fields, translate }) { 7 | if (authCheck(auth)) { 8 | try { 9 | const data = await Note.find({ 10 | userId: auth.user._id, 11 | isDeleted: false, 12 | }) 13 | .sort({ createdAt: -1 }) 14 | .select(fields) 15 | 16 | return { 17 | data, 18 | } 19 | } catch (error) { 20 | throw new Error(translate.t('common.messages.error.server')) 21 | } 22 | } 23 | 24 | throw new Error(translate.t('common.messages.error.unauthorized')) 25 | } 26 | 27 | // Get by id 28 | export async function noteDetail({ 29 | params: { noteId }, 30 | auth, 31 | fields, 32 | translate, 33 | }) { 34 | if (authCheck(auth)) { 35 | try { 36 | const data = await Note.findOne({ 37 | _id: noteId, 38 | userId: auth.user._id, 39 | isDeleted: false, 40 | }).select(fields) 41 | 42 | return { 43 | data, 44 | } 45 | } catch (error) { 46 | throw new Error(translate.t('common.messages.error.server')) 47 | } 48 | } 49 | 50 | throw new Error(translate.t('common.messages.error.unauthorized')) 51 | } 52 | -------------------------------------------------------------------------------- /backend/api/src/modules/note/seed.js: -------------------------------------------------------------------------------- 1 | // App Imports 2 | import User from '../user/model' 3 | import Note from './model' 4 | 5 | // Seeds 6 | export default async function () { 7 | console.log('SEED - Note..') 8 | 9 | const user = await User.findOne().sort({ createdAt: -1 }) 10 | 11 | const notes = [ 12 | { 13 | userId: user._id, 14 | note: 'Heu, clinias! Abactus, adiurator, et guttus.', 15 | }, 16 | 17 | { 18 | userId: user._id, 19 | note: 'Sunt caesiumes quaestio mirabilis, magnum hydraes.', 20 | }, 21 | ] 22 | 23 | for (const note of notes) { 24 | await Note.create(note) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /backend/api/src/modules/user/email/Signup.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import React from 'react' 3 | import PropTypes from 'prop-types' 4 | 5 | // App Imports 6 | import params from '../../../setup/config/params' 7 | 8 | // Component 9 | const Signup = ({ translate, to }) => ( 10 | 11 |

12 | {translate.t('common.email.head.greet')} {to}, 13 |

14 | 15 |

16 | {translate.t('user.signup.email.message', { site: params.site.name })} 17 |

18 |
19 | ) 20 | 21 | // Component Properties 22 | Signup.propTypes = { 23 | to: PropTypes.string.isRequired, 24 | } 25 | 26 | export default Signup 27 | -------------------------------------------------------------------------------- /backend/api/src/modules/user/index.js: -------------------------------------------------------------------------------- 1 | // App Imports 2 | import * as query from './query' 3 | import * as mutation from './mutation' 4 | 5 | export default { 6 | ...query, 7 | ...mutation, 8 | } 9 | -------------------------------------------------------------------------------- /backend/api/src/modules/user/model.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import mongoose, { Schema } from 'mongoose' 3 | 4 | // App Imports 5 | import params from '../../setup/config/params' 6 | 7 | // Collection name 8 | export const collection = 'User' 9 | 10 | // Schema 11 | const schema = new Schema( 12 | { 13 | email: { 14 | type: String, 15 | unique: true, 16 | required: true, 17 | }, 18 | 19 | password: { 20 | type: String, 21 | required: true, 22 | }, 23 | 24 | role: { 25 | type: String, 26 | required: true, 27 | default: params.user.roles.user.key, 28 | }, 29 | 30 | name: { 31 | type: String, 32 | }, 33 | 34 | image: { 35 | type: String, 36 | }, 37 | 38 | isDeleted: { 39 | type: Boolean, 40 | required: true, 41 | default: false, 42 | }, 43 | }, 44 | { timestamps: true }, 45 | ) 46 | 47 | // Model 48 | export default mongoose.model(collection, schema, collection) 49 | -------------------------------------------------------------------------------- /backend/api/src/modules/user/seed.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import bcrypt from 'bcrypt' 3 | 4 | // App Imports 5 | import { SECURITY_SALT_ROUNDS } from '../../setup/config/env' 6 | import params from '../../setup/config/params' 7 | import User from './model' 8 | 9 | // Seeds 10 | export default async function () { 11 | console.log('SEED - Users..') 12 | 13 | const users = [ 14 | { 15 | email: 'admin@example.com', 16 | password: '123456', 17 | role: params.user.roles.admin.key, 18 | name: 'Admin', 19 | image: 'default.jpg', 20 | isDeleted: false, 21 | }, 22 | 23 | { 24 | email: 'user@example.com', 25 | password: '123456', 26 | role: params.user.roles.user.key, 27 | name: 'User', 28 | image: 'default.jpg', 29 | isDeleted: false, 30 | }, 31 | ] 32 | 33 | for (const user of users) { 34 | user.password = await bcrypt.hash(user.password, SECURITY_SALT_ROUNDS) 35 | await User.create(user) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /backend/api/src/setup/config/env.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import dotenv from 'dotenv' 3 | 4 | // Load .env 5 | dotenv.config({ path: '.env.local' }) 6 | 7 | // Environment 8 | export const NODE_ENV = process.env.NODE_ENV 9 | 10 | // Security 11 | export const SECURITY_SECRET = process.env.SECURITY_SECRET 12 | export const SECURITY_SALT_ROUNDS = parseInt(process.env.SECURITY_SALT_ROUNDS) 13 | 14 | // Port 15 | export const PORT = process.env.PORT 16 | 17 | // Database 18 | export const MONGO_URL = process.env.MONGO_URL 19 | 20 | // URL 21 | export const LANDING_URL = process.env.LANDING_URL 22 | export const WEB_URL = process.env.WEB_URL 23 | export const API_URL = process.env.API_URL 24 | 25 | // Email 26 | export const EMAIL_ON = process.env.EMAIL_ON 27 | export const EMAIL_TEST = process.env.EMAIL_TEST 28 | export const EMAIL_HOST = process.env.EMAIL_HOST 29 | export const EMAIL_USER = process.env.EMAIL_USER 30 | export const EMAIL_PASSWORD = process.env.EMAIL_PASSWORD 31 | -------------------------------------------------------------------------------- /backend/api/src/setup/config/params.json: -------------------------------------------------------------------------------- 1 | { 2 | "site": { 3 | "version": 0.1, 4 | "name": "Example", 5 | "domain": "example.com", 6 | 7 | "emails": { 8 | "help": { 9 | "name": "example", 10 | "email": "help@example.com" 11 | } 12 | } 13 | }, 14 | 15 | "endpoint": { 16 | "url": "/", 17 | "uploads": "/uploads" 18 | }, 19 | 20 | "folder": { 21 | "uploads": "uploads" 22 | }, 23 | 24 | "common": { 25 | "language": { 26 | "default": "en-US" 27 | } 28 | }, 29 | 30 | "user": { 31 | "roles": { 32 | "admin": { 33 | "key": "admin", 34 | "title": "Admin" 35 | }, 36 | 37 | "user": { 38 | "key": "user", 39 | "title": "User" 40 | } 41 | }, 42 | 43 | "rules": { 44 | "nameMinLength": 3, 45 | "passwordMinLength": 6 46 | }, 47 | 48 | "image": { 49 | "folder": "/images/user", 50 | "default": "default.jpg", 51 | "width": 300, 52 | "height": 300 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /backend/api/src/setup/helpers/utils.js: -------------------------------------------------------------------------------- 1 | // App Imports 2 | import params from '../config/params' 3 | 4 | // Utility functions 5 | 6 | // Generate random number 7 | export function randomNumber(low, high) { 8 | return Math.floor(Math.random() * (high - low) + low) 9 | } 10 | 11 | // Auth check user 12 | export function authCheck(auth) { 13 | return auth && auth.user && auth.user._id 14 | } 15 | 16 | // Auth check Admin 17 | export function authCheckAdmin(auth) { 18 | return authCheck(auth) && auth.user.role === params.user.roles.admin.key 19 | } 20 | 21 | // No operation 22 | export const noop = () => {} 23 | -------------------------------------------------------------------------------- /backend/api/src/setup/helpers/validation.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import Validator from 'fullstack-validator' 3 | 4 | const v = new Validator(/* custom rules */) 5 | 6 | export default v 7 | -------------------------------------------------------------------------------- /backend/api/src/setup/server/authentication.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import jwt from 'jsonwebtoken' 3 | 4 | // App Imports 5 | import { SECURITY_SECRET } from '../config/env' 6 | import User from '../../modules/user/model' 7 | 8 | // Authentication middleware 9 | export default async function (request, response, next) { 10 | let header = request.headers.authentication 11 | 12 | if (header && header !== null) { 13 | try { 14 | const token = header.split(' ') 15 | const userToken = jwt.verify(token[1], SECURITY_SECRET) 16 | let user = await User.findOne({ _id: userToken.id }) 17 | 18 | if (user) { 19 | request.auth = { 20 | isAuthenticated: true, 21 | user, 22 | } 23 | } 24 | } catch (e) { 25 | console.warn('Invalid token detected.') 26 | } 27 | } else { 28 | request.auth = { 29 | isAuthenticated: false, 30 | user: null, 31 | } 32 | } 33 | 34 | next() 35 | } 36 | -------------------------------------------------------------------------------- /backend/api/src/setup/server/database.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import mongoose from 'mongoose' 3 | 4 | // App Imports 5 | import { MONGO_URL } from '../config/env' 6 | 7 | // Connect database 8 | export default async function () { 9 | console.info('SETUP - Connecting database..') 10 | 11 | await connectWithRetry() 12 | } 13 | 14 | // Handle connection error 15 | mongoose.connection.on('error', (error) => { 16 | console.log(`ERROR - Connection failed: ${error.message}`) 17 | 18 | setTimeout(async () => { 19 | console.log('SETUP - Connecting database.. retrying..') 20 | 21 | await connectWithRetry() 22 | }, 5000) 23 | }) 24 | 25 | // Retry connection 26 | const connectWithRetry = async () => { 27 | return await mongoose.connect(MONGO_URL, { 28 | useNewUrlParser: true, 29 | useCreateIndex: true, 30 | useFindAndModify: false, 31 | useUnifiedTopology: true, 32 | }) 33 | } 34 | -------------------------------------------------------------------------------- /backend/api/src/setup/server/endpoint.js: -------------------------------------------------------------------------------- 1 | // App Imports 2 | import { NODE_ENV } from '../config/env' 3 | import params from '../config/params' 4 | import translate from '../translate' 5 | import authentication from './authentication' 6 | import language from './language' 7 | import modules from './modules' 8 | 9 | // Setup endpoint 10 | export default function (server) { 11 | console.info('SETUP - Endpoint..') 12 | 13 | // API endpoint 14 | server.all( 15 | params.endpoint.url, 16 | [authentication, language], 17 | async (request, response) => { 18 | let result = { 19 | success: false, 20 | message: 'Please try again.', 21 | data: null, 22 | } 23 | 24 | // Check if operation to be called is set 25 | if (request.body.operation) { 26 | try { 27 | // Set locale 28 | translate.locale = request.language 29 | 30 | // Execute operation 31 | // operationName({ params, fields, auth }) 32 | const { 33 | data, 34 | message = translate.t('common.messages.success.default'), 35 | } = await modules[request.body.operation]({ 36 | params: request.body.params || {}, 37 | fields: request.body.fields || {}, 38 | auth: request.auth, 39 | translate, 40 | }) 41 | 42 | // Operation executed successfully 43 | result.success = true 44 | result.data = data 45 | result.message = message 46 | } catch (error) { 47 | result.message = 48 | modules[request.body.operation] === undefined 49 | ? `${request.body.operation} operation is not available.` 50 | : error.message 51 | } 52 | } 53 | 54 | // Log info in development mode 55 | if (NODE_ENV === 'development') { 56 | console.log(request.body) 57 | console.log(result.success, result.message) 58 | } 59 | 60 | // Send response 61 | response.send(result) 62 | }, 63 | ) 64 | } 65 | -------------------------------------------------------------------------------- /backend/api/src/setup/server/language.js: -------------------------------------------------------------------------------- 1 | // App Imports 2 | import params from '../../setup/config/params' 3 | 4 | // Language middleware 5 | export default async function (request, response, next) { 6 | let header = request.headers.language 7 | 8 | if (header && header !== null) { 9 | request.language = header 10 | } else { 11 | request.language = params.common.language.default 12 | } 13 | 14 | next() 15 | } 16 | -------------------------------------------------------------------------------- /backend/api/src/setup/server/middlewares.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import express from 'express' 3 | import path from 'path' 4 | import cors from 'cors' 5 | import bodyParser from 'body-parser' 6 | import morgan from 'morgan' 7 | 8 | // App Imports 9 | import { NODE_ENV, WEB_URL } from '../config/env' 10 | 11 | // Setup middlewares 12 | export default function (server) { 13 | console.info('SETUP - Middlewares..') 14 | 15 | // Enable CORS 16 | server.use( 17 | cors({ 18 | origin: WEB_URL, 19 | }), 20 | ) 21 | 22 | // Request body parser 23 | server.use(bodyParser.json()) 24 | server.use(bodyParser.urlencoded({ extended: false })) 25 | 26 | // Static files folder 27 | server.use(express.static(path.join(__dirname, '..', '..', '..', 'public'))) 28 | 29 | // HTTP logger 30 | if (NODE_ENV === 'development') { 31 | server.use(morgan('tiny')) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /backend/api/src/setup/server/modules.js: -------------------------------------------------------------------------------- 1 | // App Imports 2 | import user from '../../modules/user' 3 | import note from '../../modules/note' 4 | 5 | // Modules 6 | export default { 7 | ...user, 8 | ...note, 9 | } 10 | -------------------------------------------------------------------------------- /backend/api/src/setup/server/seeder.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import mongoose from 'mongoose' 3 | 4 | // App Imports 5 | import { NODE_ENV } from '../config/env' 6 | import database from '../server/database' 7 | import user from '../../modules/user/seed' 8 | import note from '../../modules/note/seed' 9 | 10 | // Seeder 11 | async function seeder() { 12 | console.log('SEED - Started') 13 | 14 | await database() 15 | 16 | // Clear database, only in development, do not do in production. I repeat, do not do it in production or you will be featured on www.commitstrip.com! 17 | if (NODE_ENV === 'development') { 18 | // @temp allow reset database 19 | console.log('SEED - Dropping database.. ❗') 20 | 21 | await mongoose.connection.dropDatabase() 22 | } 23 | 24 | // Seeds 25 | await user() 26 | await note() 27 | 28 | // Close connection 29 | mongoose.connection.close() 30 | 31 | console.log('SEED - Complete. ✅') 32 | } 33 | 34 | // Run seeder 35 | seeder() 36 | -------------------------------------------------------------------------------- /backend/api/src/setup/server/start.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import ip from 'ip' 3 | import mongoose from 'mongoose' 4 | import moment from 'moment' 5 | 6 | // App Imports 7 | import { PORT, NODE_ENV } from '../config/env' 8 | 9 | // Start server 10 | export default function (server) { 11 | console.info('SETUP - Starting server..') 12 | 13 | // Start Server 14 | const serverProcess = server.listen(PORT, (error) => { 15 | if (error) { 16 | console.error('ERROR - Unable to start server.') 17 | } else { 18 | console.info(`INFO - Server started on`) 19 | console.info(` Local http://localhost:${PORT} [${NODE_ENV}]`) 20 | console.info(` Network http://${ip.address()}:${PORT} [${NODE_ENV}]`) 21 | console.info(` Datetime ${moment().format('YYYY-MM-DD hh:mm:ss a')}\n`) 22 | } 23 | }) 24 | 25 | // Stop Server 26 | for (let signal of ['SIGINT', 'SIGTERM']) { 27 | process.on(signal, function () { 28 | console.info('INFO - Shutting down server..') 29 | 30 | serverProcess.close(function () { 31 | console.info('INFO - Server has been shut down.') 32 | 33 | mongoose.connection.close(false, () => { 34 | console.info('INFO - Database disconnected.') 35 | 36 | process.exit(0) 37 | }) 38 | }) 39 | }) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /backend/api/src/setup/translate/index.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import i18n from 'i18n-js' 3 | 4 | // Languages 5 | import en from './en.json' 6 | // import es from './es.json' 7 | 8 | // Translate 9 | i18n.fallbacks = en 10 | i18n.translations = { en /* es */ } 11 | 12 | export default i18n 13 | -------------------------------------------------------------------------------- /backend/api/uploads/.gitkeep: -------------------------------------------------------------------------------- 1 | Uploaded files are are stored in this folder `/api/uploads` temporarily. 2 | -------------------------------------------------------------------------------- /backend/database/.dockerignore: -------------------------------------------------------------------------------- 1 | data 2 | -------------------------------------------------------------------------------- /backend/database/.gitignore: -------------------------------------------------------------------------------- 1 | data/* 2 | !data/.gitkeep 3 | -------------------------------------------------------------------------------- /backend/database/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mongo 2 | 3 | EXPOSE 27017 4 | -------------------------------------------------------------------------------- /backend/database/data/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atulmy/fullstack-javascript-architecture/8f4fa626bb9d3fe8210ad601698ac166f15f2363/backend/database/data/.gitkeep -------------------------------------------------------------------------------- /backend/proxy/nginx.conf: -------------------------------------------------------------------------------- 1 | server { 2 | server_name example.com; 3 | 4 | listen 80; 5 | listen [::]:80; 6 | 7 | location / { 8 | proxy_pass http://landing:3000; 9 | } 10 | } 11 | 12 | server { 13 | server_name app.example.com; 14 | 15 | listen 80; 16 | listen [::]:80; 17 | 18 | location / { 19 | proxy_pass http://web:5000; 20 | } 21 | } 22 | 23 | server { 24 | server_name api.example.com; 25 | 26 | listen 80; 27 | listen [::]:80; 28 | 29 | location / { 30 | proxy_pass http://api:8000; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /deployment/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | # Backend / Database 5 | database: 6 | image: database 7 | build: ../backend/database 8 | volumes: 9 | - ../backend/database/data:/data/db 10 | restart: always 11 | networks: 12 | - back 13 | 14 | # Backend / API 15 | api: 16 | image: api 17 | build: ../backend/api 18 | volumes: 19 | - ../backend/api/public:/user/src/app/public 20 | restart: always 21 | depends_on: 22 | - database 23 | networks: 24 | - front 25 | - back 26 | 27 | # Backend / Proxy 28 | proxy: 29 | image: nginx 30 | volumes: 31 | - ../backend/proxy/nginx.conf:/etc/nginx/conf.d/proxy.conf 32 | restart: always 33 | ports: 34 | - "80:80" 35 | - "443:443" 36 | depends_on: 37 | - database 38 | - api 39 | networks: 40 | - front 41 | - docker-network 42 | 43 | # Frontend / App / Web 44 | web: 45 | image: web 46 | build: ../frontend/app/web 47 | restart: always 48 | depends_on: 49 | - api 50 | networks: 51 | - front 52 | 53 | # Frontend / Landing 54 | landing: 55 | image: landing 56 | build: ../frontend/landing 57 | networks: 58 | - front 59 | 60 | networks: 61 | front: 62 | driver: bridge 63 | back: 64 | driver: bridge 65 | docker-network: 66 | driver: bridge 67 | -------------------------------------------------------------------------------- /frontend/app/mobile/.buckconfig: -------------------------------------------------------------------------------- 1 | 2 | [android] 3 | target = Google Inc.:Google APIs:23 4 | 5 | [maven_repositories] 6 | central = https://repo1.maven.org/maven2 7 | -------------------------------------------------------------------------------- /frontend/app/mobile/.gitattributes: -------------------------------------------------------------------------------- 1 | *.pbxproj -text 2 | -------------------------------------------------------------------------------- /frontend/app/mobile/.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # Xcode 6 | # 7 | build/ 8 | *.pbxuser 9 | !default.pbxuser 10 | *.mode1v3 11 | !default.mode1v3 12 | *.mode2v3 13 | !default.mode2v3 14 | *.perspectivev3 15 | !default.perspectivev3 16 | xcuserdata 17 | *.xccheckout 18 | *.moved-aside 19 | DerivedData 20 | *.hmap 21 | *.ipa 22 | *.xcuserstate 23 | 24 | # Android/IntelliJ 25 | # 26 | build/ 27 | .idea 28 | .gradle 29 | local.properties 30 | *.iml 31 | 32 | # node.js 33 | # 34 | node_modules/ 35 | npm-debug.log 36 | yarn-error.log 37 | 38 | # BUCK 39 | buck-out/ 40 | \.buckd/ 41 | *.keystore 42 | !debug.keystore 43 | 44 | # fastlane 45 | # 46 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 47 | # screenshots whenever they are needed. 48 | # For more information about the recommended setup visit: 49 | # https://docs.fastlane.tools/best-practices/source-control/ 50 | 51 | */fastlane/report.xml 52 | */fastlane/Preview.html 53 | */fastlane/screenshots 54 | 55 | # Bundle artifact 56 | *.jsbundle 57 | 58 | # CocoaPods 59 | /ios/Pods/ 60 | -------------------------------------------------------------------------------- /frontend/app/mobile/.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | bracketSpacing: false, 3 | jsxBracketSameLine: true, 4 | singleQuote: true, 5 | trailingComma: 'all', 6 | semi: false, 7 | tabWidth: 2, 8 | useTabs: false, 9 | arrowParens: 'always', 10 | jsxSingleQuote: true, 11 | } 12 | -------------------------------------------------------------------------------- /frontend/app/mobile/.watchmanconfig: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /frontend/app/mobile/android/app/BUCK: -------------------------------------------------------------------------------- 1 | # To learn about Buck see [Docs](https://buckbuild.com/). 2 | # To run your application with Buck: 3 | # - install Buck 4 | # - `npm start` - to start the packager 5 | # - `cd android` 6 | # - `keytool -genkey -v -keystore keystores/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US"` 7 | # - `./gradlew :app:copyDownloadableDepsToLibs` - make all Gradle compile dependencies available to Buck 8 | # - `buck install -r android/app` - compile, install and run application 9 | # 10 | 11 | load(":build_defs.bzl", "create_aar_targets", "create_jar_targets") 12 | 13 | lib_deps = [] 14 | 15 | create_aar_targets(glob(["libs/*.aar"])) 16 | 17 | create_jar_targets(glob(["libs/*.jar"])) 18 | 19 | android_library( 20 | name = "all-libs", 21 | exported_deps = lib_deps, 22 | ) 23 | 24 | android_library( 25 | name = "app-code", 26 | srcs = glob([ 27 | "src/main/java/**/*.java", 28 | ]), 29 | deps = [ 30 | ":all-libs", 31 | ":build_config", 32 | ":res", 33 | ], 34 | ) 35 | 36 | android_build_config( 37 | name = "build_config", 38 | package = "com.example", 39 | ) 40 | 41 | android_resource( 42 | name = "res", 43 | package = "com.example", 44 | res = "src/main/res", 45 | ) 46 | 47 | android_binary( 48 | name = "app", 49 | keystore = "//android/keystores:debug", 50 | manifest = "src/main/AndroidManifest.xml", 51 | package_type = "debug", 52 | deps = [ 53 | ":app-code", 54 | ], 55 | ) 56 | -------------------------------------------------------------------------------- /frontend/app/mobile/android/app/build_defs.bzl: -------------------------------------------------------------------------------- 1 | """Helper definitions to glob .aar and .jar targets""" 2 | 3 | def create_aar_targets(aarfiles): 4 | for aarfile in aarfiles: 5 | name = "aars__" + aarfile[aarfile.rindex("/") + 1:aarfile.rindex(".aar")] 6 | lib_deps.append(":" + name) 7 | android_prebuilt_aar( 8 | name = name, 9 | aar = aarfile, 10 | ) 11 | 12 | def create_jar_targets(jarfiles): 13 | for jarfile in jarfiles: 14 | name = "jars__" + jarfile[jarfile.rindex("/") + 1:jarfile.rindex(".jar")] 15 | lib_deps.append(":" + name) 16 | prebuilt_jar( 17 | name = name, 18 | binary_jar = jarfile, 19 | ) 20 | -------------------------------------------------------------------------------- /frontend/app/mobile/android/app/debug.keystore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atulmy/fullstack-javascript-architecture/8f4fa626bb9d3fe8210ad601698ac166f15f2363/frontend/app/mobile/android/app/debug.keystore -------------------------------------------------------------------------------- /frontend/app/mobile/android/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | -------------------------------------------------------------------------------- /frontend/app/mobile/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /frontend/app/mobile/android/app/src/debug/ReactNativeFlipper.java: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atulmy/fullstack-javascript-architecture/8f4fa626bb9d3fe8210ad601698ac166f15f2363/frontend/app/mobile/android/app/src/debug/ReactNativeFlipper.java -------------------------------------------------------------------------------- /frontend/app/mobile/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 17 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /frontend/app/mobile/android/app/src/main/assets/fonts/AntDesign.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atulmy/fullstack-javascript-architecture/8f4fa626bb9d3fe8210ad601698ac166f15f2363/frontend/app/mobile/android/app/src/main/assets/fonts/AntDesign.ttf -------------------------------------------------------------------------------- /frontend/app/mobile/android/app/src/main/assets/fonts/Entypo.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atulmy/fullstack-javascript-architecture/8f4fa626bb9d3fe8210ad601698ac166f15f2363/frontend/app/mobile/android/app/src/main/assets/fonts/Entypo.ttf -------------------------------------------------------------------------------- /frontend/app/mobile/android/app/src/main/assets/fonts/EvilIcons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atulmy/fullstack-javascript-architecture/8f4fa626bb9d3fe8210ad601698ac166f15f2363/frontend/app/mobile/android/app/src/main/assets/fonts/EvilIcons.ttf -------------------------------------------------------------------------------- /frontend/app/mobile/android/app/src/main/assets/fonts/Feather.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atulmy/fullstack-javascript-architecture/8f4fa626bb9d3fe8210ad601698ac166f15f2363/frontend/app/mobile/android/app/src/main/assets/fonts/Feather.ttf -------------------------------------------------------------------------------- /frontend/app/mobile/android/app/src/main/assets/fonts/FontAwesome.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atulmy/fullstack-javascript-architecture/8f4fa626bb9d3fe8210ad601698ac166f15f2363/frontend/app/mobile/android/app/src/main/assets/fonts/FontAwesome.ttf -------------------------------------------------------------------------------- /frontend/app/mobile/android/app/src/main/assets/fonts/FontAwesome5_Brands.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atulmy/fullstack-javascript-architecture/8f4fa626bb9d3fe8210ad601698ac166f15f2363/frontend/app/mobile/android/app/src/main/assets/fonts/FontAwesome5_Brands.ttf -------------------------------------------------------------------------------- /frontend/app/mobile/android/app/src/main/assets/fonts/FontAwesome5_Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atulmy/fullstack-javascript-architecture/8f4fa626bb9d3fe8210ad601698ac166f15f2363/frontend/app/mobile/android/app/src/main/assets/fonts/FontAwesome5_Regular.ttf -------------------------------------------------------------------------------- /frontend/app/mobile/android/app/src/main/assets/fonts/FontAwesome5_Solid.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atulmy/fullstack-javascript-architecture/8f4fa626bb9d3fe8210ad601698ac166f15f2363/frontend/app/mobile/android/app/src/main/assets/fonts/FontAwesome5_Solid.ttf -------------------------------------------------------------------------------- /frontend/app/mobile/android/app/src/main/assets/fonts/Fontisto.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atulmy/fullstack-javascript-architecture/8f4fa626bb9d3fe8210ad601698ac166f15f2363/frontend/app/mobile/android/app/src/main/assets/fonts/Fontisto.ttf -------------------------------------------------------------------------------- /frontend/app/mobile/android/app/src/main/assets/fonts/Foundation.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atulmy/fullstack-javascript-architecture/8f4fa626bb9d3fe8210ad601698ac166f15f2363/frontend/app/mobile/android/app/src/main/assets/fonts/Foundation.ttf -------------------------------------------------------------------------------- /frontend/app/mobile/android/app/src/main/assets/fonts/Ionicons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atulmy/fullstack-javascript-architecture/8f4fa626bb9d3fe8210ad601698ac166f15f2363/frontend/app/mobile/android/app/src/main/assets/fonts/Ionicons.ttf -------------------------------------------------------------------------------- /frontend/app/mobile/android/app/src/main/assets/fonts/MaterialCommunityIcons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atulmy/fullstack-javascript-architecture/8f4fa626bb9d3fe8210ad601698ac166f15f2363/frontend/app/mobile/android/app/src/main/assets/fonts/MaterialCommunityIcons.ttf -------------------------------------------------------------------------------- /frontend/app/mobile/android/app/src/main/assets/fonts/MaterialIcons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atulmy/fullstack-javascript-architecture/8f4fa626bb9d3fe8210ad601698ac166f15f2363/frontend/app/mobile/android/app/src/main/assets/fonts/MaterialIcons.ttf -------------------------------------------------------------------------------- /frontend/app/mobile/android/app/src/main/assets/fonts/Octicons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atulmy/fullstack-javascript-architecture/8f4fa626bb9d3fe8210ad601698ac166f15f2363/frontend/app/mobile/android/app/src/main/assets/fonts/Octicons.ttf -------------------------------------------------------------------------------- /frontend/app/mobile/android/app/src/main/assets/fonts/SimpleLineIcons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atulmy/fullstack-javascript-architecture/8f4fa626bb9d3fe8210ad601698ac166f15f2363/frontend/app/mobile/android/app/src/main/assets/fonts/SimpleLineIcons.ttf -------------------------------------------------------------------------------- /frontend/app/mobile/android/app/src/main/assets/fonts/Zocial.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atulmy/fullstack-javascript-architecture/8f4fa626bb9d3fe8210ad601698ac166f15f2363/frontend/app/mobile/android/app/src/main/assets/fonts/Zocial.ttf -------------------------------------------------------------------------------- /frontend/app/mobile/android/app/src/main/java/com/example/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.example; 2 | 3 | import com.facebook.react.ReactActivity; 4 | 5 | public class MainActivity extends ReactActivity { 6 | 7 | /** 8 | * Returns the name of the main component registered from JavaScript. This is used to schedule 9 | * rendering of the component. 10 | */ 11 | @Override 12 | protected String getMainComponentName() { 13 | return "example"; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /frontend/app/mobile/android/app/src/main/res/drawable-hdpi/screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atulmy/fullstack-javascript-architecture/8f4fa626bb9d3fe8210ad601698ac166f15f2363/frontend/app/mobile/android/app/src/main/res/drawable-hdpi/screen.png -------------------------------------------------------------------------------- /frontend/app/mobile/android/app/src/main/res/drawable-ldpi/screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atulmy/fullstack-javascript-architecture/8f4fa626bb9d3fe8210ad601698ac166f15f2363/frontend/app/mobile/android/app/src/main/res/drawable-ldpi/screen.png -------------------------------------------------------------------------------- /frontend/app/mobile/android/app/src/main/res/drawable-mdpi/screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atulmy/fullstack-javascript-architecture/8f4fa626bb9d3fe8210ad601698ac166f15f2363/frontend/app/mobile/android/app/src/main/res/drawable-mdpi/screen.png -------------------------------------------------------------------------------- /frontend/app/mobile/android/app/src/main/res/drawable-xhdpi/screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atulmy/fullstack-javascript-architecture/8f4fa626bb9d3fe8210ad601698ac166f15f2363/frontend/app/mobile/android/app/src/main/res/drawable-xhdpi/screen.png -------------------------------------------------------------------------------- /frontend/app/mobile/android/app/src/main/res/drawable-xxhdpi/screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atulmy/fullstack-javascript-architecture/8f4fa626bb9d3fe8210ad601698ac166f15f2363/frontend/app/mobile/android/app/src/main/res/drawable-xxhdpi/screen.png -------------------------------------------------------------------------------- /frontend/app/mobile/android/app/src/main/res/drawable-xxxhdpi/screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atulmy/fullstack-javascript-architecture/8f4fa626bb9d3fe8210ad601698ac166f15f2363/frontend/app/mobile/android/app/src/main/res/drawable-xxxhdpi/screen.png -------------------------------------------------------------------------------- /frontend/app/mobile/android/app/src/main/res/drawable/screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atulmy/fullstack-javascript-architecture/8f4fa626bb9d3fe8210ad601698ac166f15f2363/frontend/app/mobile/android/app/src/main/res/drawable/screen.png -------------------------------------------------------------------------------- /frontend/app/mobile/android/app/src/main/res/drawable/splash.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | -------------------------------------------------------------------------------- /frontend/app/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atulmy/fullstack-javascript-architecture/8f4fa626bb9d3fe8210ad601698ac166f15f2363/frontend/app/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /frontend/app/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atulmy/fullstack-javascript-architecture/8f4fa626bb9d3fe8210ad601698ac166f15f2363/frontend/app/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /frontend/app/mobile/android/app/src/main/res/mipmap-ldpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atulmy/fullstack-javascript-architecture/8f4fa626bb9d3fe8210ad601698ac166f15f2363/frontend/app/mobile/android/app/src/main/res/mipmap-ldpi/ic_launcher.png -------------------------------------------------------------------------------- /frontend/app/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atulmy/fullstack-javascript-architecture/8f4fa626bb9d3fe8210ad601698ac166f15f2363/frontend/app/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /frontend/app/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atulmy/fullstack-javascript-architecture/8f4fa626bb9d3fe8210ad601698ac166f15f2363/frontend/app/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /frontend/app/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atulmy/fullstack-javascript-architecture/8f4fa626bb9d3fe8210ad601698ac166f15f2363/frontend/app/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /frontend/app/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atulmy/fullstack-javascript-architecture/8f4fa626bb9d3fe8210ad601698ac166f15f2363/frontend/app/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /frontend/app/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atulmy/fullstack-javascript-architecture/8f4fa626bb9d3fe8210ad601698ac166f15f2363/frontend/app/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /frontend/app/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atulmy/fullstack-javascript-architecture/8f4fa626bb9d3fe8210ad601698ac166f15f2363/frontend/app/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /frontend/app/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atulmy/fullstack-javascript-architecture/8f4fa626bb9d3fe8210ad601698ac166f15f2363/frontend/app/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /frontend/app/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atulmy/fullstack-javascript-architecture/8f4fa626bb9d3fe8210ad601698ac166f15f2363/frontend/app/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /frontend/app/mobile/android/app/src/main/res/playstore-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atulmy/fullstack-javascript-architecture/8f4fa626bb9d3fe8210ad601698ac166f15f2363/frontend/app/mobile/android/app/src/main/res/playstore-icon.png -------------------------------------------------------------------------------- /frontend/app/mobile/android/app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | #ffffff 3 | 4 | -------------------------------------------------------------------------------- /frontend/app/mobile/android/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Example 3 | 4 | -------------------------------------------------------------------------------- /frontend/app/mobile/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /frontend/app/mobile/android/build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | ext { 5 | buildToolsVersion = "29.0.2" 6 | minSdkVersion = 16 7 | compileSdkVersion = 29 8 | targetSdkVersion = 29 9 | } 10 | repositories { 11 | google() 12 | jcenter() 13 | } 14 | dependencies { 15 | classpath("com.android.tools.build:gradle:3.5.3") 16 | // NOTE: Do not place your application dependencies here; they belong 17 | // in the individual module build.gradle files 18 | } 19 | } 20 | 21 | allprojects { 22 | repositories { 23 | mavenLocal() 24 | maven { 25 | // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm 26 | url("$rootDir/../node_modules/react-native/android") 27 | } 28 | maven { 29 | // Android JSC is installed from npm 30 | url("$rootDir/../node_modules/jsc-android/dist") 31 | } 32 | 33 | google() 34 | jcenter() 35 | maven { url 'https://www.jitpack.io' } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /frontend/app/mobile/android/gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true 19 | 20 | # AndroidX package structure to make it clearer which packages are bundled with the 21 | # Android operating system, and which are packaged with your app's APK 22 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 23 | android.useAndroidX=true 24 | # Automatically convert third-party libraries to use AndroidX 25 | android.enableJetifier=true 26 | 27 | # Version of flipper SDK to use with React Native 28 | FLIPPER_VERSION=0.54.0 29 | -------------------------------------------------------------------------------- /frontend/app/mobile/android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atulmy/fullstack-javascript-architecture/8f4fa626bb9d3fe8210ad601698ac166f15f2363/frontend/app/mobile/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /frontend/app/mobile/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.2-all.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /frontend/app/mobile/android/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'example' 2 | apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings) 3 | include ':app' 4 | -------------------------------------------------------------------------------- /frontend/app/mobile/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "displayName": "example" 4 | } -------------------------------------------------------------------------------- /frontend/app/mobile/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atulmy/fullstack-javascript-architecture/8f4fa626bb9d3fe8210ad601698ac166f15f2363/frontend/app/mobile/assets/logo.png -------------------------------------------------------------------------------- /frontend/app/mobile/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ['module:metro-react-native-babel-preset'], 3 | }; 4 | -------------------------------------------------------------------------------- /frontend/app/mobile/index.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import { AppRegistry } from 'react-native' 3 | 4 | // App Imports 5 | import App from './src/setup' 6 | import { name as appName } from './app.json' 7 | 8 | // Bootstrap 9 | AppRegistry.registerComponent(appName, () => App) 10 | -------------------------------------------------------------------------------- /frontend/app/mobile/ios/File.swift: -------------------------------------------------------------------------------- 1 | // 2 | // File.swift 3 | // example 4 | // 5 | // Created by Atul Yadav on 18/04/20. 6 | // Copyright © 2020 Example. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | -------------------------------------------------------------------------------- /frontend/app/mobile/ios/Podfile: -------------------------------------------------------------------------------- 1 | require_relative '../node_modules/react-native/scripts/react_native_pods' 2 | require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' 3 | 4 | platform :ios, '10.0' 5 | 6 | target 'example' do 7 | config = use_native_modules! 8 | use_react_native!(:path => config["reactNativePath"]) 9 | 10 | target 'exampleTests' do 11 | inherit! :complete 12 | # Pods for testing 13 | end 14 | 15 | # Enables Flipper. 16 | # 17 | # Note that if you have use_frameworks! enabled, Flipper will not work and 18 | # you should disable these next few lines. 19 | use_flipper! 20 | post_install do |installer| 21 | flipper_post_install(installer) 22 | end 23 | end 24 | 25 | target 'example-tvOS' do 26 | # Pods for example-tvOS 27 | 28 | target 'example-tvOSTests' do 29 | inherit! :search_paths 30 | # Pods for testing 31 | end 32 | 33 | end 34 | -------------------------------------------------------------------------------- /frontend/app/mobile/ios/example-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // Use this file to import your target's public headers that you would like to expose to Swift. 3 | // 4 | 5 | -------------------------------------------------------------------------------- /frontend/app/mobile/ios/example-tvOS/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | NSAppTransportSecurity 26 | 27 | NSExceptionDomains 28 | 29 | localhost 30 | 31 | NSExceptionAllowsInsecureHTTPLoads 32 | 33 | 34 | 35 | 36 | NSLocationWhenInUseUsageDescription 37 | 38 | UILaunchStoryboardName 39 | LaunchScreen 40 | UIRequiredDeviceCapabilities 41 | 42 | armv7 43 | 44 | UISupportedInterfaceOrientations 45 | 46 | UIInterfaceOrientationPortrait 47 | UIInterfaceOrientationLandscapeLeft 48 | UIInterfaceOrientationLandscapeRight 49 | 50 | UIViewControllerBasedStatusBarAppearance 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /frontend/app/mobile/ios/example-tvOSTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /frontend/app/mobile/ios/example.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /frontend/app/mobile/ios/example.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /frontend/app/mobile/ios/example.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /frontend/app/mobile/ios/example.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /frontend/app/mobile/ios/example/AppDelegate.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | @interface AppDelegate : UIResponder 5 | 6 | @property (nonatomic, strong) UIWindow *window; 7 | 8 | @end 9 | -------------------------------------------------------------------------------- /frontend/app/mobile/ios/example/Images.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atulmy/fullstack-javascript-architecture/8f4fa626bb9d3fe8210ad601698ac166f15f2363/frontend/app/mobile/ios/example/Images.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /frontend/app/mobile/ios/example/Images.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atulmy/fullstack-javascript-architecture/8f4fa626bb9d3fe8210ad601698ac166f15f2363/frontend/app/mobile/ios/example/Images.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /frontend/app/mobile/ios/example/Images.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atulmy/fullstack-javascript-architecture/8f4fa626bb9d3fe8210ad601698ac166f15f2363/frontend/app/mobile/ios/example/Images.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /frontend/app/mobile/ios/example/Images.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atulmy/fullstack-javascript-architecture/8f4fa626bb9d3fe8210ad601698ac166f15f2363/frontend/app/mobile/ios/example/Images.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /frontend/app/mobile/ios/example/Images.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atulmy/fullstack-javascript-architecture/8f4fa626bb9d3fe8210ad601698ac166f15f2363/frontend/app/mobile/ios/example/Images.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /frontend/app/mobile/ios/example/Images.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atulmy/fullstack-javascript-architecture/8f4fa626bb9d3fe8210ad601698ac166f15f2363/frontend/app/mobile/ios/example/Images.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /frontend/app/mobile/ios/example/Images.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atulmy/fullstack-javascript-architecture/8f4fa626bb9d3fe8210ad601698ac166f15f2363/frontend/app/mobile/ios/example/Images.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /frontend/app/mobile/ios/example/Images.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atulmy/fullstack-javascript-architecture/8f4fa626bb9d3fe8210ad601698ac166f15f2363/frontend/app/mobile/ios/example/Images.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /frontend/app/mobile/ios/example/Images.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atulmy/fullstack-javascript-architecture/8f4fa626bb9d3fe8210ad601698ac166f15f2363/frontend/app/mobile/ios/example/Images.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /frontend/app/mobile/ios/example/Images.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atulmy/fullstack-javascript-architecture/8f4fa626bb9d3fe8210ad601698ac166f15f2363/frontend/app/mobile/ios/example/Images.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /frontend/app/mobile/ios/example/Images.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atulmy/fullstack-javascript-architecture/8f4fa626bb9d3fe8210ad601698ac166f15f2363/frontend/app/mobile/ios/example/Images.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /frontend/app/mobile/ios/example/Images.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atulmy/fullstack-javascript-architecture/8f4fa626bb9d3fe8210ad601698ac166f15f2363/frontend/app/mobile/ios/example/Images.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /frontend/app/mobile/ios/example/Images.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atulmy/fullstack-javascript-architecture/8f4fa626bb9d3fe8210ad601698ac166f15f2363/frontend/app/mobile/ios/example/Images.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /frontend/app/mobile/ios/example/Images.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atulmy/fullstack-javascript-architecture/8f4fa626bb9d3fe8210ad601698ac166f15f2363/frontend/app/mobile/ios/example/Images.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /frontend/app/mobile/ios/example/Images.xcassets/AppIcon.appiconset/ItunesArtwork@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atulmy/fullstack-javascript-architecture/8f4fa626bb9d3fe8210ad601698ac166f15f2363/frontend/app/mobile/ios/example/Images.xcassets/AppIcon.appiconset/ItunesArtwork@2x.png -------------------------------------------------------------------------------- /frontend/app/mobile/ios/example/Images.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /frontend/app/mobile/ios/example/Images.xcassets/iTunesArtwork@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atulmy/fullstack-javascript-architecture/8f4fa626bb9d3fe8210ad601698ac166f15f2363/frontend/app/mobile/ios/example/Images.xcassets/iTunesArtwork@1x.png -------------------------------------------------------------------------------- /frontend/app/mobile/ios/example/Images.xcassets/iTunesArtwork@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atulmy/fullstack-javascript-architecture/8f4fa626bb9d3fe8210ad601698ac166f15f2363/frontend/app/mobile/ios/example/Images.xcassets/iTunesArtwork@2x.png -------------------------------------------------------------------------------- /frontend/app/mobile/ios/example/Images.xcassets/iTunesArtwork@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atulmy/fullstack-javascript-architecture/8f4fa626bb9d3fe8210ad601698ac166f15f2363/frontend/app/mobile/ios/example/Images.xcassets/iTunesArtwork@3x.png -------------------------------------------------------------------------------- /frontend/app/mobile/ios/example/main.m: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #import "AppDelegate.h" 4 | 5 | int main(int argc, char * argv[]) { 6 | @autoreleasepool { 7 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /frontend/app/mobile/ios/exampleTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /frontend/app/mobile/ios/exampleTests/exampleTests.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | #import 5 | #import 6 | 7 | #define TIMEOUT_SECONDS 600 8 | #define TEXT_TO_LOOK_FOR @"Welcome to React" 9 | 10 | @interface exampleTests : XCTestCase 11 | 12 | @end 13 | 14 | @implementation exampleTests 15 | 16 | - (BOOL)findSubviewInView:(UIView *)view matching:(BOOL(^)(UIView *view))test 17 | { 18 | if (test(view)) { 19 | return YES; 20 | } 21 | for (UIView *subview in [view subviews]) { 22 | if ([self findSubviewInView:subview matching:test]) { 23 | return YES; 24 | } 25 | } 26 | return NO; 27 | } 28 | 29 | - (void)testRendersWelcomeScreen 30 | { 31 | UIViewController *vc = [[[RCTSharedApplication() delegate] window] rootViewController]; 32 | NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS]; 33 | BOOL foundElement = NO; 34 | 35 | __block NSString *redboxError = nil; 36 | #ifdef DEBUG 37 | RCTSetLogFunction(^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) { 38 | if (level >= RCTLogLevelError) { 39 | redboxError = message; 40 | } 41 | }); 42 | #endif 43 | 44 | while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) { 45 | [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 46 | [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 47 | 48 | foundElement = [self findSubviewInView:vc.view matching:^BOOL(UIView *view) { 49 | if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) { 50 | return YES; 51 | } 52 | return NO; 53 | }]; 54 | } 55 | 56 | #ifdef DEBUG 57 | RCTSetLogFunction(RCTDefaultLogFunction); 58 | #endif 59 | 60 | XCTAssertNil(redboxError, @"RedBox error: %@", redboxError); 61 | XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS); 62 | } 63 | 64 | 65 | @end 66 | -------------------------------------------------------------------------------- /frontend/app/mobile/metro.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Metro configuration for React Native 3 | * https://github.com/facebook/react-native 4 | * 5 | * @format 6 | */ 7 | 8 | module.exports = { 9 | transformer: { 10 | getTransformOptions: async () => ({ 11 | transform: { 12 | experimentalImportSupport: false, 13 | inlineRequires: false, 14 | }, 15 | }), 16 | }, 17 | }; 18 | -------------------------------------------------------------------------------- /frontend/app/mobile/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "version": "0.0.1", 4 | "private": true, 5 | "scripts": { 6 | "android": "react-native run-android", 7 | "ios": "react-native run-ios", 8 | "start": "react-native start", 9 | "test": "jest", 10 | "lint": "eslint ." 11 | }, 12 | "husky": { 13 | "hooks": { 14 | "pre-commit": "pretty-quick --staged" 15 | } 16 | }, 17 | "dependencies": { 18 | "@react-native-community/async-storage": "^1.12.0", 19 | "@react-native-community/masked-view": "^0.1.10", 20 | "@react-native-community/toolbar-android": "0.1.0-rc.2", 21 | "axios": "^0.21.1", 22 | "i18n-js": "^3.8.0", 23 | "moment": "^2.29.1", 24 | "prop-types": "^15.7.2", 25 | "react": "16.13.1", 26 | "react-native": "0.63.4", 27 | "react-native-animatable": "^1.3.3", 28 | "react-native-communications": "^2.2.1", 29 | "react-native-gesture-handler": "^1.10.2", 30 | "react-native-image-picker": "^1.1.0", 31 | "react-native-keyboard-aware-scroll-view": "^0.9.1", 32 | "react-native-languages": "^3.0.2", 33 | "react-native-linear-gradient": "^2.5.6", 34 | "react-native-reanimated": "^1.13.2", 35 | "react-native-safe-area-context": "^3.1.9", 36 | "react-native-screens": "^2.17.1", 37 | "react-native-vector-icons": "^7.1.0", 38 | "react-navigation": "^4.4.4", 39 | "react-navigation-stack": "^2.10.3", 40 | "react-navigation-tabs": "^2.11.0", 41 | "react-redux": "^7.2.2", 42 | "redux": "^4.0.5", 43 | "redux-thunk": "^2.3.0" 44 | }, 45 | "devDependencies": { 46 | "@babel/core": "^7.13.1", 47 | "@babel/runtime": "^7.13.7", 48 | "@react-native-community/eslint-config": "^1.1.0", 49 | "babel-jest": "^25.1.0", 50 | "eslint": "6.7.0", 51 | "husky": "^4.3.8", 52 | "jest": "^25.1.0", 53 | "metro-react-native-babel-preset": "^0.59.0", 54 | "prettier": "2.2.1", 55 | "pretty-quick": "^3.1.0", 56 | "react-scripts": "4.0.3", 57 | "react-test-renderer": "16.13.1" 58 | }, 59 | "jest": { 60 | "preset": "react-native" 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /frontend/app/mobile/src/modules/common/Body/index.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import React from 'react' 3 | import PropTypes from 'prop-types' 4 | import {View, StatusBar} from 'react-native' 5 | import {SafeAreaView} from 'react-navigation' 6 | import LinearGradient from 'react-native-linear-gradient' 7 | 8 | // UI Imports 9 | import {white} from '../../../ui/common/colors' 10 | import {gradient} from '../../../ui/common/styles' 11 | import styles from './styles' 12 | 13 | // App Imports 14 | import Message from '../Message' 15 | 16 | // Component 17 | const BodyInner = ({children}) => ( 18 | 19 | 20 | {/* Content */} 21 | {children} 22 | 23 | {/* Toast Message */} 24 | 25 | 26 | 27 | ) 28 | 29 | const Body = ({fullscreen, children}) => 30 | fullscreen ? ( 31 | 32 | 33 | 34 | 35 | {children} 36 | 37 | 38 | ) : ( 39 | 40 | 41 | 42 | 43 | {children} 44 | 45 | 46 | ) 47 | 48 | Body.propTypes = { 49 | fullscreen: PropTypes.bool.isRequired, 50 | } 51 | Body.defaultProps = { 52 | fullscreen: false, 53 | } 54 | 55 | export default Body 56 | -------------------------------------------------------------------------------- /frontend/app/mobile/src/modules/common/Body/styles.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import {StyleSheet} from 'react-native' 3 | 4 | // UI Imports 5 | export default StyleSheet.create({ 6 | gradient: { 7 | flex: 1, 8 | }, 9 | container: { 10 | flex: 1, 11 | }, 12 | 13 | wrapper: { 14 | flex: 1, 15 | position: 'relative', 16 | }, 17 | 18 | content: { 19 | flex: 1, 20 | position: 'relative', 21 | flexDirection: 'column', 22 | alignItems: 'stretch', 23 | justifyContent: 'space-between', 24 | }, 25 | }) 26 | -------------------------------------------------------------------------------- /frontend/app/mobile/src/modules/common/Centerize/index.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import React from 'react' 3 | import {View} from 'react-native' 4 | 5 | // UI Imports 6 | import styles from './styles' 7 | 8 | // Component 9 | const Centerize = (props) => ( 10 | {props.children} 11 | ) 12 | 13 | export default Centerize 14 | -------------------------------------------------------------------------------- /frontend/app/mobile/src/modules/common/Centerize/styles.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import {StyleSheet} from 'react-native' 3 | 4 | export default StyleSheet.create({ 5 | container: { 6 | flex: 1, 7 | alignItems: 'center', 8 | justifyContent: 'center', 9 | }, 10 | }) 11 | -------------------------------------------------------------------------------- /frontend/app/mobile/src/modules/common/EmptyMessage/index.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import React from 'react' 3 | import PropTypes from 'prop-types' 4 | import {Text, View} from 'react-native' 5 | 6 | // UI Imports 7 | import {grey2, opacityLow} from '../../../ui/common/colors' 8 | import {font} from '../../../ui/common/responsive' 9 | import Icon from '../../../ui/icon/Icon' 10 | import styles from './styles' 11 | 12 | // Component 13 | const EmptyMessage = ({icon, message}) => ( 14 | 20 | 21 | 22 | {message} 23 | 24 | ) 25 | 26 | // Component Properties 27 | EmptyMessage.propTypes = { 28 | message: PropTypes.string.isRequired, 29 | icon: PropTypes.string, 30 | } 31 | EmptyMessage.defaultProps = { 32 | icon: 'alert-circle-outline', 33 | } 34 | 35 | export default EmptyMessage 36 | -------------------------------------------------------------------------------- /frontend/app/mobile/src/modules/common/EmptyMessage/styles.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import {StyleSheet} from 'react-native' 3 | 4 | // UI Imports 5 | import { 6 | blockMarginHalf, 7 | blockPadding, 8 | font, 9 | } from '../../../ui/common/responsive' 10 | import {grey2} from '../../../ui/common/colors' 11 | 12 | export default StyleSheet.create({ 13 | container: { 14 | flex: 1, 15 | flexDirection: 'column', 16 | alignItems: 'center', 17 | justifyContent: 'center', 18 | padding: blockPadding * 2, 19 | }, 20 | text: { 21 | fontSize: font(16), 22 | color: grey2, 23 | marginTop: blockMarginHalf, 24 | textAlign: 'center', 25 | }, 26 | }) 27 | -------------------------------------------------------------------------------- /frontend/app/mobile/src/modules/common/Loading/index.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import React from 'react' 3 | import PropTypes from 'prop-types' 4 | import {View, Text, ActivityIndicator} from 'react-native' 5 | 6 | // UI Imports 7 | import {grey2} from '../../../ui/common/colors' 8 | import styles from './styles' 9 | 10 | // Component 11 | const Loading = ({message, size, color}) => ( 12 | 13 | 14 | 15 | {message} 16 | 17 | ) 18 | 19 | // Component Properties 20 | Loading.propTypes = { 21 | message: PropTypes.string, 22 | size: PropTypes.string, 23 | color: PropTypes.string, 24 | } 25 | Loading.defaultProps = { 26 | message: '', 27 | size: 'large', 28 | color: grey2, 29 | } 30 | 31 | export default Loading 32 | -------------------------------------------------------------------------------- /frontend/app/mobile/src/modules/common/Loading/styles.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import {StyleSheet} from 'react-native' 3 | 4 | // UI Imports 5 | import {blockMarginHalf, blockPadding} from '../../../ui/common/responsive' 6 | import {grey2} from '../../../ui/common/colors' 7 | 8 | export default StyleSheet.create({ 9 | container: { 10 | alignItems: 'center', 11 | justifyContent: 'center', 12 | flexDirection: 'column', 13 | padding: blockPadding, 14 | }, 15 | text: { 16 | color: grey2, 17 | marginTop: blockMarginHalf, 18 | textAlign: 'center', 19 | }, 20 | }) 21 | -------------------------------------------------------------------------------- /frontend/app/mobile/src/modules/common/Logo/index.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import React from 'react' 3 | import {View, Image} from 'react-native' 4 | 5 | // Assets 6 | import imageLogo from '../../../../assets/logo.png' 7 | 8 | // UI Imports 9 | import Typography from '../../../ui/Typography' 10 | import styles from './styles' 11 | 12 | // App Imports 13 | import params from '../../../setup/config/params' 14 | 15 | // Component 16 | const Logo = () => ( 17 | 18 | {/* Logo */} 19 | 20 | 21 | 22 | 23 | 24 | {params.site.name.toUpperCase()} 25 | 26 | 27 | ) 28 | 29 | export default Logo 30 | -------------------------------------------------------------------------------- /frontend/app/mobile/src/modules/common/Logo/styles.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import {StyleSheet} from 'react-native' 3 | 4 | // UI Imports 5 | import {scalable} from '../../../ui/common/responsive' 6 | 7 | // Styles 8 | export default StyleSheet.create({ 9 | imageWrapper: { 10 | width: scalable(100), 11 | height: scalable(100), 12 | borderRadius: scalable(15), 13 | }, 14 | 15 | image: { 16 | width: scalable(100), 17 | height: scalable(100), 18 | borderRadius: scalable(15), 19 | }, 20 | }) 21 | -------------------------------------------------------------------------------- /frontend/app/mobile/src/modules/common/Message/index.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import React from 'react' 3 | import {useDispatch, useSelector} from 'react-redux' 4 | 5 | // UI Imports 6 | import Toast from '../../../ui/Toast' 7 | 8 | // App Imports 9 | import {messageHide} from '../api/actions' 10 | 11 | // Component 12 | const Message = () => { 13 | const { 14 | message: {open, success, message}, 15 | } = useSelector((state) => state.common) 16 | const dispatch = useDispatch() 17 | 18 | return ( 19 | open && ( 20 | dispatch(messageHide())} 24 | /> 25 | ) 26 | ) 27 | } 28 | 29 | export default Message 30 | -------------------------------------------------------------------------------- /frontend/app/mobile/src/modules/common/NavigationTop/ActionBack/index.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import React, {PureComponent} from 'react' 3 | import {withNavigation} from 'react-navigation' 4 | 5 | // UI Imports 6 | import ActionIcon from '../../../../ui/icon/ActionIcon' 7 | 8 | // Component 9 | class ActionBack extends PureComponent { 10 | onPress = () => { 11 | const {navigation} = this.props 12 | 13 | navigation.goBack() 14 | } 15 | 16 | render() { 17 | const {onPress} = this.props 18 | 19 | return 20 | } 21 | } 22 | 23 | export default withNavigation(ActionBack) 24 | -------------------------------------------------------------------------------- /frontend/app/mobile/src/modules/common/NavigationTop/ActionClose/index.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import React, {PureComponent} from 'react' 3 | import PropTypes from 'prop-types' 4 | import {withNavigation} from 'react-navigation' 5 | 6 | // UI Imports 7 | import ActionIcon from '../../../../ui/icon/ActionIcon' 8 | 9 | // Component 10 | class ActionClose extends PureComponent { 11 | render() { 12 | const {onPress} = this.props 13 | 14 | return 15 | } 16 | } 17 | // Component Properties 18 | ActionClose.propTypes = { 19 | onPress: PropTypes.func.isRequired, 20 | } 21 | 22 | export default withNavigation(ActionClose) 23 | -------------------------------------------------------------------------------- /frontend/app/mobile/src/modules/common/NavigationTop/ActionDrawer/index.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import React, {PureComponent} from 'react' 3 | import PropTypes from 'prop-types' 4 | import {withNavigation} from 'react-navigation' 5 | 6 | // UI Imports 7 | import ActionIcon from '../../../../ui/icon/ActionIcon' 8 | 9 | // Component 10 | class ActionDrawer extends PureComponent { 11 | render() { 12 | const {navigation} = this.props 13 | 14 | return navigation.openDrawer()} /> 15 | } 16 | } 17 | 18 | // Component Properties 19 | ActionDrawer.propTypes = { 20 | navigation: PropTypes.object.isRequired, 21 | } 22 | 23 | export default withNavigation(ActionDrawer) 24 | -------------------------------------------------------------------------------- /frontend/app/mobile/src/modules/common/NavigationTop/index.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import React from 'react' 3 | import PropTypes from 'prop-types' 4 | import {View, Text} from 'react-native' 5 | 6 | // UI Imports 7 | import styles from './styles' 8 | 9 | // Component 10 | const NavigationTop = ({leftIcon, title, rightIcon}) => ( 11 | 12 | {/* Left Icon */} 13 | 14 | {leftIcon || } 15 | 16 | 17 | {/* Title */} 18 | {title ? ( 19 | 20 | {title} 21 | 22 | ) : null} 23 | 24 | {/* Right Icon */} 25 | 26 | {rightIcon || } 27 | 28 | 29 | ) 30 | 31 | // Component Properties 32 | NavigationTop.propTypes = { 33 | leftIcon: PropTypes.any, 34 | title: PropTypes.any, 35 | rightIcon: PropTypes.any, 36 | } 37 | NavigationTop.defaultProps = { 38 | theme: 'primary', 39 | } 40 | 41 | export default NavigationTop 42 | -------------------------------------------------------------------------------- /frontend/app/mobile/src/modules/common/NavigationTop/styles.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import {StyleSheet} from 'react-native' 3 | 4 | // UI Imports 5 | import {navigationTopHeight, font} from '../../../ui/common/responsive' 6 | import {black, white} from '../../../ui/common/colors' 7 | 8 | export default StyleSheet.create({ 9 | container: { 10 | height: navigationTopHeight, 11 | flexDirection: 'row', 12 | alignItems: 'center', 13 | justifyContent: 'space-between', 14 | }, 15 | left: { 16 | flexDirection: 'row', 17 | justifyContent: 'flex-start', 18 | alignItems: 'center', 19 | }, 20 | middle: { 21 | flexDirection: 'row', 22 | justifyContent: 'center', 23 | alignItems: 'center', 24 | }, 25 | right: { 26 | flexDirection: 'row', 27 | justifyContent: 'flex-end', 28 | alignItems: 'center', 29 | }, 30 | title: { 31 | color: black, 32 | fontSize: font(18), 33 | }, 34 | iconPlaceholder: { 35 | width: navigationTopHeight, 36 | height: navigationTopHeight, 37 | }, 38 | }) 39 | -------------------------------------------------------------------------------- /frontend/app/mobile/src/modules/common/NavigationTopInner/index.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import React from 'react' 3 | import PropTypes from 'prop-types' 4 | import {View, Text} from 'react-native' 5 | 6 | // UI Imports 7 | import stylesCommon from '../../../ui/common/styles' 8 | import styles from './styles' 9 | 10 | // Component 11 | const NavigationTopInner = ({ 12 | leftIcon, 13 | title, 14 | subTitle, 15 | rightContent, 16 | shadow, 17 | }) => ( 18 | 21 | {/* Left */} 22 | 23 | {/* Left Icon */} 24 | 25 | {leftIcon || } 26 | 27 | 28 | {/* Info */} 29 | 30 | {title && {title.toUpperCase()}} 31 | {subTitle && ( 32 | {subTitle.toUpperCase()} 33 | )} 34 | 35 | 36 | 37 | {/* Right */} 38 | {rightContent} 39 | 40 | ) 41 | 42 | // Component Properties 43 | NavigationTopInner.propTypes = { 44 | leftIcon: PropTypes.any, 45 | title: PropTypes.any, 46 | subTitle: PropTypes.any, 47 | rightContent: PropTypes.any, 48 | shadow: PropTypes.bool, 49 | } 50 | NavigationTopInner.defaultProps = { 51 | shadow: true, 52 | } 53 | 54 | export default NavigationTopInner 55 | -------------------------------------------------------------------------------- /frontend/app/mobile/src/modules/common/NavigationTopInner/styles.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import {StyleSheet} from 'react-native' 3 | 4 | // UI Imports 5 | import { 6 | navigationTopHeight, 7 | font, 8 | blockMargin, 9 | } from '../../../ui/common/responsive' 10 | import {black, grey1, white} from '../../../ui/common/colors' 11 | 12 | export default StyleSheet.create({ 13 | container: { 14 | height: navigationTopHeight, 15 | flexDirection: 'row', 16 | alignItems: 'center', 17 | justifyContent: 'space-between', 18 | backgroundColor: white, 19 | }, 20 | left: { 21 | flexDirection: 'row', 22 | }, 23 | leftIcon: { 24 | flexDirection: 'row', 25 | justifyContent: 'flex-start', 26 | height: navigationTopHeight, 27 | }, 28 | leftIconPlaceholder: { 29 | width: blockMargin * 1.5, 30 | }, 31 | right: { 32 | flexDirection: 'row', 33 | justifyContent: 'flex-end', 34 | marginRight: blockMargin * 1.5, 35 | }, 36 | info: { 37 | justifyContent: 'center', 38 | }, 39 | title: { 40 | color: black, 41 | fontSize: font(16), 42 | }, 43 | subTitle: { 44 | color: grey1, 45 | fontSize: font(10), 46 | }, 47 | }) 48 | -------------------------------------------------------------------------------- /frontend/app/mobile/src/modules/common/NavigationTopTransparent/index.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import React from 'react' 3 | import PropTypes from 'prop-types' 4 | import {View} from 'react-native' 5 | 6 | // UI Imports 7 | import styles from './styles' 8 | 9 | // Component 10 | const NavigationTopTransparent = ({leftIcon, rightContent, middleContent}) => ( 11 | 12 | {/* Left */} 13 | 14 | {/* Left Icon */} 15 | 16 | {leftIcon || } 17 | 18 | 19 | 20 | {/* Middle */} 21 | {middleContent} 22 | 23 | {/* Right */} 24 | {rightContent} 25 | 26 | ) 27 | 28 | // Component Properties 29 | NavigationTopTransparent.propTypes = { 30 | leftIcon: PropTypes.any, 31 | rightContent: PropTypes.any, 32 | } 33 | 34 | export default NavigationTopTransparent 35 | -------------------------------------------------------------------------------- /frontend/app/mobile/src/modules/common/NavigationTopTransparent/styles.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import {StyleSheet} from 'react-native' 3 | 4 | // UI Imports 5 | import { 6 | navigationTopHeight, 7 | font, 8 | blockMargin, 9 | } from '../../../ui/common/responsive' 10 | import {white} from '../../../ui/common/colors' 11 | 12 | export default StyleSheet.create({ 13 | container: { 14 | height: navigationTopHeight, 15 | flexDirection: 'row', 16 | alignItems: 'center', 17 | justifyContent: 'space-between', 18 | }, 19 | left: { 20 | flexDirection: 'row', 21 | }, 22 | leftIcon: { 23 | flexDirection: 'row', 24 | justifyContent: 'flex-start', 25 | height: navigationTopHeight, 26 | }, 27 | leftIconPlaceholder: { 28 | width: blockMargin * 1.5, 29 | }, 30 | right: { 31 | flexDirection: 'row', 32 | justifyContent: 'flex-end', 33 | marginRight: blockMargin * 1.5, 34 | }, 35 | info: { 36 | justifyContent: 'center', 37 | }, 38 | middle: { 39 | flexDirection: 'row', 40 | justifyContent: 'center', 41 | alignItems: 'center', 42 | }, 43 | }) 44 | -------------------------------------------------------------------------------- /frontend/app/mobile/src/modules/common/api/actions.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import axios from 'axios/index' 3 | 4 | // App Imports 5 | import {API_URL} from '../../../setup/config/env' 6 | import params from '../../../setup/config/params' 7 | import {MESSAGE_SHOW, MESSAGE_HIDE} from './types' 8 | 9 | // Actions 10 | export function messageShow( 11 | {success, message}, 12 | hide = params.message.timers.default, 13 | ) { 14 | return (dispatch) => { 15 | window.setTimeout(() => { 16 | dispatch({type: MESSAGE_HIDE}) 17 | }, hide) 18 | 19 | dispatch({type: MESSAGE_SHOW, success, message}) 20 | } 21 | } 22 | 23 | export function messageHide() { 24 | return {type: MESSAGE_HIDE} 25 | } 26 | 27 | export function upload(file) { 28 | return axios.post(API_URL + params.endpoint.uploads, file, { 29 | headers: { 30 | 'Content-Type': 'multipart/form-data', 31 | }, 32 | }) 33 | } 34 | -------------------------------------------------------------------------------- /frontend/app/mobile/src/modules/common/api/state.js: -------------------------------------------------------------------------------- 1 | // App Imports 2 | import {MESSAGE_SHOW, MESSAGE_HIDE} from './types' 3 | 4 | // Initial State 5 | export const commonInitialState = { 6 | message: { 7 | success: false, 8 | message: '', 9 | open: false, 10 | }, 11 | } 12 | 13 | // State 14 | export default (state = commonInitialState, action) => { 15 | switch (action.type) { 16 | case MESSAGE_SHOW: 17 | return { 18 | ...state, 19 | message: { 20 | success: action.success, 21 | message: action.message, 22 | open: true, 23 | }, 24 | } 25 | 26 | case MESSAGE_HIDE: 27 | return { 28 | ...state, 29 | ...commonInitialState, 30 | } 31 | 32 | default: 33 | return state 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /frontend/app/mobile/src/modules/common/api/types.js: -------------------------------------------------------------------------------- 1 | // Actions Types 2 | 3 | export const MESSAGE_SHOW = 'COMMON/MESSAGE_SHOW' 4 | export const MESSAGE_HIDE = 'COMMON/MESSAGE_HIDE' 5 | -------------------------------------------------------------------------------- /frontend/app/mobile/src/modules/common/translations/en.json: -------------------------------------------------------------------------------- 1 | { 2 | "common": { 3 | "loading": "Please wait...", 4 | "error": { 5 | "default": "There was some error. Please try again." 6 | }, 7 | "success": { 8 | "default": "Success." 9 | }, 10 | "button": { 11 | "submit": "Submit", 12 | "back": "Back", 13 | "save": "Save", 14 | "logout": "Logout", 15 | "create": "Create", 16 | "delete": "Delete", 17 | "close": "Close", 18 | "okay": "OK", 19 | "cancel": "Cancel", 20 | "help": "Help", 21 | "contact": "Contact Us" 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /frontend/app/mobile/src/modules/note/Create/styles.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import {StyleSheet} from 'react-native' 3 | 4 | // UI Imports 5 | import {blockMargin, blockMarginHalf} from '../../../ui/common/responsive' 6 | 7 | // Styles 8 | export default StyleSheet.create({ 9 | container: { 10 | flex: 1, 11 | }, 12 | 13 | navigationButton: { 14 | marginLeft: blockMarginHalf, 15 | }, 16 | 17 | formContainer: { 18 | flex: 1, 19 | margin: blockMargin * 2, 20 | }, 21 | }) 22 | -------------------------------------------------------------------------------- /frontend/app/mobile/src/modules/note/Detail/styles.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import {StyleSheet} from 'react-native' 3 | 4 | // UI Imports 5 | import {blockMargin} from '../../../ui/common/responsive' 6 | 7 | // Styles 8 | export default StyleSheet.create({ 9 | container: { 10 | flex: 1, 11 | }, 12 | 13 | content: { 14 | margin: blockMargin * 2, 15 | }, 16 | }) 17 | -------------------------------------------------------------------------------- /frontend/app/mobile/src/modules/note/Item/index.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import React from 'react' 3 | import PropTypes from 'prop-types' 4 | import {View, TouchableOpacity} from 'react-native' 5 | import moment from 'moment' 6 | 7 | // UI Imports 8 | import {grey2} from '../../../ui/common/colors' 9 | import Typography from '../../../ui/Typography' 10 | import styles from './styles' 11 | 12 | // App Imports 13 | import params from '../../../setup/config/params' 14 | 15 | // Component 16 | const Item = ({onSelect, item: {_id, note, createdAt}}) => ( 17 | 18 | 19 | 20 | {note} 21 | 22 | 23 | 24 | {moment(createdAt).format(params.common.formats.dateTime)} 25 | 26 | 27 | 28 | ) 29 | 30 | // Component Properties 31 | Item.propTypes = { 32 | item: PropTypes.object.isRequired, 33 | onSelect: PropTypes.func.isRequired, 34 | } 35 | 36 | export default Item 37 | -------------------------------------------------------------------------------- /frontend/app/mobile/src/modules/note/Item/styles.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import {StyleSheet} from 'react-native' 3 | 4 | // UI Imports 5 | import {blockMargin, blockMarginHalf} from '../../../ui/common/responsive' 6 | 7 | export default StyleSheet.create({ 8 | container: { 9 | margin: blockMargin * 1.5, 10 | }, 11 | 12 | date: { 13 | marginTop: blockMarginHalf, 14 | }, 15 | }) 16 | -------------------------------------------------------------------------------- /frontend/app/mobile/src/modules/note/List/styles.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import {StyleSheet} from 'react-native' 3 | 4 | // UI Imports 5 | import {blockMarginHalf} from '../../../ui/common/responsive' 6 | 7 | // Styles 8 | export default StyleSheet.create({ 9 | container: { 10 | flex: 1, 11 | }, 12 | 13 | navigationButton: { 14 | marginLeft: blockMarginHalf, 15 | }, 16 | }) 17 | -------------------------------------------------------------------------------- /frontend/app/mobile/src/modules/note/api/actions/cache-keys.js: -------------------------------------------------------------------------------- 1 | export const NOTE_LIST_CACHE = 'CACHE.KEY.NOTE.LIST' 2 | export const NOTE_DETAIL_CACHE = 'CACHE.KEY.NOTE.DETAIL.' // + noteId 3 | -------------------------------------------------------------------------------- /frontend/app/mobile/src/modules/note/api/actions/mutation.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import axios from 'axios' 3 | 4 | // App Imports 5 | import {API_URL} from '../../../../setup/config/env' 6 | 7 | // Actions 8 | 9 | // Create 10 | export function create({note}) { 11 | return axios.post(API_URL, { 12 | operation: 'noteCreate', 13 | params: {note}, 14 | }) 15 | } 16 | 17 | // Delete 18 | export function remove({noteId}) { 19 | return axios.post(API_URL, { 20 | operation: 'noteDelete', 21 | params: {noteId}, 22 | }) 23 | } 24 | -------------------------------------------------------------------------------- /frontend/app/mobile/src/modules/note/api/actions/types.js: -------------------------------------------------------------------------------- 1 | // Actions Types 2 | 3 | // List 4 | export const LIST_REQUEST = 'NOTE/LIST/REQUEST' 5 | export const LIST_RESPONSE = 'NOTE/LIST/RESPONSE' 6 | export const LIST_DONE = 'NOTE/LIST/DONE' 7 | export const LIST_RESET = 'NOTE/LIST/RESET' 8 | -------------------------------------------------------------------------------- /frontend/app/mobile/src/modules/note/api/state/index.js: -------------------------------------------------------------------------------- 1 | // App Imports 2 | import notes from './list' 3 | 4 | export default { 5 | notes, 6 | } 7 | -------------------------------------------------------------------------------- /frontend/app/mobile/src/modules/note/api/state/list.js: -------------------------------------------------------------------------------- 1 | // App Imports 2 | import { 3 | LIST_DONE, 4 | LIST_REQUEST, 5 | LIST_RESET, 6 | LIST_RESPONSE, 7 | } from '../actions/types' 8 | 9 | // List 10 | 11 | // Initial State 12 | const notesInitialState = { 13 | isLoading: false, 14 | list: [], 15 | } 16 | 17 | // State 18 | export default (state = notesInitialState, action) => { 19 | switch (action.type) { 20 | case LIST_REQUEST: 21 | return { 22 | ...state, 23 | isLoading: action.isLoading, 24 | } 25 | 26 | case LIST_RESPONSE: 27 | return { 28 | ...state, 29 | list: action.list, 30 | } 31 | 32 | case LIST_DONE: 33 | return { 34 | ...state, 35 | isLoading: false, 36 | } 37 | 38 | case LIST_RESET: 39 | return {...notesInitialState} 40 | 41 | default: 42 | return state 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /frontend/app/mobile/src/modules/note/translations/en.json: -------------------------------------------------------------------------------- 1 | { 2 | "note": { 3 | "fields": { 4 | "note": "Note", 5 | "notePlaceholder": "Enter your note.." 6 | }, 7 | 8 | "list": { 9 | "title": "Notes", 10 | "subTitle": "Start taking some notes!", 11 | "empty": "You have not added any notes yet." 12 | }, 13 | 14 | "create": { 15 | "title": "Create Note", 16 | "subTitle": "What's happening?" 17 | }, 18 | 19 | "detail": { 20 | "title": "Note", 21 | "subTitle": "Taken on {{date}}" 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /frontend/app/mobile/src/modules/pages/Entry/styles.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import {StyleSheet} from 'react-native' 3 | 4 | // Styles 5 | export default StyleSheet.create({ 6 | container: { 7 | flex: 1, 8 | justifyContent: 'center', 9 | alignItems: 'center', 10 | }, 11 | }) 12 | -------------------------------------------------------------------------------- /frontend/app/mobile/src/modules/pages/Help/styles.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import {StyleSheet} from 'react-native' 3 | import { 4 | blockMargin, 5 | blockMarginHalf, 6 | blockPadding, 7 | } from '../../../ui/common/responsive' 8 | 9 | // Styles 10 | export default StyleSheet.create({ 11 | container: { 12 | flex: 1, 13 | }, 14 | 15 | content: { 16 | paddingVertical: blockPadding * 1.5, 17 | paddingHorizontal: blockPadding * 2, 18 | }, 19 | 20 | section: { 21 | marginTop: blockMargin * 1.5, 22 | }, 23 | 24 | item: { 25 | marginTop: blockMarginHalf, 26 | }, 27 | itemRow: { 28 | flexDirection: 'row', 29 | }, 30 | itemBullet: { 31 | marginRight: blockMargin, 32 | }, 33 | }) 34 | -------------------------------------------------------------------------------- /frontend/app/mobile/src/modules/pages/translations/en.json: -------------------------------------------------------------------------------- 1 | { 2 | "pages": { 3 | "help": { 4 | "title": "Help", 5 | "subTitle": "Support and more info", 6 | 7 | "content": { 8 | "about": "This is a simple note taking application made with JavaScript.", 9 | 10 | "feature1": { 11 | "title": "Feature number one", 12 | "points": { 13 | "p1": "Ignigenas ortum in asopus!", 14 | "p2": "Est fatalis uria, cesaris.", 15 | "p3": "Heu, secundus acipenser!", 16 | "p4": "Abaculuss trabem in lentia!", 17 | "p5": "Domina bi-color orexis est." 18 | } 19 | }, 20 | 21 | "feature2": { 22 | "title": "Feature number two", 23 | "points": { 24 | "p1": "Ignigenas ortum in asopus!", 25 | "p2": "Est fatalis uria, cesaris.", 26 | "p3": "Heu, secundus acipenser!", 27 | "p4": "Abaculuss trabem in lentia!", 28 | "p5": "Domina bi-color orexis est." 29 | } 30 | } 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /frontend/app/mobile/src/modules/user/Login/styles.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import {StyleSheet} from 'react-native' 3 | 4 | // UI Imports 5 | import {blockMargin, blockPadding} from '../../../ui/common/responsive' 6 | 7 | // Styles 8 | export default StyleSheet.create({ 9 | container: { 10 | flex: 1, 11 | }, 12 | 13 | formContainer: { 14 | flex: 1, 15 | alignItems: 'center', 16 | justifyContent: 'center', 17 | marginHorizontal: blockMargin * 3, 18 | }, 19 | form: { 20 | marginBottom: blockMargin * 3, 21 | }, 22 | 23 | bottomCta: { 24 | paddingVertical: blockPadding * 2, 25 | }, 26 | }) 27 | -------------------------------------------------------------------------------- /frontend/app/mobile/src/modules/user/Profile/MyInfo/styles.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import {StyleSheet} from 'react-native' 3 | 4 | // UI Imports 5 | import { 6 | blockMargin, 7 | blockMarginHalf, 8 | scalable, 9 | } from '../../../../ui/common/responsive' 10 | import stylesCommon from '../../../../ui/common/styles' 11 | 12 | // Styles 13 | export default StyleSheet.create({ 14 | container: { 15 | flex: 1, 16 | }, 17 | 18 | // Profile 19 | profile: { 20 | flexDirection: 'column', 21 | alignItems: 'center', 22 | justifyContent: 'center', 23 | }, 24 | profileImageWrapper: { 25 | position: 'relative', 26 | marginTop: blockMargin * 3, 27 | ...stylesCommon.shadow, 28 | }, 29 | profileImage: { 30 | width: scalable(120), 31 | height: scalable(120), 32 | borderRadius: scalable(60), 33 | }, 34 | profileTitle: { 35 | marginTop: blockMargin, 36 | textAlign: 'center', 37 | }, 38 | profileCaption: { 39 | marginTop: blockMarginHalf, 40 | textAlign: 'center', 41 | }, 42 | 43 | formContainer: { 44 | flex: 1, 45 | margin: blockMargin * 2, 46 | }, 47 | }) 48 | -------------------------------------------------------------------------------- /frontend/app/mobile/src/modules/user/Profile/styles.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import {StyleSheet} from 'react-native' 3 | 4 | // Styles 5 | export default StyleSheet.create({ 6 | container: { 7 | flex: 1, 8 | }, 9 | }) 10 | -------------------------------------------------------------------------------- /frontend/app/mobile/src/modules/user/Signup/styles.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import {StyleSheet} from 'react-native' 3 | 4 | // UI Imports 5 | import {blockMargin, blockPadding} from '../../../ui/common/responsive' 6 | 7 | // Styles 8 | export default StyleSheet.create({ 9 | container: { 10 | flex: 1, 11 | }, 12 | 13 | formContainer: { 14 | flex: 1, 15 | alignItems: 'center', 16 | justifyContent: 'center', 17 | marginHorizontal: blockMargin * 3, 18 | }, 19 | form: { 20 | marginBottom: blockMargin * 3, 21 | }, 22 | 23 | bottomCta: { 24 | paddingVertical: blockPadding * 2, 25 | }, 26 | }) 27 | -------------------------------------------------------------------------------- /frontend/app/mobile/src/modules/user/Start/styles.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import {StyleSheet} from 'react-native' 3 | 4 | // UI Imports 5 | import {blockMargin, blockPadding} from '../../../ui/common/responsive' 6 | 7 | // Styles 8 | export default StyleSheet.create({ 9 | container: { 10 | flex: 1, 11 | }, 12 | 13 | // Intro 14 | intro: { 15 | flex: 1, 16 | justifyContent: 'center', 17 | alignItems: 'center', 18 | paddingHorizontal: blockPadding, 19 | }, 20 | introTitle: { 21 | marginVertical: blockMargin * 2, 22 | }, 23 | 24 | // Buttons 25 | buttons: { 26 | padding: blockPadding * 2, 27 | position: 'absolute', 28 | bottom: 0, 29 | left: 0, 30 | right: 0, 31 | }, 32 | buttonsSocial: { 33 | flexDirection: 'row', 34 | marginTop: blockMargin, 35 | }, 36 | buttonsSocialLeft: { 37 | flex: 1, 38 | }, 39 | buttonsSocialRight: { 40 | flex: 1, 41 | marginLeft: blockMargin, 42 | }, 43 | 44 | terms: { 45 | marginTop: blockMargin * 1.5, 46 | }, 47 | }) 48 | -------------------------------------------------------------------------------- /frontend/app/mobile/src/modules/user/api/actions/cache-keys.js: -------------------------------------------------------------------------------- 1 | export const USER_LIST_CACHE = 'CACHE.KEY.USER.LIST' 2 | -------------------------------------------------------------------------------- /frontend/app/mobile/src/modules/user/api/actions/mutation.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import axios from 'axios' 3 | 4 | // App Imports 5 | import {API_URL} from '../../../../setup/config/env' 6 | 7 | // Actions 8 | 9 | // Signup 10 | export function signup({name, email, password, passwordRepeat}) { 11 | return axios.post(API_URL, { 12 | operation: 'userSignup', 13 | params: {name, email, password, passwordRepeat}, 14 | }) 15 | } 16 | 17 | // Update 18 | export function profileUpdate({name}) { 19 | return axios.post(API_URL, { 20 | operation: 'userProfileUpdate', 21 | params: {name}, 22 | }) 23 | } 24 | 25 | // Change image 26 | export function changeImage({image}) { 27 | return axios.post(API_URL, { 28 | operation: 'userChangeImage', 29 | params: {image}, 30 | }) 31 | } 32 | -------------------------------------------------------------------------------- /frontend/app/mobile/src/modules/user/api/actions/types.js: -------------------------------------------------------------------------------- 1 | // Actions Types 2 | 3 | export const LOGIN_REQUEST = 'AUTH/LOGIN_REQUEST' 4 | export const LOGIN_RESPONSE = 'AUTH/LOGIN_RESPONSE' 5 | export const SET_USER = 'AUTH/SET_USER' 6 | export const LOGOUT = 'AUTH/LOGOUT' 7 | 8 | // List 9 | export const LIST_REQUEST = 'USER/LIST/REQUEST' 10 | export const LIST_RESPONSE = 'USER/LIST/RESPONSE' 11 | export const LIST_DONE = 'USER/LIST/DONE' 12 | export const LIST_RESET = 'USER/LIST/RESET' 13 | export const LIST_FILTER = 'USER/LIST/FILTER' 14 | -------------------------------------------------------------------------------- /frontend/app/mobile/src/modules/user/api/state/auth.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import isEmpty from 'lodash/isEmpty' 3 | 4 | // App Imports 5 | import {SET_USER, LOGIN_REQUEST, LOGIN_RESPONSE, LOGOUT} from '../actions/types' 6 | 7 | // Auth (user) 8 | 9 | // Initial State 10 | 11 | export const authInitialState = { 12 | error: null, 13 | isLoading: false, 14 | isAuthenticated: false, 15 | details: null, 16 | } 17 | 18 | // State 19 | export default (state = authInitialState, action) => { 20 | switch (action.type) { 21 | case SET_USER: 22 | return { 23 | ...state, 24 | isAuthenticated: !isEmpty(action.user), 25 | details: action.user, 26 | } 27 | 28 | case LOGIN_REQUEST: 29 | return { 30 | ...state, 31 | error: null, 32 | isLoading: action.isLoading, 33 | } 34 | 35 | case LOGIN_RESPONSE: 36 | return { 37 | ...state, 38 | error: action.error, 39 | isLoading: false, 40 | } 41 | 42 | case LOGOUT: 43 | return authInitialState 44 | 45 | default: 46 | return state 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /frontend/app/mobile/src/modules/user/api/state/index.js: -------------------------------------------------------------------------------- 1 | // App Imports 2 | import auth from './auth' 3 | 4 | export default { 5 | auth, 6 | } 7 | -------------------------------------------------------------------------------- /frontend/app/mobile/src/modules/user/api/state/list.js: -------------------------------------------------------------------------------- 1 | // App Imports 2 | import { 3 | LIST_DONE, 4 | LIST_FILTER, 5 | LIST_REQUEST, 6 | LIST_RESET, 7 | LIST_RESPONSE, 8 | } from '../actions/types' 9 | 10 | // User list 11 | 12 | // Initial State 13 | const usersInitialState = { 14 | isLoading: false, 15 | list: [], 16 | } 17 | 18 | // State 19 | export default (state = usersInitialState, action) => { 20 | switch (action.type) { 21 | case LIST_REQUEST: 22 | return { 23 | ...state, 24 | isLoading: action.isLoading, 25 | } 26 | 27 | case LIST_RESPONSE: 28 | return { 29 | ...state, 30 | list: action.list, 31 | } 32 | 33 | case LIST_DONE: 34 | return { 35 | ...state, 36 | isLoading: false, 37 | } 38 | 39 | case LIST_RESET: 40 | return {...usersInitialState} 41 | 42 | case LIST_FILTER: 43 | return { 44 | ...state, 45 | list: action.list, 46 | } 47 | 48 | default: 49 | return state 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /frontend/app/mobile/src/modules/user/translations/en.json: -------------------------------------------------------------------------------- 1 | { 2 | "user": { 3 | "fields": { 4 | "name": "Name", 5 | "namePlaceholder": "Enter your name", 6 | "email": "Email", 7 | "emailPlaceholder": "Enter your email", 8 | "password": "Password", 9 | "passwordPlaceholder": "Enter your password", 10 | "passwordRepeat": "Retype Password", 11 | "passwordRepeatPlaceholder": "Retype your password" 12 | }, 13 | 14 | "start": { 15 | "title": "Simple note taking application!", 16 | 17 | "button": { 18 | "email": "Start with email", 19 | "terms": "By signing up, you agree to accept the terms of use." 20 | } 21 | }, 22 | 23 | "signup": { 24 | "title": "Signup", 25 | 26 | "button": { 27 | "login": "Already have an account? Login" 28 | } 29 | }, 30 | 31 | "login": { 32 | "title": "Login", 33 | 34 | "button": { 35 | "signup": "Don't have an account? Signup" 36 | } 37 | }, 38 | 39 | "profile": { 40 | "title": "Profile", 41 | "subTitle": "Your information", 42 | 43 | "prompts": { 44 | "logout": "Are you sure you want to logout?" 45 | }, 46 | 47 | "messages": { 48 | "logout": "You have logged out successfully." 49 | }, 50 | 51 | "button": { 52 | "submit": "Save" 53 | } 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /frontend/app/mobile/src/setup/config/env.js: -------------------------------------------------------------------------------- 1 | // Env 2 | 3 | export const APP_ENV = 'development' 4 | 5 | export const API_URL = 'http://192.168.225.49:8000' 6 | export const WEB_URL = 'http://192.168.1.1:5000' 7 | export const LANDING_URL = 'http://192.168.1.1:3000' 8 | -------------------------------------------------------------------------------- /frontend/app/mobile/src/setup/config/params.json: -------------------------------------------------------------------------------- 1 | { 2 | "site": { 3 | "name": "Example", 4 | "url": { 5 | "contact": "contact", 6 | "terms": "terms-of-use" 7 | }, 8 | "emails": { 9 | "help": { 10 | "name": "Example", 11 | "email": "help@example.com" 12 | } 13 | } 14 | }, 15 | 16 | "endpoint": { 17 | "uploads": "/uploads" 18 | }, 19 | 20 | "message": { 21 | "timers": { 22 | "short": 2000, 23 | "default": 4000, 24 | "long": 6000 25 | } 26 | }, 27 | 28 | "folders": { 29 | "public": "public/", 30 | "user": { 31 | "image": "/images/user" 32 | } 33 | }, 34 | 35 | "common": { 36 | "formats": { 37 | "time": "HH:mm", 38 | "dateTime": "ddd MMM Do, hh:mm a" 39 | } 40 | }, 41 | 42 | "user": { 43 | "password": { 44 | "minLength": 6 45 | }, 46 | "image": { 47 | "default": "default.jpg" 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /frontend/app/mobile/src/setup/helpers/utils.js: -------------------------------------------------------------------------------- 1 | // App Imports 2 | import params from '../config/params' 3 | import {API_URL} from '../config/env' 4 | 5 | // Utility functions 6 | 7 | // Get percentage 8 | export function percent(value, percent) { 9 | return (value / 100) * percent 10 | } 11 | 12 | // No operation 13 | export const noop = () => {} 14 | 15 | // Render if 16 | export function renderIf(condition, content) { 17 | return condition ? content : null 18 | } 19 | 20 | // Routing 21 | export function getRoutesForStack(routes) { 22 | return Object.values(routes).reduce((result, route) => { 23 | result[route.name] = route 24 | return result 25 | }, {}) 26 | } 27 | 28 | // User Image 29 | export function userImage(image) { 30 | return API_URL + params.folders.user.image + '/' + image 31 | } 32 | -------------------------------------------------------------------------------- /frontend/app/mobile/src/setup/index.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import React from 'react' 3 | import {YellowBox} from 'react-native' 4 | import {Provider} from 'react-redux' 5 | 6 | // App Imports 7 | import {store} from './store' 8 | import Routes from './routes' 9 | 10 | // App 11 | export default class App extends React.Component { 12 | constructor(props) { 13 | super(props) 14 | 15 | YellowBox.ignoreWarnings([ 16 | 'Warning: componentWillMount is deprecated', 17 | 'Warning: componentWillReceiveProps is deprecated', 18 | 'Warning: componentWillUpdate is deprecated', 19 | ]) 20 | } 21 | 22 | render() { 23 | return ( 24 | 25 | 26 | 27 | ) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /frontend/app/mobile/src/setup/routes/index.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import React from 'react' 3 | import {createAppContainer, createSwitchNavigator} from 'react-navigation' 4 | 5 | // App Imports 6 | import names from './names' 7 | import Entry from '../../modules/pages/Entry' 8 | import PreLoginStack from './preLogin' 9 | import PostLoginStack from './postLogin' 10 | 11 | /* 12 | Routing Structure 13 | 14 | Routes (SwitchNavigator) 15 | - Entry 16 | - PreLoginStack (StackNavigator) 17 | - Start 18 | - Signup 19 | - Login 20 | - PostLoginStack (TabNavigator) 21 | - Notes (StackNavigator) 22 | - List 23 | - Detail 24 | - User (StackNavigator) 25 | - Profile 26 | */ 27 | 28 | // Router 29 | const AppNavigator = createSwitchNavigator( 30 | { 31 | [names.entry]: Entry, 32 | [names.preLoginStack]: PreLoginStack, 33 | [names.postLoginStack]: PostLoginStack, 34 | }, 35 | {initialRouteName: 'entry'}, 36 | ) 37 | 38 | export default createAppContainer(AppNavigator) 39 | -------------------------------------------------------------------------------- /frontend/app/mobile/src/setup/routes/names.js: -------------------------------------------------------------------------------- 1 | export default { 2 | entry: 'entry', 3 | preLoginStack: 'auth', 4 | postLoginStack: 'app', 5 | } 6 | -------------------------------------------------------------------------------- /frontend/app/mobile/src/setup/routes/postLogin/index.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import React from 'react' 3 | import {createBottomTabNavigator} from 'react-navigation-tabs' 4 | 5 | // UI Imports 6 | import {font, scalable} from '../../../ui/common/responsive' 7 | import {black, grey2, white} from '../../../ui/common/colors' 8 | import Icon from '../../../ui/icon/Icon' 9 | 10 | // App Imports 11 | import {getRoutesForStack} from '../../helpers/utils' 12 | import NoteStack from './note' 13 | import UserStack from './user' 14 | 15 | const iconSize = font(27) 16 | 17 | // Routes App 18 | export const routesPostLogin = { 19 | // Note 20 | note: { 21 | name: 'note', 22 | path: 'note', 23 | screen: NoteStack, 24 | navigationOptions: { 25 | tabBarLabel: 'Notes', 26 | tabBarIcon: ({tintColor}) => ( 27 | 28 | ), 29 | }, 30 | }, 31 | 32 | // User 33 | user: { 34 | name: 'user', 35 | path: 'user', 36 | screen: UserStack, 37 | navigationOptions: { 38 | tabBarLabel: 'Profile', 39 | tabBarIcon: ({tintColor}) => ( 40 | 41 | ), 42 | }, 43 | }, 44 | } 45 | 46 | export default createBottomTabNavigator(getRoutesForStack(routesPostLogin), { 47 | initialRouteName: routesPostLogin.note.name, 48 | navigationOptions: { 49 | tabBarVisible: true, 50 | }, 51 | tabBarOptions: { 52 | activeTintColor: black, 53 | inactiveTintColor: grey2, 54 | style: { 55 | backgroundColor: white, 56 | paddingVertical: scalable(5), 57 | height: scalable(55), 58 | }, 59 | tabStyle: { 60 | flexDirection: 'column', 61 | alignItems: 'center', 62 | justifyContent: 'center', 63 | }, 64 | labelStyle: { 65 | marginLeft: 0, 66 | }, 67 | }, 68 | }) 69 | -------------------------------------------------------------------------------- /frontend/app/mobile/src/setup/routes/postLogin/note.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import {createStackNavigator} from 'react-navigation-stack' 3 | 4 | // App Imports 5 | import {getRoutesForStack} from '../../helpers/utils' 6 | import List from '../../../modules/note/List' 7 | import Detail from '../../../modules/note/Detail' 8 | import Create from '../../../modules/note/Create' 9 | 10 | // Routes 11 | export const routesNote = { 12 | // List 13 | list: { 14 | name: 'note', 15 | path: 'note', 16 | screen: List, 17 | }, 18 | 19 | // Create 20 | create: { 21 | name: 'noteCreate', 22 | path: 'note/create', 23 | screen: Create, 24 | }, 25 | 26 | // Detail 27 | detail: { 28 | name: 'noteDetail', 29 | path: 'note/detail', 30 | screen: Detail, 31 | }, 32 | } 33 | 34 | export default createStackNavigator(getRoutesForStack(routesNote), { 35 | initialRouteName: routesNote.list.name, // Initial route name 36 | headerMode: 'none', 37 | navigationOptions: { 38 | headerVisible: false, 39 | }, 40 | }) 41 | -------------------------------------------------------------------------------- /frontend/app/mobile/src/setup/routes/postLogin/user.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import {createStackNavigator} from 'react-navigation-stack' 3 | 4 | // App Imports 5 | import {getRoutesForStack} from '../../helpers/utils' 6 | import Profile from '../../../modules/user/Profile' 7 | import Help from '../../../modules/pages/Help' 8 | 9 | // Routes 10 | export const routesUser = { 11 | // Profile 12 | user: { 13 | name: 'user', 14 | path: 'user', 15 | screen: Profile, 16 | }, 17 | 18 | // Help 19 | help: { 20 | name: 'help', 21 | path: 'help', 22 | screen: Help, 23 | }, 24 | } 25 | 26 | export default createStackNavigator(getRoutesForStack(routesUser), { 27 | initialRouteName: routesUser.user.name, // Initial route name 28 | headerMode: 'none', 29 | navigationOptions: { 30 | headerVisible: false, 31 | }, 32 | }) 33 | -------------------------------------------------------------------------------- /frontend/app/mobile/src/setup/routes/preLogin/index.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import {createStackNavigator} from 'react-navigation-stack' 3 | 4 | // App Imports 5 | import {getRoutesForStack} from '../../helpers/utils' 6 | import Start from '../../../modules/user/Start' 7 | import Signup from '../../../modules/user/Signup' 8 | import Login from '../../../modules/user/Login' 9 | 10 | // Auth routes 11 | export const routesPreLogin = { 12 | start: { 13 | name: 'userStart', 14 | path: 'user/start', 15 | screen: Start, 16 | }, 17 | 18 | signup: { 19 | name: 'userSignup', 20 | path: 'user/signup', 21 | screen: Signup, 22 | }, 23 | 24 | login: { 25 | name: 'userLogin', 26 | path: 'user/login', 27 | screen: Login, 28 | }, 29 | } 30 | 31 | export default createStackNavigator(getRoutesForStack(routesPreLogin), { 32 | initialRouteName: routesPreLogin.start.name, // Initial route name @temp routesPreLogin.start.name 33 | headerMode: 'none', 34 | navigationOptions: { 35 | headerVisible: false, 36 | }, 37 | }) 38 | -------------------------------------------------------------------------------- /frontend/app/mobile/src/setup/store.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import {compose, combineReducers} from 'redux' 3 | import {createStore, applyMiddleware} from 'redux' 4 | import thunk from 'redux-thunk' 5 | 6 | // App Imports 7 | import common from '../modules/common/api/state' 8 | import user from '../modules/user/api/state' 9 | import note from '../modules/note/api/state' 10 | 11 | // Root Reducer 12 | const rootReducer = combineReducers({ 13 | common, 14 | ...user, 15 | ...note, 16 | }) 17 | 18 | // Store 19 | export const store = createStore( 20 | rootReducer, 21 | 22 | compose(applyMiddleware(thunk)), 23 | ) 24 | -------------------------------------------------------------------------------- /frontend/app/mobile/src/setup/translate/en.js: -------------------------------------------------------------------------------- 1 | // App Imports 2 | import common from '../../modules/common/translations/en' 3 | import pages from '../../modules/pages/translations/en' 4 | import user from '../../modules/user/translations/en' 5 | import note from '../../modules/note/translations/en' 6 | 7 | // Translations (English) 8 | export default { 9 | ...common, 10 | ...pages, 11 | ...user, 12 | ...note, 13 | } 14 | -------------------------------------------------------------------------------- /frontend/app/mobile/src/setup/translate/index.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import RNLanguages from 'react-native-languages' 3 | import i18n from 'i18n-js' 4 | 5 | // Languages 6 | import en from './en.js' 7 | 8 | // Translate 9 | i18n.locale = RNLanguages.language 10 | i18n.fallbacks = en 11 | i18n.translations = {en} 12 | 13 | export default i18n 14 | -------------------------------------------------------------------------------- /frontend/app/mobile/src/ui/Toast/index.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import React from 'react' 3 | import PropTypes from 'prop-types' 4 | import {StyleSheet, View, TouchableWithoutFeedback} from 'react-native' 5 | 6 | // UI Imports 7 | import {blockPadding, font, blockMarginHalf} from '../common/responsive' 8 | import {white, positive, negative, black} from '../common/colors' 9 | import Typography from '../Typography' 10 | import stylesCommon from '../common/styles' 11 | import Icon from '../icon/Icon' 12 | 13 | // Component 14 | const Toast = ({onPress, message, success}) => ( 15 | 16 | 17 | 18 | 23 | 24 | 25 | 26 | {message} 27 | 28 | 29 | 30 | ) 31 | 32 | // Component Properties 33 | Toast.propTypes = { 34 | onPress: PropTypes.func, 35 | message: PropTypes.string, 36 | success: PropTypes.bool, 37 | } 38 | 39 | export default Toast 40 | 41 | // Component Styles 42 | const styles = StyleSheet.create({ 43 | container: { 44 | flex: 1, 45 | flexDirection: 'row', 46 | alignItems: 'center', 47 | justifyContent: 'center', 48 | position: 'absolute', 49 | backgroundColor: white, 50 | bottom: 0, 51 | left: 0, 52 | right: 0, 53 | paddingVertical: blockPadding, 54 | paddingHorizontal: blockPadding * 2, 55 | ...stylesCommon.shadowSubtle, 56 | }, 57 | icon: { 58 | marginRight: blockMarginHalf, 59 | }, 60 | text: { 61 | fontSize: font(14), 62 | color: white, 63 | }, 64 | }) 65 | -------------------------------------------------------------------------------- /frontend/app/mobile/src/ui/Typography/index.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import React from 'react' 3 | import PropTypes from 'prop-types' 4 | import {Text} from 'react-native' 5 | 6 | // UI Imports 7 | import {black} from '../common/colors' 8 | import {font} from '../common/responsive' 9 | 10 | const sizes = (s = 12) => { 11 | const sizePredefined = { 12 | h1: 30, 13 | h2: 26, 14 | h3: 20, 15 | h4: 16, 16 | h5: 14, 17 | h6: 12, 18 | h7: 10, 19 | }[s] 20 | 21 | return sizePredefined ? font(sizePredefined) : font(s) 22 | } 23 | 24 | // Component 25 | const Typography = ({ 26 | size, 27 | color, 28 | spacing, 29 | align, 30 | weight, 31 | style = {}, 32 | children, 33 | ...props 34 | }) => ( 35 | 47 | {children} 48 | 49 | ) 50 | 51 | // Component Properties 52 | Typography.propTypes = { 53 | size: PropTypes.any, 54 | color: PropTypes.string, 55 | spacing: PropTypes.number, 56 | align: PropTypes.string, 57 | weight: PropTypes.string, 58 | } 59 | 60 | Typography.defaultProps = { 61 | size: 12, 62 | color: black, 63 | spacing: 0, 64 | align: 'left', 65 | weight: 'normal', 66 | } 67 | 68 | export default Typography 69 | -------------------------------------------------------------------------------- /frontend/app/mobile/src/ui/button/Fab.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import React from 'react' 3 | import PropTypes from 'prop-types' 4 | import {StyleSheet, TouchableOpacity, View} from 'react-native' 5 | 6 | // UI Imports 7 | import {font, scalable} from '../common/responsive' 8 | import {grey1, grey3, white} from '../common/colors' 9 | import Icon from '../../ui/icon/Icon' 10 | import stylesCommon from '../common/styles' 11 | 12 | // Component 13 | const Fab = ({onPress, icon, color, iconColor, size, disabled}) => { 14 | return disabled ? ( 15 | 16 | 17 | 18 | ) : ( 19 | 20 | 25 | 26 | 27 | 28 | ) 29 | } 30 | 31 | // Component Properties 32 | Fab.propTypes = { 33 | icon: PropTypes.string.isRequired, 34 | onPress: PropTypes.func.isRequired, 35 | color: PropTypes.string, 36 | size: PropTypes.number, 37 | disabled: PropTypes.bool, 38 | } 39 | Fab.defaultProps = { 40 | color: white, 41 | iconColor: grey1, 42 | size: 26, 43 | disabled: false, 44 | } 45 | 46 | export default Fab 47 | 48 | // Component Styles 49 | const styles = StyleSheet.create({ 50 | container: { 51 | height: scalable(40), 52 | width: scalable(40), 53 | borderRadius: scalable(20), 54 | alignItems: 'center', 55 | justifyContent: 'center', 56 | }, 57 | }) 58 | -------------------------------------------------------------------------------- /frontend/app/mobile/src/ui/common/colors.js: -------------------------------------------------------------------------------- 1 | // Colors 2 | 3 | // Monochrome 4 | export const transparent = 'transparent' 5 | export const black = '#000000' 6 | export const blackLight = '#222222' 7 | export const grey = '#333333' 8 | export const grey1 = '#606060' 9 | export const grey2 = '#999999' 10 | export const grey3 = '#aaaaaa' 11 | export const grey4 = '#cccccc' 12 | export const grey5 = '#dddddd' 13 | export const grey6 = '#f0f0f0' 14 | export const grey7 = '#fafafa' 15 | export const white = '#ffffff' 16 | 17 | // App 18 | export const primary = '#2196F3' 19 | export const primaryLight = '#64B5F6' 20 | export const secondary = '#FFEB3B' 21 | export const secondaryLight = '#FFF59D' 22 | export const highlight = '#00d907' 23 | export const positive = '#00c407' 24 | export const negative = '#d90012' 25 | 26 | // Custom 27 | export const content = '#fafafa' 28 | 29 | // Opacity (usage: primary + opacityLow) 30 | export const opacitySubtle = 'ee' 31 | export const opacityLow = 'cc' 32 | export const opacityMedium = '66' 33 | export const opacityHigh = '33' 34 | -------------------------------------------------------------------------------- /frontend/app/mobile/src/ui/common/responsive.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import {Dimensions} from 'react-native' 3 | 4 | // Scale 5 | const scaleFactor = 1.4 6 | export const scalable = (size) => 7 | size * (deviceWidth >= breakpointTablet ? scaleFactor : 1) 8 | 9 | // Breakpoints 10 | export const breakpointMobile = 320 11 | export const breakpointTablet = 768 12 | 13 | // Device 14 | export const deviceWidth = Dimensions.get('window').width 15 | export const deviceHeight = Dimensions.get('window').height 16 | 17 | // Margin and Padding 18 | export const blockMargin = 19 | deviceWidth >= breakpointTablet ? 10 * scaleFactor : 10 20 | export const blockMarginHalf = blockMargin / 2 21 | export const blockPadding = 22 | deviceWidth >= breakpointTablet ? 10 * scaleFactor : 10 23 | export const blockPaddingHalf = blockPadding / 2 24 | 25 | // Font 26 | export const font = (size) => 27 | size * (deviceWidth >= breakpointTablet ? scaleFactor : 1) 28 | 29 | // Button 30 | export const buttonPadding = 31 | deviceWidth >= breakpointTablet ? 10 * scaleFactor : 10 32 | export const buttonRadius = 33 | deviceWidth >= breakpointTablet ? 40 * scaleFactor : 40 34 | 35 | // Item 36 | export const itemSpacing = 37 | deviceWidth >= breakpointTablet ? 20 * scaleFactor : 20 38 | export const itemRadius = 39 | deviceWidth >= breakpointTablet ? 10 * scaleFactor : 10 40 | export const itemRadiusHalf = itemRadius / 2 41 | 42 | // Navigation 43 | export const navigationTopHeight = 44 | deviceWidth >= breakpointTablet ? 50 * scaleFactor : 50 45 | -------------------------------------------------------------------------------- /frontend/app/mobile/src/ui/divider/Item.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import React from 'react' 3 | import {View} from 'react-native' 4 | 5 | // UI Imports 6 | import {grey4} from '../common/colors' 7 | 8 | // Component 9 | const DividerItem = ({margin = 0}) => ( 10 | 17 | ) 18 | 19 | export default DividerItem 20 | -------------------------------------------------------------------------------- /frontend/app/mobile/src/ui/icon/ActionIcon.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import React from 'react' 3 | import PropTypes from 'prop-types' 4 | import {StyleSheet, TouchableOpacity, View} from 'react-native' 5 | 6 | // UI Imports 7 | import {font, navigationTopHeight} from '../common/responsive' 8 | import {black, white} from '../common/colors' 9 | import Icon from '../../ui/icon/Icon' 10 | 11 | // Component 12 | const ActionIcon = ({onPress, icon, color, size}) => { 13 | return ( 14 | 15 | 16 | 17 | 18 | 19 | ) 20 | } 21 | 22 | // Component Properties 23 | ActionIcon.propTypes = { 24 | icon: PropTypes.string.isRequired, 25 | onPress: PropTypes.func.isRequired, 26 | color: PropTypes.string, 27 | size: PropTypes.number, 28 | } 29 | ActionIcon.defaultProps = { 30 | color: black, 31 | size: 22, 32 | } 33 | 34 | export default ActionIcon 35 | 36 | // Component Styles 37 | const styles = StyleSheet.create({ 38 | container: { 39 | flex: 1, 40 | height: navigationTopHeight, 41 | width: navigationTopHeight, 42 | alignItems: 'center', 43 | justifyContent: 'center', 44 | }, 45 | }) 46 | -------------------------------------------------------------------------------- /frontend/app/mobile/src/ui/icon/Icon.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import React from 'react' 3 | import PropTypes from 'prop-types' 4 | import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons' 5 | import Zocial from 'react-native-vector-icons/Zocial' 6 | 7 | // Component 8 | const Icon = (props) => { 9 | return props.pack === 'social' ? ( 10 | 11 | ) : ( 12 | 13 | ) 14 | } 15 | 16 | Icon.propTypes = { 17 | pack: PropTypes.string.isRequired, 18 | name: PropTypes.string.isRequired, 19 | size: PropTypes.number, 20 | color: PropTypes.string, 21 | } 22 | 23 | Icon.defaultProps = { 24 | pack: 'material', 25 | } 26 | 27 | export default Icon 28 | -------------------------------------------------------------------------------- /frontend/app/mobile/src/ui/input/Switch.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import React from 'react' 3 | import PropTypes from 'prop-types' 4 | import {StyleSheet, Switch, View} from 'react-native' 5 | 6 | // UI Imports 7 | import {blockMargin, blockMarginHalf, blockPadding} from '../common/responsive' 8 | import {grey1, grey2, grey5, highlight} from '../common/colors' 9 | import Typography from '../Typography' 10 | import {renderIf} from '../../setup/helpers/utils' 11 | 12 | // Component 13 | const InputSwitch = ({ 14 | label, 15 | onChange, 16 | value, 17 | description = '', 18 | trackColor = highlight, 19 | style = {}, 20 | ...props 21 | }) => { 22 | return ( 23 | 24 | 25 | {label.toUpperCase()} 26 | 27 | 28 | 37 | 38 | {renderIf( 39 | description, 40 | 41 | {description} 42 | , 43 | )} 44 | 45 | ) 46 | } 47 | 48 | InputSwitch.propTypes = { 49 | label: PropTypes.string.isRequired, 50 | onChange: PropTypes.func.isRequired, 51 | value: PropTypes.bool, 52 | description: PropTypes.string, 53 | trackColor: PropTypes.string, 54 | style: PropTypes.object, 55 | } 56 | 57 | export default InputSwitch 58 | 59 | // Component Styles 60 | const styles = StyleSheet.create({ 61 | container: { 62 | paddingHorizontal: blockPadding * 2, 63 | paddingVertical: blockPadding * 1.2, 64 | alignItems: 'flex-start', 65 | }, 66 | switch: { 67 | marginTop: blockMargin, 68 | }, 69 | description: { 70 | marginTop: blockMarginHalf, 71 | }, 72 | }) 73 | -------------------------------------------------------------------------------- /frontend/app/mobile/src/ui/input/Text.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import React from 'react' 3 | import {StyleSheet, TextInput} from 'react-native' 4 | 5 | // UI Imports 6 | import {blockMarginHalf, blockPadding, font} from '../common/responsive' 7 | import {grey1, transparent} from '../common/colors' 8 | 9 | // Component 10 | const InputText = ({onSubmitEditing, inputRef, style = {}, ...props}) => { 11 | return ( 12 | { 15 | if (onSubmitEditing) onSubmitEditing() 16 | }} 17 | placeholderTextColor={grey1} 18 | underlineColorAndroid={transparent} 19 | autoCorrect={false} 20 | style={[styles.container, style]} 21 | {...props} 22 | /> 23 | ) 24 | } 25 | 26 | export default InputText 27 | 28 | // Component Styles 29 | const styles = StyleSheet.create({ 30 | container: { 31 | marginVertical: blockMarginHalf, 32 | padding: blockPadding, 33 | fontSize: font(16), 34 | borderWidth: 0, 35 | textAlign: 'center', 36 | }, 37 | }) 38 | -------------------------------------------------------------------------------- /frontend/app/web/.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | build 3 | -------------------------------------------------------------------------------- /frontend/app/web/.env.dev.example: -------------------------------------------------------------------------------- 1 | PORT=5000 2 | REACT_APP_LANDING_URL=http://localhost:3000 3 | REACT_APP_WEB_URL=http://localhost:5000 4 | REACT_APP_API_URL=http://localhost:8000 5 | -------------------------------------------------------------------------------- /frontend/app/web/.env.prod.example: -------------------------------------------------------------------------------- 1 | PORT=5000 2 | REACT_APP_LANDING_URL=http://example.com 3 | REACT_APP_WEB_URL=http://app.example.com 4 | REACT_APP_API_URL=http://api.example.com 5 | -------------------------------------------------------------------------------- /frontend/app/web/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env 17 | .env.local 18 | .env.development.local 19 | .env.test.local 20 | .env.production.local 21 | 22 | npm-debug.log* 23 | yarn-debug.log* 24 | yarn-error.log* 25 | -------------------------------------------------------------------------------- /frontend/app/web/.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | bracketSpacing: true, 3 | jsxBracketSameLine: true, 4 | singleQuote: true, 5 | trailingComma: 'all', 6 | semi: false, 7 | tabWidth: 2, 8 | useTabs: false, 9 | arrowParens: 'always', 10 | jsxSingleQuote: true, 11 | } 12 | -------------------------------------------------------------------------------- /frontend/app/web/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:10 2 | RUN mkdir -p /user/src/app 3 | WORKDIR /user/src/app 4 | COPY ./package*.json ./ 5 | RUN npm install --quiet 6 | RUN npm install -g serve 7 | COPY . ./ 8 | RUN npm run build 9 | EXPOSE 5000 10 | ENTRYPOINT ["serve", "-s", "build", "-p", "5000"] 11 | -------------------------------------------------------------------------------- /frontend/app/web/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "web", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "start": "react-scripts start", 7 | "build": "react-scripts build", 8 | "test": "react-scripts test", 9 | "eject": "react-scripts eject" 10 | }, 11 | "husky": { 12 | "hooks": { 13 | "pre-commit": "pretty-quick --staged" 14 | } 15 | }, 16 | "dependencies": { 17 | "@material-ui/core": "4.11.3", 18 | "@material-ui/icons": "4.11.2", 19 | "axios": "0.21.1", 20 | "lodash": "4.17.21", 21 | "moment": "^2.29.1", 22 | "prop-types": "15.7.2", 23 | "react": "^17.0.1", 24 | "react-dom": "^17.0.1", 25 | "react-redux": "7.2.2", 26 | "react-router-dom": "5.2.0", 27 | "redux": "4.0.5", 28 | "redux-thunk": "2.3.0" 29 | }, 30 | "devDependencies": { 31 | "husky": "^4.3.8", 32 | "prettier": "2.2.1", 33 | "pretty-quick": "^3.1.0", 34 | "react-scripts": "4.0.3" 35 | }, 36 | "eslintConfig": { 37 | "extends": "react-app" 38 | }, 39 | "browserslist": [ 40 | ">0.2%", 41 | "not dead", 42 | "not ie <= 11", 43 | "not op_mini all" 44 | ] 45 | } 46 | -------------------------------------------------------------------------------- /frontend/app/web/public/images/favicon/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atulmy/fullstack-javascript-architecture/8f4fa626bb9d3fe8210ad601698ac166f15f2363/frontend/app/web/public/images/favicon/android-chrome-192x192.png -------------------------------------------------------------------------------- /frontend/app/web/public/images/favicon/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atulmy/fullstack-javascript-architecture/8f4fa626bb9d3fe8210ad601698ac166f15f2363/frontend/app/web/public/images/favicon/apple-touch-icon.png -------------------------------------------------------------------------------- /frontend/app/web/public/images/favicon/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | #da532c 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /frontend/app/web/public/images/favicon/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atulmy/fullstack-javascript-architecture/8f4fa626bb9d3fe8210ad601698ac166f15f2363/frontend/app/web/public/images/favicon/favicon-16x16.png -------------------------------------------------------------------------------- /frontend/app/web/public/images/favicon/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atulmy/fullstack-javascript-architecture/8f4fa626bb9d3fe8210ad601698ac166f15f2363/frontend/app/web/public/images/favicon/favicon-32x32.png -------------------------------------------------------------------------------- /frontend/app/web/public/images/favicon/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atulmy/fullstack-javascript-architecture/8f4fa626bb9d3fe8210ad601698ac166f15f2363/frontend/app/web/public/images/favicon/favicon.ico -------------------------------------------------------------------------------- /frontend/app/web/public/images/favicon/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atulmy/fullstack-javascript-architecture/8f4fa626bb9d3fe8210ad601698ac166f15f2363/frontend/app/web/public/images/favicon/mstile-150x150.png -------------------------------------------------------------------------------- /frontend/app/web/public/images/favicon/site.webmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "name": "", 3 | "short_name": "", 4 | "icons": [ 5 | { 6 | "src": "/android-chrome-192x192.png", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | }, 10 | { 11 | "src": "/android-chrome-512x512.png", 12 | "sizes": "512x512", 13 | "type": "image/png" 14 | } 15 | ], 16 | "theme_color": "#ffffff", 17 | "background_color": "#ffffff", 18 | "display": "standalone" 19 | } 20 | -------------------------------------------------------------------------------- /frontend/app/web/public/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atulmy/fullstack-javascript-architecture/8f4fa626bb9d3fe8210ad601698ac166f15f2363/frontend/app/web/public/images/logo.png -------------------------------------------------------------------------------- /frontend/app/web/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "FSJA", 3 | "name": "Full Stack JavaScript Architecture", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": ".", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /frontend/app/web/src/index.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import React from 'react' 3 | import ReactDOM from 'react-dom' 4 | import { Provider as StateProvider } from 'react-redux' 5 | import { BrowserRouter as Router, Route, Switch } from 'react-router-dom' 6 | import { MuiThemeProvider } from '@material-ui/core/styles' 7 | import isFunction from 'lodash/isFunction' 8 | 9 | // App Imports 10 | import { store } from './setup/store' 11 | import routes from './setup/routes' 12 | import theme from './setup/theme' 13 | import { setUser, setUserLocally } from './modules/user/api/actions/query' 14 | import Layout from './modules/common/Layout' 15 | import Redirector from './modules/common/Redirector' 16 | import RoutePrivate from './modules/auth/RoutePrivate' 17 | import * as serviceWorker from './serviceWorker' 18 | 19 | // User Authentication 20 | const token = window.localStorage.getItem('token') 21 | if (token && token !== 'undefined' && token !== '') { 22 | const user = JSON.parse(window.localStorage.getItem('user')) 23 | if (user) { 24 | // Dispatch action 25 | store.dispatch(setUser(token, user)) 26 | 27 | setUserLocally(token, user) 28 | } 29 | } 30 | 31 | // Render App 32 | ReactDOM.render( 33 | 34 | 35 | 36 | 37 | 38 | {Object.values(routes).map((route, index) => 39 | route.auth ? ( 40 | 45 | ) : ( 46 | 51 | ), 52 | )} 53 | 54 | 55 | 56 | 57 | 58 | 59 | , 60 | document.getElementById('root'), 61 | ) 62 | 63 | // Service Worker 64 | serviceWorker.unregister() 65 | -------------------------------------------------------------------------------- /frontend/app/web/src/modules/admin/dashboard/Dashboard/styles.js: -------------------------------------------------------------------------------- 1 | // UI Imports 2 | import grey from '@material-ui/core/colors/grey' 3 | 4 | // Component Styles 5 | const styles = (theme) => ({ 6 | root: { 7 | padding: theme.spacing(), 8 | backgroundColor: grey[500], 9 | }, 10 | 11 | grow: { 12 | flexGrow: 1, 13 | }, 14 | }) 15 | 16 | export default styles 17 | -------------------------------------------------------------------------------- /frontend/app/web/src/modules/admin/user/List/Item/index.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import React, { PureComponent } from 'react' 3 | import PropTypes from 'prop-types' 4 | import { connect } from 'react-redux' 5 | 6 | // UI Imports 7 | import TableCell from '@material-ui/core/TableCell' 8 | import TableRow from '@material-ui/core/TableRow' 9 | 10 | // App Imports 11 | import { routeImageUser } from '../../../../../setup/routes' 12 | import { messageShow } from '../../../../common/api/actions' 13 | 14 | // Component 15 | class Item extends PureComponent { 16 | render() { 17 | const { user } = this.props 18 | 19 | return ( 20 | 21 | 22 | {user.image ? ( 23 | {user.name} 28 | ) : ( 29 | 'No Image' 30 | )} 31 | 32 | {user.email} 33 | {user.name} 34 | 35 | ) 36 | } 37 | } 38 | 39 | // Component Properties 40 | Item.propTypes = { 41 | user: PropTypes.object.isRequired, 42 | messageShow: PropTypes.func.isRequired, 43 | } 44 | 45 | export default connect(null, { messageShow })(Item) 46 | -------------------------------------------------------------------------------- /frontend/app/web/src/modules/admin/user/List/styles.js: -------------------------------------------------------------------------------- 1 | // Component Styles 2 | const styles = { 3 | toolbar: { 4 | textAlign: 'right', 5 | justifyContent: 'flex-end', 6 | }, 7 | } 8 | 9 | export default styles 10 | -------------------------------------------------------------------------------- /frontend/app/web/src/modules/auth/AuthCheck.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import React from 'react' 3 | import { Redirect } from 'react-router-dom' 4 | import { useSelector } from 'react-redux' 5 | 6 | // App Imports 7 | import params from '../../setup/config/params' 8 | import routes from '../../setup/routes' 9 | 10 | // Component 11 | const AuthCheck = () => { 12 | const { isAuthenticated, details } = useSelector((state) => state.auth) 13 | 14 | return isAuthenticated ? ( 15 | details.role === params.user.roles.admin.key ? ( 16 | 17 | ) : ( 18 | 19 | ) 20 | ) : ( 21 | '' 22 | ) 23 | } 24 | 25 | export default AuthCheck 26 | -------------------------------------------------------------------------------- /frontend/app/web/src/modules/auth/RoutePrivate.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import React from 'react' 3 | import { Route, Redirect } from 'react-router-dom' 4 | import { useSelector } from 'react-redux' 5 | 6 | // App Imports 7 | import userRoutes from '../../setup/routes/user' 8 | 9 | // Component 10 | const RoutePrivate = ({ role, component, ...props }) => { 11 | const { isAuthenticated, details } = useSelector((state) => state.auth) 12 | 13 | return isAuthenticated ? ( 14 | role ? ( 15 | details.role === role ? ( 16 | 17 | ) : ( 18 | 19 | ) 20 | ) : ( 21 | 22 | ) 23 | ) : ( 24 | 25 | ) 26 | } 27 | 28 | export default RoutePrivate 29 | -------------------------------------------------------------------------------- /frontend/app/web/src/modules/common/EmptyMessage/index.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import React from 'react' 3 | import PropTypes from 'prop-types' 4 | 5 | // UI Imports 6 | import Typography from '@material-ui/core/Typography' 7 | import { withStyles } from '@material-ui/core/styles/index' 8 | import styles from './styles' 9 | 10 | // Component 11 | const EmptyMessage = ({ message, classes }) => ( 12 | 16 | {message} 17 | 18 | ) 19 | 20 | // Component Properties 21 | EmptyMessage.propTypes = { 22 | classes: PropTypes.object.isRequired, 23 | message: PropTypes.string, 24 | } 25 | 26 | // Component Default Properties 27 | EmptyMessage.defaultProps = { 28 | message: 'No data to show', 29 | } 30 | 31 | export default withStyles(styles)(EmptyMessage) 32 | -------------------------------------------------------------------------------- /frontend/app/web/src/modules/common/EmptyMessage/styles.js: -------------------------------------------------------------------------------- 1 | // Component Styles 2 | const styles = (theme) => ({ 3 | root: { 4 | padding: theme.spacing(2), 5 | textAlign: 'center', 6 | }, 7 | }) 8 | 9 | export default styles 10 | -------------------------------------------------------------------------------- /frontend/app/web/src/modules/common/Header/styles.js: -------------------------------------------------------------------------------- 1 | // Component Styles 2 | const styles = { 3 | header: { 4 | flexGrow: 1, 5 | }, 6 | flex: { 7 | flexGrow: 1, 8 | }, 9 | } 10 | 11 | export default styles 12 | -------------------------------------------------------------------------------- /frontend/app/web/src/modules/common/Layout/index.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import React from 'react' 3 | import { withRouter } from 'react-router-dom' 4 | 5 | // App Imports 6 | import Header from '../Header' 7 | import Message from '../Message' 8 | 9 | // Component 10 | const Layout = ({ children }) => { 11 | // render 12 | return ( 13 |
14 | {/* Header */} 15 |
16 | 17 | {/* Body */} 18 |
{children}
19 | 20 | {/* Message */} 21 | 22 |
23 | ) 24 | } 25 | 26 | export default withRouter(Layout) 27 | -------------------------------------------------------------------------------- /frontend/app/web/src/modules/common/Loading/index.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import React from 'react' 3 | import PropTypes from 'prop-types' 4 | 5 | // UI Imports 6 | import Fade from '@material-ui/core/Fade' 7 | import CircularProgress from '@material-ui/core/CircularProgress' 8 | import Typography from '@material-ui/core/Typography' 9 | import green from '@material-ui/core/colors/green' 10 | import { withStyles } from '@material-ui/core/styles/index' 11 | import styles from './styles' 12 | 13 | // Component 14 | const Loading = ({ classes, size, message }) => { 15 | return ( 16 | 17 |
18 | 19 | 20 | 21 | {message} 22 | 23 |
24 |
25 | ) 26 | } 27 | 28 | // Component Properties 29 | Loading.propTypes = { 30 | classes: PropTypes.object.isRequired, 31 | size: PropTypes.number.isRequired, 32 | message: PropTypes.string.isRequired, 33 | } 34 | Loading.defaultProps = { 35 | size: 40, 36 | message: 'please wait..', 37 | } 38 | 39 | export default withStyles(styles)(Loading) 40 | -------------------------------------------------------------------------------- /frontend/app/web/src/modules/common/Loading/styles.js: -------------------------------------------------------------------------------- 1 | // Component Styles 2 | const styles = (theme) => ({ 3 | root: { 4 | padding: theme.spacing(2), 5 | textAlign: 'center', 6 | }, 7 | }) 8 | 9 | export default styles 10 | -------------------------------------------------------------------------------- /frontend/app/web/src/modules/common/Message/index.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import React from 'react' 3 | import PropTypes from 'prop-types' 4 | import { useDispatch, useSelector } from 'react-redux' 5 | 6 | // UI Imports 7 | import Snackbar from '@material-ui/core/Snackbar' 8 | import IconButton from '@material-ui/core/IconButton' 9 | import CloseIcon from '@material-ui/icons/Close' 10 | 11 | // App Imports 12 | import { messageHide } from '../api/actions' 13 | 14 | // Component 15 | const Message = ({}) => { 16 | // state 17 | const { message } = useSelector((state) => state.common) 18 | const dispatch = useDispatch() 19 | 20 | const onHideMessage = () => { 21 | dispatch(messageHide()) 22 | } 23 | 24 | // render 25 | return ( 26 | 37 | 38 | , 39 | ]} 40 | /> 41 | ) 42 | } 43 | 44 | // Component Properties 45 | Message.propTypes = { 46 | common: PropTypes.object.isRequired, 47 | } 48 | 49 | export default Message 50 | -------------------------------------------------------------------------------- /frontend/app/web/src/modules/common/Message/styles.js: -------------------------------------------------------------------------------- 1 | // UI Imports 2 | import grey from '@material-ui/core/colors/grey' 3 | 4 | // Component Styles 5 | const styles = (theme) => ({ 6 | root: { 7 | padding: theme.spacing(), 8 | backgroundColor: grey[500], 9 | }, 10 | }) 11 | 12 | export default styles 13 | -------------------------------------------------------------------------------- /frontend/app/web/src/modules/common/Redirector/index.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import React from 'react' 3 | import { Redirect } from 'react-router-dom' 4 | 5 | // App Imports 6 | import routes from '../../../setup/routes/index' 7 | 8 | // Component 9 | const Redirector = ({ path = routes.pagesHome.path }) => 10 | 11 | export default Redirector 12 | -------------------------------------------------------------------------------- /frontend/app/web/src/modules/common/Section/index.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import React from 'react' 3 | import PropTypes from 'prop-types' 4 | 5 | // UI Imports 6 | import { withStyles } from '@material-ui/core/styles/index' 7 | import styles from './styles' 8 | 9 | // Component 10 | const Section = ({ classes, ...props }) => ( 11 |
12 | {props.children} 13 |
14 | ) 15 | 16 | // Component Properties 17 | Section.propTypes = { 18 | classes: PropTypes.object.isRequired, 19 | } 20 | 21 | export default withStyles(styles)(Section) 22 | -------------------------------------------------------------------------------- /frontend/app/web/src/modules/common/Section/styles.js: -------------------------------------------------------------------------------- 1 | // Component Styles 2 | const styles = (theme) => ({ 3 | root: { 4 | paddingLeft: theme.spacing(3), 5 | paddingRight: theme.spacing(3), 6 | }, 7 | }) 8 | 9 | export default styles 10 | -------------------------------------------------------------------------------- /frontend/app/web/src/modules/common/api/actions.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import axios from 'axios/index' 3 | 4 | // API Imports 5 | import { API_URL } from '../../../setup/config/env' 6 | 7 | // Actions Types 8 | export const MESSAGE_SHOW = 'COMMON_MESSAGE_SHOW' 9 | export const MESSAGE_HIDE = 'COMMON_MESSAGE_HIDE' 10 | 11 | export function messageShow(message) { 12 | return { type: MESSAGE_SHOW, message } 13 | } 14 | 15 | export function messageHide() { 16 | return { type: MESSAGE_HIDE } 17 | } 18 | 19 | export function upload(data) { 20 | return (dispatch) => { 21 | return axios.post(`${API_URL}/upload`, data, { 22 | headers: { 23 | 'Content-Type': 'multipart/form-data', 24 | }, 25 | }) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /frontend/app/web/src/modules/common/api/state.js: -------------------------------------------------------------------------------- 1 | // App Imports 2 | import { MESSAGE_SHOW, MESSAGE_HIDE } from './actions' 3 | 4 | // Initial State 5 | export const commonInitialState = { 6 | message: { 7 | text: [], 8 | open: false, 9 | }, 10 | } 11 | 12 | // State 13 | export default (state = commonInitialState, action) => { 14 | switch (action.type) { 15 | case MESSAGE_SHOW: 16 | return { 17 | ...state, 18 | message: { 19 | text: action.message, 20 | open: true, 21 | }, 22 | } 23 | 24 | case MESSAGE_HIDE: 25 | return { 26 | ...state, 27 | message: { 28 | text: [], 29 | open: false, 30 | }, 31 | } 32 | 33 | default: 34 | return state 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /frontend/app/web/src/modules/note/Create/styles.js: -------------------------------------------------------------------------------- 1 | // UI Imports 2 | import grey from '@material-ui/core/colors/grey' 3 | 4 | // Component Styles 5 | const styles = (theme) => ({ 6 | root: { 7 | padding: theme.spacing(), 8 | backgroundColor: grey[500], 9 | }, 10 | 11 | menuButton: { 12 | marginLeft: -12, 13 | }, 14 | 15 | grow: { 16 | flexGrow: 1, 17 | }, 18 | 19 | container: { 20 | padding: theme.spacing(2), 21 | }, 22 | 23 | buttonsContainer: { 24 | textAlign: 'right', 25 | marginTop: theme.spacing(), 26 | }, 27 | }) 28 | 29 | export default styles 30 | -------------------------------------------------------------------------------- /frontend/app/web/src/modules/note/List/Item/index.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import React from 'react' 3 | import PropTypes from 'prop-types' 4 | import moment from 'moment' 5 | 6 | // UI Imports 7 | import Card from '@material-ui/core/Card' 8 | import CardContent from '@material-ui/core/CardContent' 9 | import CardActions from '@material-ui/core/CardActions' 10 | import Typography from '@material-ui/core/Typography' 11 | import Button from '@material-ui/core/Button' 12 | import { withStyles } from '@material-ui/core/styles' 13 | import styles from './styles' 14 | 15 | // App Imports 16 | 17 | // Component 18 | const Item = ({ note: { _id, note, createdAt }, onDelete, classes }) => ( 19 | 20 | 21 | {note} 22 | 23 | 24 | {moment(createdAt).format('YYYY-MM-DD hh:mm a')} 25 | 26 | 27 | 28 | 29 | 32 | 33 | 34 | ) 35 | 36 | // Component Properties 37 | Item.propTypes = { 38 | note: PropTypes.object.isRequired, 39 | onDelete: PropTypes.func.isRequired, 40 | classes: PropTypes.object.isRequired, 41 | } 42 | 43 | export default withStyles(styles)(Item) 44 | -------------------------------------------------------------------------------- /frontend/app/web/src/modules/note/List/Item/styles.js: -------------------------------------------------------------------------------- 1 | // Component Styles 2 | const styles = (theme) => ({ 3 | root: { 4 | marginBottom: theme.spacing(3), 5 | }, 6 | 7 | content: { 8 | paddingBottom: 0, 9 | }, 10 | 11 | note: { 12 | whiteSpace: 'pre-line', 13 | }, 14 | }) 15 | 16 | export default styles 17 | -------------------------------------------------------------------------------- /frontend/app/web/src/modules/note/List/styles.js: -------------------------------------------------------------------------------- 1 | // Component Styles 2 | const styles = (theme) => ({ 3 | grow: { 4 | flexGrow: 1, 5 | }, 6 | }) 7 | 8 | export default styles 9 | -------------------------------------------------------------------------------- /frontend/app/web/src/modules/note/api/actions/cache-keys.js: -------------------------------------------------------------------------------- 1 | export const NOTE_LIST_CACHE = 'CACHE.KEY.NOTE.LIST' 2 | -------------------------------------------------------------------------------- /frontend/app/web/src/modules/note/api/actions/mutation.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import axios from 'axios' 3 | 4 | // App Imports 5 | import { API_URL } from '../../../../setup/config/env' 6 | 7 | // Actions 8 | 9 | // Create 10 | export function create({ note }) { 11 | return axios.post(API_URL, { 12 | operation: 'noteCreate', 13 | params: { note }, 14 | }) 15 | } 16 | 17 | // Delete 18 | export function remove({ noteId }) { 19 | return axios.post(API_URL, { 20 | operation: 'noteDelete', 21 | params: { noteId }, 22 | }) 23 | } 24 | -------------------------------------------------------------------------------- /frontend/app/web/src/modules/note/api/actions/query.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import axios from 'axios' 3 | 4 | // App Imports 5 | import { API_URL } from '../../../../setup/config/env' 6 | import { MESSAGE_SHOW } from '../../../common/api/actions' 7 | import { NOTE_LIST_CACHE } from './cache-keys' 8 | import { LIST_REQUEST, LIST_RESPONSE, LIST_DONE } from './types' 9 | 10 | // Actions 11 | 12 | // Get list 13 | export function list(isLoading = true) { 14 | return async (dispatch) => { 15 | // Caching 16 | try { 17 | const list = JSON.parse(window.localStorage.getItem(NOTE_LIST_CACHE)) 18 | 19 | if (list) { 20 | dispatch({ 21 | type: LIST_RESPONSE, 22 | list, 23 | }) 24 | } else { 25 | dispatch({ 26 | type: LIST_REQUEST, 27 | isLoading, 28 | }) 29 | } 30 | } catch (e) { 31 | dispatch({ 32 | type: LIST_REQUEST, 33 | isLoading, 34 | }) 35 | } 36 | 37 | try { 38 | const { data } = await axios.post(API_URL, { 39 | operation: 'noteList', 40 | fields: ['_id', 'note', 'createdAt'], 41 | }) 42 | 43 | if (!data.success) { 44 | dispatch({ 45 | type: MESSAGE_SHOW, 46 | success: data.success, 47 | message: data.message, 48 | }) 49 | } else { 50 | const list = data.data 51 | 52 | dispatch({ 53 | type: LIST_RESPONSE, 54 | list, 55 | }) 56 | 57 | window.localStorage.setItem(NOTE_LIST_CACHE, JSON.stringify(list)) 58 | } 59 | } catch (error) { 60 | dispatch({ 61 | type: MESSAGE_SHOW, 62 | success: false, 63 | message: error.message, 64 | }) 65 | } finally { 66 | dispatch({ 67 | type: LIST_DONE, 68 | isLoading: false, 69 | }) 70 | } 71 | } 72 | } 73 | 74 | // Get detail 75 | export function detail({ noteId }) { 76 | return (dispatch) => { 77 | return axios.post(API_URL, { 78 | operation: 'noteDetail', 79 | params: { noteId }, 80 | }) 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /frontend/app/web/src/modules/note/api/actions/types.js: -------------------------------------------------------------------------------- 1 | // Actions Types 2 | 3 | // List 4 | export const LIST_REQUEST = 'NOTE/LIST/REQUEST' 5 | export const LIST_RESPONSE = 'NOTE/LIST/RESPONSE' 6 | export const LIST_DONE = 'NOTE/LIST/DONE' 7 | export const LIST_RESET = 'NOTE/LIST/RESET' 8 | -------------------------------------------------------------------------------- /frontend/app/web/src/modules/note/api/state/index.js: -------------------------------------------------------------------------------- 1 | // App Imports 2 | import notes from './list' 3 | 4 | export default { 5 | notes, 6 | } 7 | -------------------------------------------------------------------------------- /frontend/app/web/src/modules/note/api/state/list.js: -------------------------------------------------------------------------------- 1 | // App Imports 2 | import { 3 | LIST_DONE, 4 | LIST_REQUEST, 5 | LIST_RESET, 6 | LIST_RESPONSE, 7 | } from '../actions/types' 8 | 9 | // List 10 | 11 | // Initial State 12 | const notesInitialState = { 13 | isLoading: false, 14 | list: [], 15 | } 16 | 17 | // State 18 | export default (state = notesInitialState, action) => { 19 | switch (action.type) { 20 | case LIST_REQUEST: 21 | return { 22 | ...state, 23 | isLoading: action.isLoading, 24 | } 25 | 26 | case LIST_RESPONSE: 27 | return { 28 | ...state, 29 | list: action.list, 30 | } 31 | 32 | case LIST_DONE: 33 | return { 34 | ...state, 35 | isLoading: false, 36 | } 37 | 38 | case LIST_RESET: 39 | return { ...notesInitialState } 40 | 41 | default: 42 | return state 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /frontend/app/web/src/modules/pages/Home/index.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import React from 'react' 3 | import PropTypes from 'prop-types' 4 | import { Link } from 'react-router-dom' 5 | 6 | // UI Imports 7 | import Toolbar from '@material-ui/core/Toolbar/Toolbar' 8 | import Button from '@material-ui/core/Button/Button' 9 | import Typography from '@material-ui/core/Typography/Typography' 10 | import { withStyles } from '@material-ui/core/styles/index' 11 | import styles from './styles' 12 | 13 | // App Imports 14 | import routes from '../../../setup/routes' 15 | import AuthCheck from '../../auth/AuthCheck' 16 | import Section from '../../common/Section' 17 | 18 | // Component 19 | const Home = ({ classes }) => ( 20 |
21 | 22 | 23 | Home 24 | 25 | 26 | 27 |
28 | Simple note taking application! 29 | 30 | 31 | 34 | 35 | 36 | 37 | 40 | 41 |
42 | 43 | {/* Auth Check */} 44 | 45 |
46 | ) 47 | 48 | // Component Properties 49 | Home.propTypes = { 50 | classes: PropTypes.object.isRequired, 51 | } 52 | 53 | export default withStyles(styles)(Home) 54 | -------------------------------------------------------------------------------- /frontend/app/web/src/modules/pages/Home/styles.js: -------------------------------------------------------------------------------- 1 | // UI Imports 2 | import grey from '@material-ui/core/colors/grey' 3 | 4 | // Component Styles 5 | const styles = (theme) => ({ 6 | root: { 7 | padding: theme.spacing(), 8 | backgroundColor: grey[500], 9 | }, 10 | 11 | grow: { 12 | flexGrow: 1, 13 | }, 14 | }) 15 | 16 | export default styles 17 | -------------------------------------------------------------------------------- /frontend/app/web/src/modules/user/Dashboard/index.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import React from 'react' 3 | import PropTypes from 'prop-types' 4 | 5 | // UI Imports 6 | import Toolbar from '@material-ui/core/Toolbar/Toolbar' 7 | import Typography from '@material-ui/core/Typography/Typography' 8 | import { withStyles } from '@material-ui/core/styles/index' 9 | import styles from './styles' 10 | 11 | // App Imports 12 | import Section from '../../common/Section' 13 | 14 | // Component 15 | const Dashboard = ({ classes }) => ( 16 |
17 | 18 | 19 | Dashboard 20 | 21 | 22 | 23 |
24 | 25 | Classis germanus habena est. Quadra de grandis bromium, imitari rector! 26 | 27 |
28 |
29 | ) 30 | 31 | // Component Properties 32 | Dashboard.propTypes = { 33 | classes: PropTypes.object.isRequired, 34 | } 35 | 36 | export default withStyles(styles)(Dashboard) 37 | -------------------------------------------------------------------------------- /frontend/app/web/src/modules/user/Dashboard/styles.js: -------------------------------------------------------------------------------- 1 | // Component Styles 2 | const styles = (theme) => ({ 3 | grow: { 4 | flexGrow: 1, 5 | }, 6 | }) 7 | 8 | export default styles 9 | -------------------------------------------------------------------------------- /frontend/app/web/src/modules/user/Login/styles.js: -------------------------------------------------------------------------------- 1 | // Component Styles 2 | const styles = (theme) => ({ 3 | root: { 4 | marginTop: '10%', 5 | marginLeft: theme.spacing(3), 6 | marginRight: theme.spacing(3), 7 | }, 8 | 9 | container: { 10 | padding: theme.spacing(2), 11 | maxWidth: 400, 12 | margin: '0 auto', 13 | backgroundColor: 'white', 14 | }, 15 | 16 | buttonsContainer: { 17 | textAlign: 'right', 18 | marginTop: theme.spacing(), 19 | }, 20 | 21 | heading: { 22 | marginBottom: theme.spacing(), 23 | textAlign: 'center', 24 | }, 25 | }) 26 | 27 | export default styles 28 | -------------------------------------------------------------------------------- /frontend/app/web/src/modules/user/Profile/index.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import React from 'react' 3 | import PropTypes from 'prop-types' 4 | import { useSelector } from 'react-redux' 5 | 6 | // UI Imports 7 | import Toolbar from '@material-ui/core/Toolbar/Toolbar' 8 | import Typography from '@material-ui/core/Typography/Typography' 9 | import { withStyles } from '@material-ui/core/styles/index' 10 | import styles from './styles' 11 | 12 | // App Imports 13 | import Section from '../../common/Section' 14 | 15 | // Component 16 | const Profile = ({ classes }) => { 17 | // state 18 | const { details } = useSelector((state) => state.auth) 19 | 20 | return ( 21 |
22 | 23 | 24 | Profile 25 | 26 | 27 | 28 |
29 | 30 | Sunt consiliumes convertam nobilis, neuter cobaltumes. 31 | 32 | 33 | Name: {details.name} 34 | Email: {details.email} 35 |
36 |
37 | ) 38 | } 39 | 40 | // Component Properties 41 | Profile.propTypes = { 42 | classes: PropTypes.object.isRequired, 43 | } 44 | 45 | export default withStyles(styles)(Profile) 46 | -------------------------------------------------------------------------------- /frontend/app/web/src/modules/user/Profile/styles.js: -------------------------------------------------------------------------------- 1 | // Component Styles 2 | const styles = (theme) => ({ 3 | grow: { 4 | flexGrow: 1, 5 | }, 6 | }) 7 | 8 | export default styles 9 | -------------------------------------------------------------------------------- /frontend/app/web/src/modules/user/Signup/styles.js: -------------------------------------------------------------------------------- 1 | // Component Styles 2 | const styles = (theme) => ({ 3 | root: { 4 | marginTop: '10%', 5 | marginLeft: theme.spacing(3), 6 | marginRight: theme.spacing(3), 7 | }, 8 | 9 | container: { 10 | padding: theme.spacing(2), 11 | maxWidth: 400, 12 | margin: '0 auto', 13 | backgroundColor: 'white', 14 | }, 15 | 16 | buttonsContainer: { 17 | textAlign: 'right', 18 | marginTop: theme.spacing(), 19 | }, 20 | 21 | heading: { 22 | marginBottom: theme.spacing(), 23 | textAlign: 'center', 24 | }, 25 | }) 26 | 27 | export default styles 28 | -------------------------------------------------------------------------------- /frontend/app/web/src/modules/user/api/actions/cache-keys.js: -------------------------------------------------------------------------------- 1 | export const USER_LIST_CACHE = 'CACHE.KEY.USER.LIST' 2 | -------------------------------------------------------------------------------- /frontend/app/web/src/modules/user/api/actions/mutation.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import axios from 'axios' 3 | import isEmpty from 'lodash/isEmpty' 4 | 5 | // App Imports 6 | import { API_URL } from '../../../../setup/config/env' 7 | 8 | // Signup 9 | export function signup({ name, email, password, passwordRepeat }) { 10 | return axios.post(API_URL, { 11 | operation: 'userSignup', 12 | params: { name, email, password, passwordRepeat }, 13 | }) 14 | } 15 | 16 | // Create or update 17 | export function createOrUpdate(user) { 18 | if (!isEmpty(user.id)) { 19 | return update(user) 20 | } else { 21 | delete user.id 22 | return create(user) 23 | } 24 | } 25 | 26 | // Create 27 | export function create(user) { 28 | return (dispatch) => { 29 | return axios.post(API_URL, { 30 | operation: 'userCreate', 31 | params: user, 32 | }) 33 | } 34 | } 35 | 36 | // Update 37 | export function update(user) { 38 | return (dispatch) => { 39 | return axios.post(API_URL, { 40 | operation: 'userUpdate', 41 | params: user, 42 | }) 43 | } 44 | } 45 | 46 | // Remove 47 | export function remove(data) { 48 | return (dispatch) => { 49 | return axios.post(API_URL, { 50 | operation: 'userRemove', 51 | params: data, 52 | }) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /frontend/app/web/src/modules/user/api/actions/types.js: -------------------------------------------------------------------------------- 1 | // Actions Types 2 | 3 | // Auth 4 | export const LOGIN_REQUEST = 'AUTH/LOGIN_REQUEST' 5 | export const LOGIN_RESPONSE = 'AUTH/LOGIN_RESPONSE' 6 | export const SET_USER = 'AUTH/SET_USER' 7 | export const LOGOUT = 'AUTH/LOGOUT' 8 | 9 | // List 10 | export const LIST_REQUEST = 'USER/LIST/REQUEST' 11 | export const LIST_RESPONSE = 'USER/LIST/RESPONSE' 12 | export const LIST_DONE = 'USER/LIST/DONE' 13 | export const LIST_RESET = 'USER/LIST/RESET' 14 | export const LIST_FILTER = 'USER/LIST/FILTER' 15 | -------------------------------------------------------------------------------- /frontend/app/web/src/modules/user/api/state/auth.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import isEmpty from 'lodash/isEmpty' 3 | 4 | // App Imports 5 | import { 6 | SET_USER, 7 | LOGIN_REQUEST, 8 | LOGIN_RESPONSE, 9 | LOGOUT, 10 | } from '../actions/types' 11 | 12 | // Auth (user) 13 | 14 | // Initial State 15 | 16 | export const authInitialState = { 17 | error: null, 18 | isLoading: false, 19 | isAuthenticated: false, 20 | details: null, 21 | } 22 | 23 | // State 24 | export default (state = authInitialState, action) => { 25 | switch (action.type) { 26 | case SET_USER: 27 | return { 28 | ...state, 29 | isAuthenticated: !isEmpty(action.user), 30 | details: action.user, 31 | } 32 | 33 | case LOGIN_REQUEST: 34 | return { 35 | ...state, 36 | error: null, 37 | isLoading: action.isLoading, 38 | } 39 | 40 | case LOGIN_RESPONSE: 41 | return { 42 | ...state, 43 | error: action.error, 44 | isLoading: false, 45 | } 46 | 47 | case LOGOUT: 48 | return authInitialState 49 | 50 | default: 51 | return state 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /frontend/app/web/src/modules/user/api/state/index.js: -------------------------------------------------------------------------------- 1 | // App Imports 2 | import auth from './auth' 3 | import users from './list' 4 | 5 | export default { 6 | auth, 7 | users, 8 | } 9 | -------------------------------------------------------------------------------- /frontend/app/web/src/modules/user/api/state/list.js: -------------------------------------------------------------------------------- 1 | // App Imports 2 | import { 3 | LIST_DONE, 4 | LIST_FILTER, 5 | LIST_REQUEST, 6 | LIST_RESET, 7 | LIST_RESPONSE, 8 | } from '../actions/types' 9 | 10 | // User list (users) 11 | 12 | // Initial State 13 | const usersInitialState = { 14 | isLoading: false, 15 | list: [], 16 | } 17 | 18 | // State 19 | export default (state = usersInitialState, action) => { 20 | switch (action.type) { 21 | case LIST_REQUEST: 22 | return { 23 | ...state, 24 | isLoading: action.isLoading, 25 | } 26 | 27 | case LIST_RESPONSE: 28 | return { 29 | ...state, 30 | list: action.list, 31 | } 32 | 33 | case LIST_DONE: 34 | return { 35 | ...state, 36 | isLoading: false, 37 | } 38 | 39 | case LIST_RESET: 40 | return { ...usersInitialState } 41 | 42 | case LIST_FILTER: 43 | return { 44 | ...state, 45 | list: action.list, 46 | } 47 | 48 | default: 49 | return state 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /frontend/app/web/src/setup/config/env.js: -------------------------------------------------------------------------------- 1 | // Configurations 2 | 3 | // URL 4 | export const LANDING_URL = process.env.REACT_APP_LANDING_URL 5 | export const WEB_URL = process.env.REACT_APP_WEB_URL 6 | export const API_URL = process.env.REACT_APP_API_URL 7 | -------------------------------------------------------------------------------- /frontend/app/web/src/setup/config/params.json: -------------------------------------------------------------------------------- 1 | { 2 | "site": { 3 | "name": "Example" 4 | }, 5 | 6 | "folders": { 7 | "public": "public/" 8 | }, 9 | 10 | "user": { 11 | "roles": { 12 | "admin": { 13 | "key": "admin", 14 | "title": "Admin" 15 | }, 16 | "user": { 17 | "key": "user", 18 | "title": "User" 19 | } 20 | }, 21 | "uploads": { 22 | "path": "images/user" 23 | } 24 | }, 25 | 26 | "import": { 27 | "uploads": { 28 | "path": "import" 29 | } 30 | }, 31 | 32 | "image": { 33 | "default": "default.jpg" 34 | }, 35 | 36 | "date": { 37 | "format": { 38 | "date": "YYYY-MM-DD", 39 | "time": "HH:mm", 40 | "nice": { 41 | "date": "Do MMM", 42 | "time": "h:mm A" 43 | } 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /frontend/app/web/src/setup/helpers.js: -------------------------------------------------------------------------------- 1 | // Helpers 2 | 3 | // Render element or component by provided condition 4 | export function renderIf(condition, renderFn) { 5 | return condition ? renderFn() : null 6 | } 7 | 8 | // Substring with ... 9 | export function subString(string = '', length = 0) { 10 | return string.length > length ? `${string.substr(0, length)}...` : string 11 | } 12 | 13 | // Return empty string if value is null 14 | export function nullToEmptyString(value) { 15 | return value || '' 16 | } 17 | 18 | // Return zero if value is null 19 | export function nullToZero(value) { 20 | return value === null ? 0 : value 21 | } 22 | 23 | // Add (s) to any string by count 24 | export function plural(value) { 25 | return value === 1 ? '' : 's' 26 | } 27 | -------------------------------------------------------------------------------- /frontend/app/web/src/setup/routes/admin/dashboard.js: -------------------------------------------------------------------------------- 1 | // App Imports 2 | import params from '../../../setup/config/params' 3 | import Dashboard from '../../../modules/admin/dashboard/Dashboard' 4 | 5 | // Admin dashboard routes 6 | export default { 7 | adminDashboard: { 8 | path: '/admin/dashboard', 9 | component: Dashboard, 10 | auth: true, 11 | role: params.user.roles.admin.key, 12 | }, 13 | } 14 | -------------------------------------------------------------------------------- /frontend/app/web/src/setup/routes/admin/index.js: -------------------------------------------------------------------------------- 1 | // App Imports 2 | import dashboard from './dashboard' 3 | import user from './user' 4 | 5 | // Admin routes 6 | export default { 7 | ...dashboard, 8 | ...user, 9 | } 10 | -------------------------------------------------------------------------------- /frontend/app/web/src/setup/routes/admin/user.js: -------------------------------------------------------------------------------- 1 | // App Imports 2 | import params from '../../config/params' 3 | import UserList from '../../../modules/admin/user/List' 4 | 5 | // Admin user routes 6 | export default { 7 | adminUserList: { 8 | path: '/admin/users', 9 | component: UserList, 10 | auth: true, 11 | role: params.user.roles.admin.key, 12 | }, 13 | } 14 | -------------------------------------------------------------------------------- /frontend/app/web/src/setup/routes/index.js: -------------------------------------------------------------------------------- 1 | // App Imports 2 | import { API_URL } from '../config/env' 3 | import params from '../config/params' 4 | import pages from './pages' 5 | import user from './user' 6 | import note from './note' 7 | import admin from './admin' 8 | 9 | // Image 10 | export const routeImageUser = `${API_URL}/${params.user.uploads.path}/` 11 | 12 | // Combined routes 13 | const routes = { 14 | ...pages, 15 | ...user, 16 | ...note, 17 | ...admin, 18 | } 19 | 20 | export default routes 21 | -------------------------------------------------------------------------------- /frontend/app/web/src/setup/routes/note.js: -------------------------------------------------------------------------------- 1 | // App Imports 2 | import List from '../../modules/note/List' 3 | import Create from '../../modules/note/Create' 4 | 5 | // Pages routes 6 | export default { 7 | noteList: { 8 | path: '/note/list', 9 | component: List, 10 | auth: true, 11 | }, 12 | 13 | noteCreate: { 14 | path: '/note/create', 15 | component: Create, 16 | auth: true, 17 | }, 18 | } 19 | -------------------------------------------------------------------------------- /frontend/app/web/src/setup/routes/pages.js: -------------------------------------------------------------------------------- 1 | // App Imports 2 | import Home from '../../modules/pages/Home' 3 | 4 | // Pages routes 5 | export default { 6 | pagesHome: { 7 | path: '/', 8 | component: Home, 9 | exact: true, 10 | }, 11 | } 12 | -------------------------------------------------------------------------------- /frontend/app/web/src/setup/routes/user.js: -------------------------------------------------------------------------------- 1 | // App Imports 2 | import Login from '../../modules/user/Login' 3 | import Signup from '../../modules/user/Signup' 4 | import Profile from '../../modules/user/Profile' 5 | import Dashboard from '../../modules/user/Dashboard' 6 | 7 | // Pages routes 8 | export default { 9 | userLogin: { 10 | path: '/user/login', 11 | component: Login, 12 | }, 13 | 14 | userSignup: { 15 | path: '/user/signup', 16 | component: Signup, 17 | }, 18 | 19 | userProfile: { 20 | path: '/user/profile', 21 | component: Profile, 22 | auth: true, 23 | }, 24 | 25 | userDashboard: { 26 | path: '/user/dashboard', 27 | component: Dashboard, 28 | auth: true, 29 | }, 30 | } 31 | -------------------------------------------------------------------------------- /frontend/app/web/src/setup/store.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import { createStore, combineReducers, applyMiddleware } from 'redux' 3 | import thunk from 'redux-thunk' 4 | 5 | // App Imports 6 | import common from '../modules/common/api/state' 7 | import user from '../modules/user/api/state' 8 | import note from '../modules/note/api/state' 9 | 10 | // Root Reducer 11 | const rootReducer = combineReducers({ 12 | common, 13 | ...user, 14 | ...note, 15 | }) 16 | 17 | // Store 18 | export const store = createStore(rootReducer, applyMiddleware(thunk)) 19 | -------------------------------------------------------------------------------- /frontend/app/web/src/setup/theme.js: -------------------------------------------------------------------------------- 1 | // UI Imports 2 | import { createMuiTheme } from '@material-ui/core/styles' 3 | import blue from '@material-ui/core/colors/blue' 4 | import yellow from '@material-ui/core/colors/yellow' 5 | 6 | export default createMuiTheme({ 7 | palette: { 8 | primary: blue, 9 | secondary: yellow, 10 | }, 11 | typography: { 12 | useNextVariants: true, 13 | }, 14 | }) 15 | -------------------------------------------------------------------------------- /frontend/landing/.env.development: -------------------------------------------------------------------------------- 1 | NEXT_PUBLIC_URL_API=http://localhost:8000 2 | NEXT_PUBLIC_URL_WEB=http://localhost:5000 3 | NEXT_PUBLIC_URL_LANDING=http://localhost:3000 4 | 5 | NEXT_PUBLIC_GOOGLE_ANALYTICS= 6 | -------------------------------------------------------------------------------- /frontend/landing/.env.production: -------------------------------------------------------------------------------- 1 | NEXT_PUBLIC_URL_API=http://api.example.com 2 | NEXT_PUBLIC_URL_WEB=http://app.example.com 3 | NEXT_PUBLIC_URL_LANDING=http://example.com 4 | 5 | NEXT_PUBLIC_GOOGLE_ANALYTICS= 6 | -------------------------------------------------------------------------------- /frontend/landing/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # local env files 28 | .env.local 29 | .env.development.local 30 | .env.test.local 31 | .env.production.local 32 | 33 | # vercel 34 | .vercel 35 | -------------------------------------------------------------------------------- /frontend/landing/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "all", 3 | "semi": false, 4 | "tabWidth": 2, 5 | "useTabs": false, 6 | "arrowParens": "always", 7 | "singleQuote": true, 8 | "jsxSingleQuote": true 9 | } 10 | -------------------------------------------------------------------------------- /frontend/landing/.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | bracketSpacing: true, 3 | jsxBracketSameLine: true, 4 | singleQuote: true, 5 | trailingComma: 'all', 6 | semi: false, 7 | tabWidth: 2, 8 | useTabs: false, 9 | arrowParens: 'always', 10 | jsxSingleQuote: true, 11 | } 12 | -------------------------------------------------------------------------------- /frontend/landing/README.md: -------------------------------------------------------------------------------- 1 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). 2 | 3 | ## Getting Started 4 | 5 | First, run the development server: 6 | 7 | ```bash 8 | npm run dev 9 | # or 10 | yarn dev 11 | ``` 12 | 13 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. 14 | 15 | You can start editing the page by modifying `pages/index.js`. The page auto-updates as you edit the file. 16 | 17 | [API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.js`. 18 | 19 | The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages. 20 | 21 | ## Learn More 22 | 23 | To learn more about Next.js, take a look at the following resources: 24 | 25 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. 26 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. 27 | 28 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! 29 | 30 | ## Deploy on Vercel 31 | 32 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/import?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. 33 | 34 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. 35 | -------------------------------------------------------------------------------- /frontend/landing/jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": "src" 4 | }, 5 | "include": ["src"] 6 | } 7 | -------------------------------------------------------------------------------- /frontend/landing/next.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | devIndicators: { 3 | autoPrerender: false, 4 | }, 5 | poweredByHeader: false, 6 | } 7 | -------------------------------------------------------------------------------- /frontend/landing/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "landing", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "build": "next build", 7 | "start": "next dev -p 3000", 8 | "start:prod": "next start -p 3000" 9 | }, 10 | "husky": { 11 | "hooks": { 12 | "pre-commit": "pretty-quick --staged" 13 | } 14 | }, 15 | "dependencies": { 16 | "@material-ui/core": "^4.11.3", 17 | "@material-ui/icons": "^4.11.2", 18 | "@material-ui/styles": "^4.11.3", 19 | "axios": "^0.21.1", 20 | "next": "10.0.7", 21 | "nprogress": "^0.2.0", 22 | "react": "17.0.1", 23 | "react-dom": "17.0.1", 24 | "react-ga": "^3.3.0" 25 | }, 26 | "devDependencies": { 27 | "husky": "^4.3.8", 28 | "prettier": "2.2.1", 29 | "pretty-quick": "^3.1.0" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /frontend/landing/public/images/favicon/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atulmy/fullstack-javascript-architecture/8f4fa626bb9d3fe8210ad601698ac166f15f2363/frontend/landing/public/images/favicon/android-chrome-192x192.png -------------------------------------------------------------------------------- /frontend/landing/public/images/favicon/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atulmy/fullstack-javascript-architecture/8f4fa626bb9d3fe8210ad601698ac166f15f2363/frontend/landing/public/images/favicon/apple-touch-icon.png -------------------------------------------------------------------------------- /frontend/landing/public/images/favicon/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | #da532c 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /frontend/landing/public/images/favicon/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atulmy/fullstack-javascript-architecture/8f4fa626bb9d3fe8210ad601698ac166f15f2363/frontend/landing/public/images/favicon/favicon-16x16.png -------------------------------------------------------------------------------- /frontend/landing/public/images/favicon/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atulmy/fullstack-javascript-architecture/8f4fa626bb9d3fe8210ad601698ac166f15f2363/frontend/landing/public/images/favicon/favicon-32x32.png -------------------------------------------------------------------------------- /frontend/landing/public/images/favicon/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atulmy/fullstack-javascript-architecture/8f4fa626bb9d3fe8210ad601698ac166f15f2363/frontend/landing/public/images/favicon/favicon.ico -------------------------------------------------------------------------------- /frontend/landing/public/images/favicon/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atulmy/fullstack-javascript-architecture/8f4fa626bb9d3fe8210ad601698ac166f15f2363/frontend/landing/public/images/favicon/mstile-150x150.png -------------------------------------------------------------------------------- /frontend/landing/public/images/favicon/site.webmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "name": "", 3 | "short_name": "", 4 | "icons": [ 5 | { 6 | "src": "/android-chrome-192x192.png", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | }, 10 | { 11 | "src": "/android-chrome-512x512.png", 12 | "sizes": "512x512", 13 | "type": "image/png" 14 | } 15 | ], 16 | "theme_color": "#ffffff", 17 | "background_color": "#ffffff", 18 | "display": "standalone" 19 | } 20 | -------------------------------------------------------------------------------- /frontend/landing/public/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atulmy/fullstack-javascript-architecture/8f4fa626bb9d3fe8210ad601698ac166f15f2363/frontend/landing/public/images/logo.png -------------------------------------------------------------------------------- /frontend/landing/public/reset.css: -------------------------------------------------------------------------------- 1 | html { 2 | box-sizing: border-box; 3 | -webkit-font-smoothing: antialiased; 4 | -moz-osx-font-smoothing: grayscale; 5 | font-family: "Roboto", "Helvetica", "Arial", sans-serif; 6 | } 7 | *, *::before, *::after { 8 | box-sizing: inherit; 9 | } 10 | body { 11 | margin: 0; 12 | background-color: #fafafa; 13 | } 14 | a { 15 | color: inherit; 16 | text-decoration: none; 17 | } -------------------------------------------------------------------------------- /frontend/landing/public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | 3 | -------------------------------------------------------------------------------- /frontend/landing/src/modules/common/Header/styles.js: -------------------------------------------------------------------------------- 1 | // Component Styles 2 | const styles = { 3 | root: { 4 | flexGrow: 1, 5 | }, 6 | flex: { 7 | flexGrow: 1, 8 | }, 9 | } 10 | 11 | export default styles 12 | -------------------------------------------------------------------------------- /frontend/landing/src/modules/common/Layout/index.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import React from 'react' 3 | import Head from 'next/head' 4 | 5 | // App Imports 6 | import { URL_LANDING } from 'setup/config/env' 7 | import params from 'setup/config/params' 8 | import Header from 'modules/common/header' 9 | 10 | // Component 11 | const Layout = ({ children }) => { 12 | return ( 13 |
14 | {/* Meta tags */} 15 | 16 | {params.site.title} 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 30 | 31 | 32 | 33 | 34 | {/* Header */} 35 |
36 | 37 | {/* Body */} 38 |
{children}
39 |
40 | ) 41 | } 42 | 43 | export default Layout 44 | -------------------------------------------------------------------------------- /frontend/landing/src/modules/common/Scroll/index.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import React, { PureComponent } from 'react' 3 | import { withRouter } from 'react-router-dom' 4 | 5 | // Component 6 | class Index extends PureComponent { 7 | componentDidUpdate(prevProps) { 8 | if (this.props.location !== prevProps.location) { 9 | window.scrollTo(0, 0) 10 | } 11 | } 12 | 13 | render() { 14 | return this.props.children 15 | } 16 | } 17 | 18 | export default withRouter(Index) 19 | -------------------------------------------------------------------------------- /frontend/landing/src/modules/common/Section/index.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import React from 'react' 3 | import PropTypes from 'prop-types' 4 | 5 | // UI Imports 6 | import { withStyles } from '@material-ui/core/styles/index' 7 | import styles from './styles' 8 | 9 | // Component 10 | const Section = ({ classes, ...props }) => ( 11 |
12 | {props.children} 13 |
14 | ) 15 | 16 | // Component Properties 17 | Section.propTypes = { 18 | classes: PropTypes.object.isRequired, 19 | } 20 | 21 | export default withStyles(styles)(Section) 22 | -------------------------------------------------------------------------------- /frontend/landing/src/modules/common/Section/styles.js: -------------------------------------------------------------------------------- 1 | // Component Styles 2 | const styles = (theme) => ({ 3 | root: { 4 | padding: theme.spacing(3), 5 | }, 6 | }) 7 | 8 | export default styles 9 | -------------------------------------------------------------------------------- /frontend/landing/src/pages/404/index.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import React from 'react' 3 | import Link from 'next/link' 4 | 5 | // UI Imports 6 | import Typography from '@material-ui/core/Typography' 7 | import Button from '@material-ui/core/Button' 8 | import { withStyles } from '@material-ui/core/styles/index' 9 | import styles from './styles' 10 | 11 | // App Imports 12 | import routes from 'setup/routes' 13 | import Section from 'modules/common/section' 14 | 15 | // Component 16 | const NotFound = ({ classes }) => ( 17 |
18 | 19 | Its a 404 20 | 21 | 22 | 23 | The page you are looking for does not exists or has been removed. 24 | 25 | 26 | 27 | 34 | 35 |
36 | ) 37 | 38 | export default withStyles(styles)(NotFound) 39 | -------------------------------------------------------------------------------- /frontend/landing/src/pages/404/styles.js: -------------------------------------------------------------------------------- 1 | // Component Styles 2 | const styles = (theme) => ({ 3 | button: { 4 | marginTop: theme.spacing(2), 5 | }, 6 | }) 7 | 8 | export default styles 9 | -------------------------------------------------------------------------------- /frontend/landing/src/pages/_document.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import React from 'react' 3 | import Document, { Html, Head, Main, NextScript } from 'next/document' 4 | 5 | // UI imports 6 | import { ServerStyleSheets } from '@material-ui/core/styles' 7 | 8 | // Document 9 | class CustomDocument extends Document { 10 | static async getInitialProps(ctx) { 11 | const initialProps = await Document.getInitialProps(ctx) 12 | return { ...initialProps } 13 | } 14 | 15 | render() { 16 | return ( 17 | 18 | 19 | 20 |
21 | 22 | 23 | 24 | ) 25 | } 26 | } 27 | 28 | CustomDocument.getInitialProps = async (ctx) => { 29 | // Render app and page and get the context of the page with collected side effects. 30 | const sheets = new ServerStyleSheets() 31 | const originalRenderPage = ctx.renderPage 32 | 33 | ctx.renderPage = () => 34 | originalRenderPage({ 35 | enhanceApp: (App) => (props) => sheets.collect(), 36 | }) 37 | 38 | const initialProps = await Document.getInitialProps(ctx) 39 | 40 | return { 41 | ...initialProps, 42 | // Styles fragment is rendered after the app and page rendering finish. 43 | styles: [ 44 | ...React.Children.toArray(initialProps.styles), 45 | sheets.getStyleElement(), 46 | ], 47 | } 48 | } 49 | 50 | export default CustomDocument 51 | -------------------------------------------------------------------------------- /frontend/landing/src/pages/contact/index.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import React from 'react' 3 | import Head from 'next/head' 4 | 5 | // UI Imports 6 | import Typography from '@material-ui/core/Typography' 7 | 8 | // App Imports 9 | import params from 'setup/config/params' 10 | import Layout from 'modules/common/layout' 11 | import Section from 'modules/common/section' 12 | 13 | // Component 14 | const Contact = () => ( 15 | 16 |
17 | {/* Meta tags */} 18 | 19 | Contact 20 | 21 | 22 | {/* Content */} 23 | Contact 24 | 25 |

Bromium potus, omnes urbses imperium talis, regius particulaes.

26 | 27 |
28 | {params.site.name},
29 | {params.site.contact.address}.
30 | {params.site.contact.phone} 31 |
32 | {params.site.contact.email} 33 |
34 |
35 |
36 | ) 37 | 38 | export default Contact 39 | -------------------------------------------------------------------------------- /frontend/landing/src/pages/home/styles.js: -------------------------------------------------------------------------------- 1 | // Component Styles 2 | const styles = (theme) => ({ 3 | hero: { 4 | maxWidth: 600, 5 | margin: '0 auto', 6 | padding: `${theme.spacing(8)}px 0 ${theme.spacing(6)}px`, 7 | }, 8 | heroButtons: { 9 | marginRight: theme.spacing(4), 10 | }, 11 | 12 | iconMobileApp: { 13 | fontSize: 16, 14 | marginRight: theme.spacing(), 15 | }, 16 | 17 | iconWebApp: { 18 | fontSize: 16, 19 | marginLeft: theme.spacing(), 20 | }, 21 | }) 22 | 23 | export default styles 24 | -------------------------------------------------------------------------------- /frontend/landing/src/pages/index.js: -------------------------------------------------------------------------------- 1 | // App imports 2 | export { default } from 'pages/home' 3 | -------------------------------------------------------------------------------- /frontend/landing/src/pages/privacy/styles.js: -------------------------------------------------------------------------------- 1 | // Component Styles 2 | const styles = (theme) => ({ 3 | root: { 4 | padding: theme.spacing(2), 5 | }, 6 | }) 7 | 8 | export default styles 9 | -------------------------------------------------------------------------------- /frontend/landing/src/pages/reset.css: -------------------------------------------------------------------------------- 1 | /* - reset */ 2 | 3 | html { 4 | box-sizing: border-box; 5 | -webkit-font-smoothing: antialiased; 6 | -moz-osx-font-smoothing: grayscale; 7 | -webkit-font-smoothing: antialiased; 8 | -moz-osx-font-smoothing: grayscale; 9 | margin: 0; 10 | padding: 0; 11 | } 12 | *, 13 | *::before, 14 | *::after { 15 | box-sizing: inherit; 16 | } 17 | a { 18 | color: inherit; 19 | text-decoration: none; 20 | } 21 | -------------------------------------------------------------------------------- /frontend/landing/src/pages/terms/styles.js: -------------------------------------------------------------------------------- 1 | // Component Styles 2 | const styles = (theme) => ({ 3 | root: { 4 | padding: theme.spacing(2), 5 | }, 6 | }) 7 | 8 | export default styles 9 | -------------------------------------------------------------------------------- /frontend/landing/src/setup/analytics.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import ReactGA from 'react-ga' 3 | 4 | // App imports 5 | import { GOOGLE_ANALYTICS } from 'setup/config/env' 6 | 7 | // Google Analytics 8 | export const initGA = () => { 9 | ReactGA.initialize(GOOGLE_ANALYTICS) 10 | } 11 | 12 | export const logPageView = () => { 13 | ReactGA.set({ page: window.location.pathname }) 14 | ReactGA.pageview(window.location.pathname + window.location.search) 15 | } 16 | 17 | export const logEvent = (category = '', action = '') => { 18 | if (category && action) { 19 | ReactGA.event({ category, action }) 20 | } 21 | } 22 | 23 | export const logException = (description = '', fatal = false) => { 24 | if (description) { 25 | ReactGA.exception({ description, fatal }) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /frontend/landing/src/setup/config/env.js: -------------------------------------------------------------------------------- 1 | // Configurations 2 | export const ENV = process.env.NODE_ENV 3 | 4 | // URL 5 | export const URL_API = process.env.NEXT_PUBLIC_URL_API 6 | export const URL_WEB = process.env.NEXT_PUBLIC_URL_WEB 7 | export const URL_LANDING = process.env.NEXT_PUBLIC_URL_LANDING 8 | 9 | // Analytics 10 | export const GOOGLE_ANALYTICS = process.env.NEXT_PUBLIC_GOOGLE_ANALYTICS 11 | -------------------------------------------------------------------------------- /frontend/landing/src/setup/config/params.json: -------------------------------------------------------------------------------- 1 | { 2 | "site": { 3 | "version": 0.1, 4 | "name": "Example", 5 | "domain": "example.com", 6 | "url": { 7 | "store": { 8 | "ios": "https://itunes.apple.com/us/app/google-maps-transit-food/id585027354", 9 | "android": "https://play.google.com/store/apps/details?id=com.google.android.apps.maps" 10 | } 11 | }, 12 | "title": "Example - A sample website for fullstack JavaScript architecture.", 13 | "description": "Simple note taking application.", 14 | "keywords": "example, fullstack, javascript, architecture", 15 | "author": "Example", 16 | "copyright": "Example", 17 | "applicationName": "Example", 18 | "copyright_year": 2018, 19 | "image": "logo.png", 20 | "contact": { 21 | "address": "Bangalore, India", 22 | "email": "contact@example.com", 23 | "phone": "+91 98765 43210" 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /frontend/landing/src/setup/helpers.js: -------------------------------------------------------------------------------- 1 | // App imports 2 | import { ENV } from 'setup/config/env' 3 | 4 | // Helpers 5 | 6 | // Check development env 7 | export function isDevelopment() { 8 | return ENV === 'development' 9 | } 10 | 11 | // get site env 12 | export function getSiteEnv() { 13 | return ENV === 'development' ? 'dev' : 'live' 14 | } 15 | -------------------------------------------------------------------------------- /frontend/landing/src/setup/routes.js: -------------------------------------------------------------------------------- 1 | // Imports 2 | import React from 'react' 3 | 4 | // Routes 5 | export const routes = { 6 | pagesHome: { 7 | path: '/', 8 | }, 9 | 10 | pagesContact: { 11 | path: '/contact', 12 | }, 13 | 14 | pagesPrivacy: { 15 | path: '/privacy', 16 | }, 17 | 18 | pagesTerms: { 19 | path: '/terms', 20 | }, 21 | } 22 | 23 | export default Object.values(routes) 24 | -------------------------------------------------------------------------------- /frontend/landing/src/setup/theme.js: -------------------------------------------------------------------------------- 1 | // UI Imports 2 | import { createMuiTheme } from '@material-ui/core/styles' 3 | import blue from '@material-ui/core/colors/blue' 4 | import yellow from '@material-ui/core/colors/yellow' 5 | 6 | const theme = createMuiTheme({ 7 | palette: { 8 | primary: blue, 9 | secondary: yellow, 10 | }, 11 | typography: { 12 | useNextVariants: true, 13 | }, 14 | }) 15 | 16 | export default theme 17 | --------------------------------------------------------------------------------