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 |