nitro-web 0.0.11 → 0.0.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.json +4 -19
- package/_example/.env +1 -1
- package/_example/client/config.ts +2 -1
- package/_example/client/index.ts +6 -24
- package/_example/components/index.tsx +1 -1
- package/_example/package.json +1 -1
- package/_example/server/config.js +6 -7
- package/_example/tailwind.config.js +1 -1
- package/_example/tsconfig.json +10 -2
- package/_example/types.ts +1 -0
- package/client/{app.js → app.tsx} +101 -99
- package/client/globals.ts +42 -0
- package/client/index.ts +52 -0
- package/client/store.ts +31 -0
- package/components/auth/auth.api.js +3 -2
- package/components/auth/{reset.jsx → reset.tsx} +21 -23
- package/components/auth/{signin.jsx → signin.tsx} +14 -16
- package/components/auth/{signup.jsx → signup.tsx} +15 -17
- package/components/billing/stripe.api.js +2 -1
- package/components/dashboard/{dashboard.jsx → dashboard.tsx} +3 -3
- package/components/partials/element/{accordion.jsx → accordion.tsx} +21 -13
- package/components/partials/element/avatar.tsx +40 -0
- package/components/partials/element/{button.jsx → button.tsx} +20 -16
- package/components/partials/element/{dropdown.jsx → dropdown.tsx} +32 -30
- package/components/partials/element/{github-link.jsx → github-link.tsx} +3 -3
- package/components/partials/element/{initials.jsx → initials.tsx} +11 -2
- package/components/partials/element/{message.jsx → message.tsx} +22 -23
- package/components/partials/element/{modal.jsx → modal.tsx} +4 -3
- package/components/partials/element/{sidebar.jsx → sidebar.tsx} +14 -7
- package/components/partials/element/{tooltip.jsx → tooltip.tsx} +11 -3
- package/components/partials/element/{topbar.jsx → topbar.tsx} +9 -7
- package/components/partials/form/{checkbox.jsx → checkbox.tsx} +13 -13
- package/components/partials/form/drop-handler.tsx +68 -0
- package/components/partials/form/{drop.jsx → drop.tsx} +51 -33
- package/components/partials/form/form-error.tsx +27 -0
- package/components/partials/form/{input-color.jsx → input-color.tsx} +27 -15
- package/components/partials/form/{input-currency.jsx → input-currency.tsx} +37 -32
- package/components/partials/form/{input-date.jsx → input-date.tsx} +4 -3
- package/components/partials/form/{input.jsx → input.tsx} +35 -19
- package/components/partials/form/{location.jsx → location.tsx} +21 -8
- package/components/partials/form/{select.jsx → select.tsx} +142 -143
- package/components/partials/form/{toggle.jsx → toggle.tsx} +10 -2
- package/components/partials/{is-first-render.js → is-first-render.ts} +1 -2
- package/components/partials/layout/layout1.tsx +29 -0
- package/components/partials/layout/{layout2.jsx → layout2.tsx} +3 -3
- package/components/partials/{styleguide.jsx → styleguide.tsx} +16 -19
- package/components/settings/{settings-account.jsx → settings-account.tsx} +9 -13
- package/components/settings/{settings-business.jsx → settings-business.tsx} +7 -8
- package/components/settings/{settings-team--member.jsx → settings-team--member.tsx} +4 -11
- package/components/settings/{settings-team.jsx → settings-team.tsx} +4 -8
- package/components/settings/settings.api.js +1 -0
- package/package.json +17 -31
- package/readme.md +1 -1
- package/server/email/index.js +2 -1
- package/server/index.js +1 -0
- package/server/models/company.js +2 -1
- package/server/models/user.js +2 -1
- package/server/router.js +8 -2
- package/tsconfig.json +36 -0
- package/types/required-globals.d.ts +39 -0
- package/types/util.d.ts +12 -2
- package/types/util.d.ts.map +1 -1
- package/types.ts +43 -0
- package/util.js +14 -34
- package/webpack.config.js +23 -4
- package/_example/types/index.d.ts +0 -13
- package/_example/types/twin.d.ts +0 -19
- package/client/index.js +0 -44
- package/components/partials/element/avatar.jsx +0 -31
- package/components/partials/form/drop-handler.jsx +0 -62
- package/components/partials/form/form-error.jsx +0 -21
- package/components/partials/layout/layout1.jsx +0 -38
- package/types/client/app.d.ts +0 -2
- package/types/client/app.d.ts.map +0 -1
- package/types/client/index.d.ts +0 -29
- package/types/client/index.d.ts.map +0 -1
- package/types/components/auth/reset.d.ts +0 -3
- package/types/components/auth/reset.d.ts.map +0 -1
- package/types/components/auth/signin.d.ts +0 -4
- package/types/components/auth/signin.d.ts.map +0 -1
- package/types/components/auth/signup.d.ts +0 -4
- package/types/components/auth/signup.d.ts.map +0 -1
- package/types/components/dashboard/dashboard.d.ts +0 -4
- package/types/components/dashboard/dashboard.d.ts.map +0 -1
- package/types/components/partials/element/accordion.d.ts +0 -7
- package/types/components/partials/element/accordion.d.ts.map +0 -1
- package/types/components/partials/element/avatar.d.ts +0 -8
- package/types/components/partials/element/avatar.d.ts.map +0 -1
- package/types/components/partials/element/button.d.ts +0 -11
- package/types/components/partials/element/button.d.ts.map +0 -1
- package/types/components/partials/element/dropdown.d.ts +0 -17
- package/types/components/partials/element/dropdown.d.ts.map +0 -1
- package/types/components/partials/element/initials.d.ts +0 -9
- package/types/components/partials/element/initials.d.ts.map +0 -1
- package/types/components/partials/element/message.d.ts +0 -2
- package/types/components/partials/element/message.d.ts.map +0 -1
- package/types/components/partials/element/modal.d.ts +0 -10
- package/types/components/partials/element/modal.d.ts.map +0 -1
- package/types/components/partials/element/sidebar.d.ts +0 -6
- package/types/components/partials/element/sidebar.d.ts.map +0 -1
- package/types/components/partials/element/tooltip.d.ts +0 -8
- package/types/components/partials/element/tooltip.d.ts.map +0 -1
- package/types/components/partials/element/topbar.d.ts +0 -8
- package/types/components/partials/element/topbar.d.ts.map +0 -1
- package/types/components/partials/form/checkbox.d.ts +0 -14
- package/types/components/partials/form/checkbox.d.ts.map +0 -1
- package/types/components/partials/form/drop-handler.d.ts +0 -6
- package/types/components/partials/form/drop-handler.d.ts.map +0 -1
- package/types/components/partials/form/drop.d.ts +0 -11
- package/types/components/partials/form/drop.d.ts.map +0 -1
- package/types/components/partials/form/form-error.d.ts +0 -6
- package/types/components/partials/form/form-error.d.ts.map +0 -1
- package/types/components/partials/form/input-color.d.ts +0 -10
- package/types/components/partials/form/input-color.d.ts.map +0 -1
- package/types/components/partials/form/input-currency.d.ts +0 -10
- package/types/components/partials/form/input-currency.d.ts.map +0 -1
- package/types/components/partials/form/input.d.ts +0 -9
- package/types/components/partials/form/input.d.ts.map +0 -1
- package/types/components/partials/form/location.d.ts +0 -12
- package/types/components/partials/form/location.d.ts.map +0 -1
- package/types/components/partials/form/select.d.ts +0 -27
- package/types/components/partials/form/select.d.ts.map +0 -1
- package/types/components/partials/form/toggle.d.ts +0 -9
- package/types/components/partials/form/toggle.d.ts.map +0 -1
- package/types/components/partials/is-first-render.d.ts +0 -2
- package/types/components/partials/is-first-render.d.ts.map +0 -1
- package/types/components/partials/layout/layout1.d.ts +0 -13
- package/types/components/partials/layout/layout1.d.ts.map +0 -1
- package/types/components/partials/layout/layout2.d.ts +0 -4
- package/types/components/partials/layout/layout2.d.ts.map +0 -1
- package/types/components/partials/not-found.d.ts +0 -2
- package/types/components/partials/not-found.d.ts.map +0 -1
- package/types/components/partials/styleguide.d.ts +0 -4
- package/types/components/partials/styleguide.d.ts.map +0 -1
- package/types/components/settings/settings-account.d.ts +0 -6
- package/types/components/settings/settings-account.d.ts.map +0 -1
- package/types/components/settings/settings-business.d.ts +0 -4
- package/types/components/settings/settings-business.d.ts.map +0 -1
- package/types/components/settings/settings-team--member.d.ts +0 -5
- package/types/components/settings/settings-team--member.d.ts.map +0 -1
- package/types/components/settings/settings-team.d.ts +0 -4
- package/types/components/settings/settings-team.d.ts.map +0 -1
- /package/components/partials/{not-found.jsx → not-found.tsx} +0 -0
package/.eslintrc.json
CHANGED
|
@@ -11,24 +11,7 @@
|
|
|
11
11
|
"node": true
|
|
12
12
|
},
|
|
13
13
|
"globals": {
|
|
14
|
-
|
|
15
|
-
"CONFIG": true,
|
|
16
|
-
"cssVar": true,
|
|
17
|
-
"onChange": true,
|
|
18
|
-
"sharedStore": true,
|
|
19
|
-
"sharedStoreCache": true,
|
|
20
|
-
// Dependency globals
|
|
21
|
-
"Link": true,
|
|
22
|
-
"forwardRef": true,
|
|
23
|
-
"useCallback": true,
|
|
24
|
-
"useEffect": true,
|
|
25
|
-
"useLocation": true,
|
|
26
|
-
"useNavigate": true,
|
|
27
|
-
"useImperativeHandle": true,
|
|
28
|
-
"useMemo": true,
|
|
29
|
-
"useParams": true,
|
|
30
|
-
"useState": true,
|
|
31
|
-
"useRef": true
|
|
14
|
+
"Link": true
|
|
32
15
|
},
|
|
33
16
|
"parser": "@typescript-eslint/parser",
|
|
34
17
|
"parserOptions": {
|
|
@@ -58,10 +41,12 @@
|
|
|
58
41
|
"allowObject": true
|
|
59
42
|
}],
|
|
60
43
|
"max-len": ["error", { "code": 140, "ignorePattern": "^\\s*(<(rect|path|line)\\s|class=|className=|'|`)" }],
|
|
44
|
+
"no-var": "off",
|
|
61
45
|
"no-prototype-builtins": "off",
|
|
62
46
|
"@typescript-eslint/no-this-alias": "off",
|
|
63
|
-
|
|
47
|
+
"@typescript-eslint/ban-ts-comment": "off",
|
|
64
48
|
"no-unused-vars": "off",
|
|
49
|
+
"@typescript-eslint/no-unsafe-function-type": "off",
|
|
65
50
|
"@typescript-eslint/no-unused-vars": ["error", {
|
|
66
51
|
"argsIgnorePattern": "^_",
|
|
67
52
|
"varsIgnorePattern": "^_",
|
package/_example/.env
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
export
|
|
1
|
+
export type Config = import('nitro-web/types').Config & {}
|
|
2
|
+
export default { ...INJECTED } as Config
|
package/_example/client/index.ts
CHANGED
|
@@ -1,29 +1,11 @@
|
|
|
1
|
-
import { useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react'
|
|
2
|
-
import { Link, useLocation, useNavigate, useParams } from 'react-router-dom'
|
|
3
|
-
import { setupApp, onChange } from 'nitro-web'
|
|
4
1
|
|
|
5
|
-
import
|
|
2
|
+
import 'nitro-web/client/globals'
|
|
3
|
+
import { setupApp } from 'nitro-web'
|
|
4
|
+
|
|
6
5
|
import './css/index.css'
|
|
6
|
+
import config from './config'
|
|
7
7
|
import { Layout1, Layout2 } from '../components/partials/layouts'
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
// application globals
|
|
11
|
-
onChange: onChange,
|
|
12
|
-
sharedStore: undefined, // defined in setupApp
|
|
13
|
-
sharedStoreCache: undefined, // defined in setupApp
|
|
14
|
-
|
|
15
|
-
// dependency globals
|
|
16
|
-
Link: Link,
|
|
17
|
-
useCallback: useCallback,
|
|
18
|
-
useEffect: useEffect,
|
|
19
|
-
useLocation: useLocation,
|
|
20
|
-
useNavigate: useNavigate,
|
|
21
|
-
useImperativeHandle: useImperativeHandle,
|
|
22
|
-
useMemo: useMemo,
|
|
23
|
-
useParams: useParams,
|
|
24
|
-
useRef: useRef,
|
|
25
|
-
useState: useState,
|
|
26
|
-
})
|
|
27
|
-
|
|
28
|
-
// Fetch state and start app
|
|
9
|
+
// Auto-import page components, initilize app, and run config.beforeApp
|
|
29
10
|
setupApp(config, [Layout1, Layout2])
|
|
11
|
+
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* will be automatically imported and setup by the client router, we're just listing them all here for the sake of example.
|
|
4
4
|
*/
|
|
5
5
|
import { css, theme } from 'twin.macro'
|
|
6
|
+
import config from '../client/config'
|
|
6
7
|
import {
|
|
7
8
|
Signin,
|
|
8
9
|
Signup,
|
|
@@ -16,7 +17,6 @@ import {
|
|
|
16
17
|
// SettingsTeam,
|
|
17
18
|
} from 'nitro-web'
|
|
18
19
|
|
|
19
|
-
import config from '../client/config'
|
|
20
20
|
|
|
21
21
|
// Signin page (can be saved onto a seperate .jsx/.tsx file under the components folder)
|
|
22
22
|
export const SigninPage = () => <Signin config={config} />
|
package/_example/package.json
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
"build": "NODE_ENV=production webpack --target=web",
|
|
10
10
|
"dev": "clear && npm run dev:lint --silent & npm run dev:server --silent & npm run dev:client --silent",
|
|
11
11
|
"dev:client": "webpack serve --progress --config ./webpack.config.js",
|
|
12
|
+
"dev:client-only": "isStatic=true npm run dev:client",
|
|
12
13
|
"dev:server": "nodemon ./server -q -w ./server/ -w ./components/ -e js",
|
|
13
14
|
"dev:lint": "eslint ./components ./server/",
|
|
14
15
|
"minor": "standard-version --release-as minor && git push staging",
|
|
@@ -48,7 +49,6 @@
|
|
|
48
49
|
"@typescript-eslint/eslint-plugin": "^8.18.1",
|
|
49
50
|
"@typescript-eslint/parser": "^8.18.1",
|
|
50
51
|
"autoprefixer": "^9.8.8",
|
|
51
|
-
"babel-eslint": "^10.0.3",
|
|
52
52
|
"babel-loader": "^8.0.6",
|
|
53
53
|
"babel-plugin-macros": "^3.1.0",
|
|
54
54
|
"babel-plugin-react-html-attrs": "^2.1.0",
|
|
@@ -5,17 +5,21 @@ const env = process.env.env || (process.env.NODE_ENV !== 'production' ? 'develop
|
|
|
5
5
|
const pwd = process.env.PWD + '/'
|
|
6
6
|
|
|
7
7
|
export default {
|
|
8
|
-
inject: 'awsUrl clientUrl currencies countries env googleMapsApiKey stripePublishableKey
|
|
8
|
+
inject: 'awsUrl clientUrl currencies countries env googleMapsApiKey isStatic placeholderEmail stripePublishableKey version',
|
|
9
9
|
|
|
10
10
|
clientUrl: process.env.originUrl || 'http://localhost:3000',
|
|
11
11
|
emailFrom: process.env.emailFrom,
|
|
12
12
|
emailReplyTo: process.env.emailReplyTo,
|
|
13
13
|
emailTestMode: process.env.emailTestMode,
|
|
14
14
|
env: env,
|
|
15
|
+
homepage: _require(pwd + 'package.json').homepage,
|
|
16
|
+
isStatic: process.env.isStatic,
|
|
15
17
|
masterPassword: process.env.masterPassword,
|
|
16
18
|
mongoUrl: process.env.mongoUrl,
|
|
19
|
+
placeholderEmail: process.env.placeholderEmail,
|
|
17
20
|
publicPath: process.env.publicPath,
|
|
18
|
-
|
|
21
|
+
pwd: pwd, // change to rootDir
|
|
22
|
+
version: _require(pwd + 'package.json').version,
|
|
19
23
|
|
|
20
24
|
awsUrl: process.env.awsUrl,
|
|
21
25
|
googleMapsApiKey: process.env.googleMapsApiKey,
|
|
@@ -120,9 +124,4 @@ export default {
|
|
|
120
124
|
else res.unauthorized('Please sign in first.')
|
|
121
125
|
},
|
|
122
126
|
},
|
|
123
|
-
|
|
124
|
-
pwd: pwd,
|
|
125
|
-
homepage: _require(pwd + 'package.json').homepage,
|
|
126
|
-
repository: _require(pwd + 'package.json').repository,
|
|
127
|
-
version: _require(pwd + 'package.json').version,
|
|
128
127
|
}
|
package/_example/tsconfig.json
CHANGED
|
@@ -17,11 +17,19 @@
|
|
|
17
17
|
"noImplicitThis": true,
|
|
18
18
|
"noStrictGenericChecks": false,
|
|
19
19
|
"outDir": "./dist",
|
|
20
|
+
"paths": {
|
|
21
|
+
"types": ["./types.ts"]
|
|
22
|
+
},
|
|
20
23
|
"resolveJsonModule": true,
|
|
21
24
|
"skipLibCheck": true,
|
|
22
25
|
"sourceMap": true,
|
|
23
26
|
"strict": true,
|
|
24
|
-
"target": "es6"
|
|
27
|
+
"target": "es6"
|
|
25
28
|
},
|
|
26
|
-
"include": [
|
|
29
|
+
"include": [
|
|
30
|
+
"client",
|
|
31
|
+
"components/**/*.tsx",
|
|
32
|
+
"components/**/*.ts",
|
|
33
|
+
"types"
|
|
34
|
+
]
|
|
27
35
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type { Config, Errors, User } from 'nitro-web/types'
|
|
@@ -1,28 +1,47 @@
|
|
|
1
1
|
import { createBrowserRouter, createHashRouter, redirect, useParams, RouterProvider } from 'react-router-dom'
|
|
2
|
+
import { Fragment, ReactNode } from 'react'
|
|
2
3
|
import ReactDOM from 'react-dom/client'
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
4
|
+
import { AxiosRequestConfig } from '@hokify/axios'
|
|
5
|
+
import { beforeCreate, Provider, exposedData } from './store'
|
|
6
|
+
import { axios, camelCase, pick, toArray, setTimeoutPromise } from 'nitro-web/util'
|
|
7
|
+
import { Config, Store } from 'types'
|
|
5
8
|
|
|
6
|
-
|
|
9
|
+
type Settings = {
|
|
10
|
+
afterApp?: () => void
|
|
11
|
+
middleware: Record<string, (route: unknown, store: Store) => undefined | { redirect: string }>
|
|
12
|
+
beforeApp: (config: Config) => Promise<object>
|
|
13
|
+
beforeStoreUpdate: (prevStore: Store | null, newData: Store) => Store
|
|
14
|
+
isStatic?: boolean
|
|
15
|
+
layouts: React.FC[]
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
type Route = {
|
|
19
|
+
component: React.FC<{ route?: Route; params?: object; location?: object }>
|
|
20
|
+
meta?: { title?: string }
|
|
21
|
+
middleware: string[]
|
|
22
|
+
name: string
|
|
23
|
+
path: string
|
|
24
|
+
redirect?: string
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export async function setupApp(config: Config, layouts: React.FC[]) {
|
|
7
28
|
// Fetch state and init app
|
|
8
|
-
const settings = {
|
|
29
|
+
const settings: Settings = {
|
|
9
30
|
middleware: Object.assign(defaultMiddleware, config.middleware || {}),
|
|
10
|
-
beforeApp:
|
|
11
|
-
beforeStoreUpdate: config.beforeStoreUpdate ||
|
|
12
|
-
|
|
31
|
+
beforeApp: config.beforeApp || beforeApp,
|
|
32
|
+
beforeStoreUpdate: config.beforeStoreUpdate || beforeStoreUpdate,
|
|
33
|
+
isStatic: config.isStatic,
|
|
13
34
|
layouts: layouts,
|
|
14
35
|
}
|
|
15
36
|
if (!settings.layouts) throw new Error('layouts are required')
|
|
16
|
-
const
|
|
17
|
-
|
|
37
|
+
const initData = (await settings.beforeApp(config)) || {}
|
|
38
|
+
beforeCreate(initData, settings.beforeStoreUpdate)
|
|
18
39
|
|
|
19
|
-
const root = ReactDOM.createRoot(document.getElementById('app'))
|
|
40
|
+
const root = ReactDOM.createRoot(document.getElementById('app') as HTMLElement)
|
|
20
41
|
root.render(<App settings={settings} />)
|
|
21
42
|
}
|
|
22
43
|
|
|
23
|
-
function App({ settings }) {
|
|
24
|
-
// Should only render once
|
|
25
|
-
const Provider = window.sharedStore.Provider
|
|
44
|
+
function App({ settings }: { settings: Settings }): ReactNode {
|
|
26
45
|
// const themeNormalised = theme
|
|
27
46
|
const router = getRouter(settings)
|
|
28
47
|
// const theme = pick(themeNormalised, []) // e.g. 'topPanelHeight'
|
|
@@ -39,67 +58,42 @@ function App({ settings }) {
|
|
|
39
58
|
* 'idle' event is fired, after route/layout change.
|
|
40
59
|
*/
|
|
41
60
|
const sessionHistory = JSON.parse(sessionStorage.getItem('sessionHistory') || '{}')
|
|
42
|
-
router
|
|
61
|
+
router?.subscribe((state) => {
|
|
43
62
|
// @param state.historyAction - 'POP', 'PUSH', 'REPLACE'
|
|
44
63
|
// @param state.navigation.state - 'loading', 'idle' (after navigation)
|
|
45
64
|
if (state.navigation.state === 'idle') return
|
|
46
|
-
sessionHistory[state.location.key] = scrollbarElement()
|
|
65
|
+
sessionHistory[state.location.key] = scrollbarElement()?.scrollTop // 100k pages == 1.6MB (we're good, lol)
|
|
47
66
|
sessionStorage.setItem('sessionHistory', JSON.stringify(sessionHistory))
|
|
48
67
|
})
|
|
49
|
-
}, [])
|
|
68
|
+
}, [!!router])
|
|
50
69
|
|
|
51
70
|
return (
|
|
52
71
|
<Provider>
|
|
53
72
|
{/* <ThemeProvider theme={themeNormalised}> */}
|
|
54
|
-
<RouterProvider router={router} />
|
|
73
|
+
{ router && <RouterProvider router={router} /> }
|
|
55
74
|
<AfterApp settings={settings} />
|
|
56
75
|
{/* </ThemeProvider> */}
|
|
57
76
|
</Provider>
|
|
58
77
|
)
|
|
59
78
|
}
|
|
60
79
|
|
|
61
|
-
function AfterApp(settings) {
|
|
80
|
+
function AfterApp({ settings }: { settings: Settings }) {
|
|
62
81
|
if (settings.afterApp) settings.afterApp()
|
|
63
82
|
return (null)
|
|
64
83
|
}
|
|
65
84
|
|
|
66
|
-
function
|
|
67
|
-
// @param {object} newStoreData - normally provided from a /login or /state request data
|
|
68
|
-
return createContainer(() => {
|
|
69
|
-
// Normally only setup once
|
|
70
|
-
// Fast reload - rebuilds <Root/>, and then createContainer() without providing the initialState
|
|
71
|
-
// Hot reload - rebuilds the whole app
|
|
72
|
-
let [store, setStore] = useState(() => beforeStoreUpdate(null, newStoreData || window.sharedStoreCache || {})) // runs only once
|
|
73
|
-
|
|
74
|
-
// Wrap the setState function to always run beforeStoreUpdate
|
|
75
|
-
const wrappedSetStore = (updater) => {
|
|
76
|
-
if (typeof updater === 'function') {
|
|
77
|
-
setStore((prevStore) => {
|
|
78
|
-
const nextState = updater(prevStore)
|
|
79
|
-
return beforeStoreUpdate(prevStore, nextState)
|
|
80
|
-
})
|
|
81
|
-
} else {
|
|
82
|
-
setStore((prevStore) => beforeStoreUpdate(prevStore, updater))
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
window.sharedStoreCache = store
|
|
87
|
-
newStoreData = null
|
|
88
|
-
return [store, wrappedSetStore]
|
|
89
|
-
})
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
function getRouter(settings) {
|
|
85
|
+
function getRouter(settings: Settings) {
|
|
93
86
|
/**
|
|
94
87
|
* Get all routes from components folder
|
|
95
88
|
* @return {array} routes for for creatingBrowserRouter
|
|
89
|
+
*
|
|
90
|
+
* During build time, webpack returns `require` function, which also contains all matching files
|
|
91
|
+
* https://webpack.js.org/guides/dependency-management/#requirecontext
|
|
96
92
|
*/
|
|
97
|
-
// During build time, webpack returns `require` function, which also contains all matching files
|
|
98
|
-
// https://webpack.js.org/guides/dependency-management/#requirecontext
|
|
99
|
-
|
|
100
93
|
let requireContext
|
|
101
94
|
try {
|
|
102
|
-
|
|
95
|
+
// @ts-expect-error
|
|
96
|
+
requireContext = import.meta.webpackContext('componentsDir', {
|
|
103
97
|
recursive: true,
|
|
104
98
|
regExp: /(?<!\.api)\.(j|t)sx$/,
|
|
105
99
|
})
|
|
@@ -110,7 +104,7 @@ function getRouter(settings) {
|
|
|
110
104
|
}
|
|
111
105
|
// Loop files
|
|
112
106
|
// const components = {}
|
|
113
|
-
const layouts = []
|
|
107
|
+
const layouts: Route[][] = []
|
|
114
108
|
|
|
115
109
|
for (const filename of requireContext.keys()) {
|
|
116
110
|
const file = requireContext(filename) // require
|
|
@@ -120,7 +114,7 @@ function getRouter(settings) {
|
|
|
120
114
|
const isReactFnComponentOrFnRef = typeof file[key] === 'function' || !!file[key]?.render
|
|
121
115
|
if (!file.hasOwnProperty(key) || key.match(/route/i) || !isReactFnComponentOrFnRef) continue
|
|
122
116
|
const componentRoutes = toArray(file[key].route || file.route || file.Route)
|
|
123
|
-
const componentName = key || camelCase(key.replace(/^.*[\\\/]|\.jsx$/g, '')) // eslint-disable-line
|
|
117
|
+
const componentName = key || camelCase(key.replace(/^.*[\\\/]|\.jsx$/g, '')) as string // eslint-disable-line
|
|
124
118
|
// console.log(file)
|
|
125
119
|
// Todo: need to retrieve the original function name for default exports during minification.
|
|
126
120
|
// console.log(1, file[key].name, key, componentName, file[key])
|
|
@@ -136,12 +130,14 @@ function getRouter(settings) {
|
|
|
136
130
|
|
|
137
131
|
for (const routePath of routePaths) {
|
|
138
132
|
const layoutNum = (route.meta?.layout || 1) - 1
|
|
133
|
+
|
|
139
134
|
// get the routes middleware
|
|
140
135
|
const middleware = toArray(route[routePath]).filter(o => {
|
|
141
136
|
if (o === true) return // ignore true
|
|
142
137
|
else if (settings.middleware[o]) return true
|
|
143
138
|
else console.error(`No middleware named '${o}' defined under config.middleware, skipping..`)
|
|
144
139
|
})
|
|
140
|
+
|
|
145
141
|
// Push route to layout
|
|
146
142
|
if (!layouts[layoutNum]) layouts[layoutNum] = []
|
|
147
143
|
layouts[layoutNum].push({
|
|
@@ -161,26 +157,30 @@ function getRouter(settings) {
|
|
|
161
157
|
}
|
|
162
158
|
|
|
163
159
|
// Generate createBrowserRouter array
|
|
164
|
-
let nonce
|
|
165
|
-
const createRouterArray = layouts.map((
|
|
166
|
-
if (!
|
|
160
|
+
let nonce: boolean
|
|
161
|
+
const createRouterArray = layouts.map((layout, i) => {
|
|
162
|
+
if (!layout) return
|
|
167
163
|
const Layout = settings.layouts[i]
|
|
168
164
|
if (!Layout) throw new Error(`Please pass Layout${i+1 || ''} to appSetup()`)
|
|
169
165
|
return {
|
|
170
166
|
// path: '/', // (disbaled: page component with path:'/' doesnt work)
|
|
171
|
-
element:
|
|
172
|
-
<
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
167
|
+
element: (
|
|
168
|
+
<Fragment>
|
|
169
|
+
<Layout />
|
|
170
|
+
<RestoreScroll />
|
|
171
|
+
</Fragment>
|
|
172
|
+
),
|
|
173
|
+
children: layout.map((route) => {
|
|
176
174
|
return {
|
|
177
|
-
element:
|
|
175
|
+
element: (
|
|
176
|
+
<RouteComponent route={route} />
|
|
177
|
+
),
|
|
178
178
|
path: route.path,
|
|
179
|
-
loader: async (
|
|
180
|
-
// wait for
|
|
179
|
+
loader: async () => { // request
|
|
180
|
+
// wait for container/exposedData to be setup
|
|
181
181
|
if (!nonce) nonce = true && await setTimeoutPromise(() => {}, 0) // eslint-disable-line
|
|
182
|
-
for (
|
|
183
|
-
|
|
182
|
+
for (const key of route.middleware) {
|
|
183
|
+
const error = settings.middleware[key](route, exposedData || {})
|
|
184
184
|
if (error && error.redirect) {
|
|
185
185
|
return redirect(error.redirect)
|
|
186
186
|
}
|
|
@@ -190,13 +190,13 @@ function getRouter(settings) {
|
|
|
190
190
|
}
|
|
191
191
|
}),
|
|
192
192
|
}
|
|
193
|
-
}).filter(o => o)
|
|
193
|
+
}).filter(o => !!o)
|
|
194
194
|
|
|
195
195
|
// console.log(createRouterArray) ////////////////////////
|
|
196
196
|
|
|
197
197
|
// Create Browser/HashRouter (if there is no error above otheriwse router will be empty)
|
|
198
198
|
if (createRouterArray.length) {
|
|
199
|
-
if (settings.
|
|
199
|
+
if (settings.isStatic) return createHashRouter(createRouterArray) // Use HashRouter for demo
|
|
200
200
|
else return createBrowserRouter(createRouterArray) // Use BrowserRouter otherwise
|
|
201
201
|
}
|
|
202
202
|
}
|
|
@@ -210,8 +210,8 @@ function RestoreScroll() {
|
|
|
210
210
|
const previousScrollY = sessionHistory[location.key] || 0
|
|
211
211
|
// console.log(3, previousScrollY, window.history.state)
|
|
212
212
|
const attemptScroll = (attempts = 0) => {
|
|
213
|
-
if (scrollbarElement()
|
|
214
|
-
scrollbarElement()
|
|
213
|
+
if (scrollbarElement()?.scrollTop !== previousScrollY && attempts < 100) {
|
|
214
|
+
scrollbarElement()?.scrollTo(0, previousScrollY)
|
|
215
215
|
setTimeout(() => attemptScroll(attempts + 1), 10)
|
|
216
216
|
}
|
|
217
217
|
}
|
|
@@ -221,11 +221,14 @@ function RestoreScroll() {
|
|
|
221
221
|
return (null)
|
|
222
222
|
}
|
|
223
223
|
|
|
224
|
-
function RouteComponent({ route }) {
|
|
224
|
+
function RouteComponent({ route }: { route: Route }) {
|
|
225
|
+
const Component = route.component
|
|
225
226
|
const params = useParams()
|
|
226
227
|
const location = useLocation()
|
|
227
|
-
document.title = route.meta
|
|
228
|
-
return
|
|
228
|
+
document.title = route.meta?.title || ''
|
|
229
|
+
return (
|
|
230
|
+
<Component route={route} params={params} location={location} />
|
|
231
|
+
)
|
|
229
232
|
}
|
|
230
233
|
|
|
231
234
|
function scrollbarElement() {
|
|
@@ -236,45 +239,44 @@ function scrollbarElement() {
|
|
|
236
239
|
|
|
237
240
|
// ---- Overridable defaults ------------
|
|
238
241
|
|
|
239
|
-
async function
|
|
242
|
+
async function beforeApp(config: Config) {
|
|
240
243
|
/**
|
|
241
244
|
* Gets called once before React is initialised
|
|
242
|
-
* @return {promise} - newStoreData which is used for
|
|
245
|
+
* @return {promise} - newStoreData which is used for sharedStore, later merged with the config.store() defaults
|
|
243
246
|
*/
|
|
244
247
|
let apiAvailable
|
|
245
|
-
let
|
|
248
|
+
let stateData
|
|
246
249
|
try {
|
|
247
250
|
// Unload prehot data
|
|
248
|
-
if (window.prehot) {
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
}
|
|
252
|
-
if (!
|
|
253
|
-
|
|
251
|
+
// if (window.prehot) {
|
|
252
|
+
// sharedStoreCache = window.prehot.sharedStoreCache
|
|
253
|
+
// delete window.prehot
|
|
254
|
+
// }
|
|
255
|
+
if (!exposedData && !config.isStatic) {
|
|
256
|
+
stateData = (await axios().get('/api/state', { 'axios-retry': { retries: 3 }, timeout: 4000 } as AxiosRequestConfig)).data
|
|
254
257
|
apiAvailable = true
|
|
255
258
|
}
|
|
256
259
|
} catch (err) {
|
|
257
260
|
console.error('We had trouble connecting to the API, please refresh')
|
|
258
261
|
console.log(err)
|
|
259
262
|
}
|
|
260
|
-
return { ...(
|
|
263
|
+
return { ...(stateData || exposedData), apiAvailable }
|
|
261
264
|
}
|
|
262
265
|
|
|
263
|
-
function
|
|
266
|
+
function beforeStoreUpdate(prevStore: Store | null, newData: Store) {
|
|
264
267
|
/**
|
|
265
268
|
* Get store object (called on signup/signin/signout/state)
|
|
266
269
|
* @param {object} store - existing store
|
|
267
270
|
* @param {object} <newStoreData> - pass to override store with /login or /state request data
|
|
268
271
|
* @return {object} store
|
|
269
272
|
*/
|
|
270
|
-
if (!
|
|
271
|
-
store = {
|
|
272
|
-
...(
|
|
273
|
-
message:
|
|
274
|
-
|
|
275
|
-
user: null, // defined if user is signed in
|
|
273
|
+
if (!newData) return newData
|
|
274
|
+
const store = {
|
|
275
|
+
...(prevStore || {
|
|
276
|
+
message: undefined,
|
|
277
|
+
user: undefined, // defined if user is signed in
|
|
276
278
|
}),
|
|
277
|
-
...(
|
|
279
|
+
...(newData || {}),
|
|
278
280
|
}
|
|
279
281
|
|
|
280
282
|
// Used to verify if the current cookie belongs to this user
|
|
@@ -285,20 +287,20 @@ function defaultBeforeStoreUpdate(store, newStoreData) {
|
|
|
285
287
|
|
|
286
288
|
const defaultMiddleware = {
|
|
287
289
|
// Global middleware that can referenced from component routes
|
|
288
|
-
isAdmin: (route, store) => {
|
|
289
|
-
|
|
290
|
-
if (user
|
|
291
|
-
else if (user
|
|
290
|
+
isAdmin: (route: unknown, store: { user?: { type?: string } }) => {
|
|
291
|
+
const user = store.user || { type: 'visitor' }
|
|
292
|
+
if (user?.type?.match(/admin/)) return
|
|
293
|
+
else if (user?.type && user?.type !== 'visitor') return { redirect: '/signin?unauth' }
|
|
292
294
|
else return { redirect: '/signin?signin' }
|
|
293
295
|
},
|
|
294
|
-
isSubscribed: (route, store) => {
|
|
295
|
-
|
|
296
|
-
if (!user
|
|
296
|
+
isSubscribed: (route: unknown, store: { user?: { company?: { currentSubscription?: string } } }) => {
|
|
297
|
+
const user = store?.user || { type: 'visitor', company: { currentSubscription: '' } }
|
|
298
|
+
if (!user?.company?.currentSubscription) return
|
|
297
299
|
else return { redirect: '/plans/subscribe' }
|
|
298
300
|
},
|
|
299
|
-
isUser: (route, store) => {
|
|
300
|
-
|
|
301
|
-
if (user
|
|
301
|
+
isUser: (route: unknown, store: { user?: { type?: string } }) => {
|
|
302
|
+
const user = store.user || { type: 'visitor' }
|
|
303
|
+
if (user?.type !== 'visitor') return
|
|
302
304
|
else return { redirect: '/signin?signin' }
|
|
303
305
|
},
|
|
304
306
|
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react'
|
|
2
|
+
import { Link, useLocation, useNavigate, useParams } from 'react-router-dom'
|
|
3
|
+
import { onChange } from 'nitro-web'
|
|
4
|
+
import { useTracked } from './store'
|
|
5
|
+
|
|
6
|
+
declare global {
|
|
7
|
+
// Common application globals
|
|
8
|
+
const onChange: typeof import('nitro-web').onChange
|
|
9
|
+
/** Global shared store, a react-tracked container initialized in `setupApp()` */
|
|
10
|
+
let useTracked: typeof import('./store').useTracked
|
|
11
|
+
|
|
12
|
+
// Common aependency globals
|
|
13
|
+
/** The public API for rendering a history-aware `<a>`. */
|
|
14
|
+
const Link: typeof import('react-router-dom').Link
|
|
15
|
+
const useCallback: typeof import('react').useCallback
|
|
16
|
+
const useEffect: typeof import('react').useEffect
|
|
17
|
+
const useImperativeHandle: typeof import('react').useImperativeHandle
|
|
18
|
+
const useLocation: typeof import('react-router-dom').useLocation
|
|
19
|
+
const useMemo: typeof import('react').useMemo
|
|
20
|
+
const useNavigate: typeof import('react-router-dom').useNavigate
|
|
21
|
+
const useParams: typeof import('react-router-dom').useParams
|
|
22
|
+
const useRef: typeof import('react').useRef
|
|
23
|
+
const useState: typeof import('react').useState
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
Object.assign(window, {
|
|
27
|
+
// application globals
|
|
28
|
+
onChange: onChange,
|
|
29
|
+
useTracked: useTracked,
|
|
30
|
+
|
|
31
|
+
// dependency globals
|
|
32
|
+
Link: Link,
|
|
33
|
+
useCallback: useCallback,
|
|
34
|
+
useEffect: useEffect,
|
|
35
|
+
useImperativeHandle: useImperativeHandle,
|
|
36
|
+
useLocation: useLocation,
|
|
37
|
+
useMemo: useMemo,
|
|
38
|
+
useNavigate: useNavigate,
|
|
39
|
+
useParams: useParams,
|
|
40
|
+
useRef: useRef,
|
|
41
|
+
useState: useState,
|
|
42
|
+
})
|
package/client/index.ts
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// Required Global Types
|
|
2
|
+
import '../types/required-globals.d.ts'
|
|
3
|
+
|
|
4
|
+
// export const pi = parseFloat(3.142)
|
|
5
|
+
export * from './app'
|
|
6
|
+
export * from '../util.js'
|
|
7
|
+
export * as util from '../util.js'
|
|
8
|
+
|
|
9
|
+
// Component Pages
|
|
10
|
+
export { Signin } from '../components/auth/signin'
|
|
11
|
+
export { Signup } from '../components/auth/signup'
|
|
12
|
+
export { ResetInstructions, ResetPassword } from '../components/auth/reset'
|
|
13
|
+
export { Dashboard } from '../components/dashboard/dashboard'
|
|
14
|
+
export { NotFound } from '../components/partials/not-found'
|
|
15
|
+
export { Styleguide } from '../components/partials/styleguide'
|
|
16
|
+
// export { SettingsAccount } from '../components/settings/settings-account'
|
|
17
|
+
// export { SettingsBusiness } from '../components/settings/settings-business'
|
|
18
|
+
// export { SettingsTeamMember } from '../components/settings/settings-team--member'
|
|
19
|
+
// export { SettingsTeam } from '../components/settings/settings-team'
|
|
20
|
+
|
|
21
|
+
// Component Elements
|
|
22
|
+
export { Accordion } from '../components/partials/element/accordion'
|
|
23
|
+
export { Avatar } from '../components/partials/element/avatar'
|
|
24
|
+
export { Button } from '../components/partials/element/button'
|
|
25
|
+
export { Dropdown } from '../components/partials/element/dropdown'
|
|
26
|
+
export { GithubLink } from '../components/partials/element/github-link'
|
|
27
|
+
export { Initials } from '../components/partials/element/initials'
|
|
28
|
+
export { Message } from '../components/partials/element/message'
|
|
29
|
+
// export { Modal } from '../components/partials/element/modal'
|
|
30
|
+
export { Sidebar, type SidebarProps } from '../components/partials/element/sidebar'
|
|
31
|
+
export { Tooltip } from '../components/partials/element/tooltip'
|
|
32
|
+
export { Topbar } from '../components/partials/element/topbar'
|
|
33
|
+
|
|
34
|
+
// Component Form
|
|
35
|
+
export { Checkbox } from '../components/partials/form/checkbox'
|
|
36
|
+
export { Drop } from '../components/partials/form/drop'
|
|
37
|
+
export { DropHandler } from '../components/partials/form/drop-handler'
|
|
38
|
+
export { FormError } from '../components/partials/form/form-error'
|
|
39
|
+
export { Input } from '../components/partials/form/input'
|
|
40
|
+
export { InputColor } from '../components/partials/form/input-color'
|
|
41
|
+
export { InputCurrency } from '../components/partials/form/input-currency'
|
|
42
|
+
// export { InputDate } from '../components/partials/form/input-date'
|
|
43
|
+
export { Location } from '../components/partials/form/location'
|
|
44
|
+
export { Select, getSelectStyle } from '../components/partials/form/select'
|
|
45
|
+
export { Toggle } from '../components/partials/form/toggle'
|
|
46
|
+
|
|
47
|
+
// Component Layouts
|
|
48
|
+
export { Layout1 } from '../components/partials/layout/layout1'
|
|
49
|
+
export { Layout2 } from '../components/partials/layout/layout2'
|
|
50
|
+
|
|
51
|
+
// Component Other
|
|
52
|
+
export { IsFirstRender } from '../components/partials/is-first-render'
|