nitro-web 0.0.83 → 0.0.85

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/client/app.tsx CHANGED
@@ -94,10 +94,12 @@ function App({ settings, config, storeContainer }: { settings: Settings, config:
94
94
  }, [!!router])
95
95
 
96
96
  return (
97
+ // <StrictMode>
97
98
  <storeContainer.Provider>
98
99
  { router && <RouterProvider router={router}/> }
99
100
  <AfterApp settings={settings} />
100
101
  </storeContainer.Provider>
102
+ // </StrictMode>
101
103
  )
102
104
  }
103
105
 
@@ -291,9 +293,9 @@ async function beforeApp(config: Config) {
291
293
 
292
294
  const defaultMiddleware = {
293
295
  // Global middleware that can referenced from component routes
294
- isAdmin: (route: unknown, store: { user?: { type?: string } }) => {
296
+ isAdmin: (route: unknown, store: { user?: { type?: string, isAdmin?: boolean } }) => {
295
297
  const user = store.user || { type: 'visitor' }
296
- if (user?.type?.match(/admin/)) return
298
+ if (user?.type?.match(/admin/) || user?.isAdmin) return
297
299
  else if (user?.type && user?.type !== 'visitor') return { redirect: '/signin?unauth' }
298
300
  else return { redirect: '/signin?signin' }
299
301
  },
package/client/index.ts CHANGED
@@ -18,10 +18,6 @@ export { ResetInstructions, ResetPassword } from '../components/auth/reset'
18
18
  export { Dashboard } from '../components/dashboard/dashboard'
19
19
  export { NotFound } from '../components/partials/not-found'
20
20
  export { Styleguide } from '../components/partials/styleguide'
21
- // export { SettingsAccount } from '../components/settings/settings-account'
22
- // export { SettingsBusiness } from '../components/settings/settings-business'
23
- // export { SettingsTeamMember } from '../components/settings/settings-team--member'
24
- // export { SettingsTeam } from '../components/settings/settings-team'
25
21
 
26
22
  // Component Elements
27
23
  export { Accordion } from '../components/partials/element/accordion'
@@ -192,7 +192,7 @@ async function resetPassword(req, res) {
192
192
  async function inviteInstructions(req, res) {
193
193
  try {
194
194
  // Check if user is admin here rather than in middleware (which may not exist yet)
195
- if (req.user.type != 'admin') {
195
+ if (req.user.type != 'admin' && !req.user.isAdmin) {
196
196
  throw new Error('You are not authorized to invite users.')
197
197
  }
198
198
  const inviteToken = await tokenCreate()
@@ -315,7 +315,7 @@ export async function signinAndGetStore(user, isDesktop, getStore) {
315
315
  return { ...store, jwt }
316
316
  }
317
317
 
318
- export async function userCreate({ name, business, email, password }) {
318
+ export async function userCreate({ business, password, ...userDataProp }) {
319
319
  try {
320
320
  if (!this.findUserFromProvider) {
321
321
  throw new Error('this.findUserFromProvider doesn\'t exist, make sure the context is available when calling this function')
@@ -330,14 +330,15 @@ export async function userCreate({ name, business, email, password }) {
330
330
  users: [{ _id: userId, role: 'owner', status: 'active' }],
331
331
  }
332
332
  const userData = {
333
- _id: userId,
334
- email: email,
335
- firstName: fullNameSplit(name)[0],
336
- lastName: fullNameSplit(name)[1],
333
+ ...userDataProp,
334
+ _id: userId,
335
+ ...(userDataProp.name ? {
336
+ firstName: fullNameSplit(userDataProp.name)[0],
337
+ lastName: fullNameSplit(userDataProp.name)[1],
338
+ } : {}),
337
339
  password: password ? await bcrypt.hash(password, 10) : undefined,
338
340
  ...(isMultiTenant ? { company: companyData._id } : {}),
339
341
  }
340
-
341
342
  // First validate the data so we don't have to create a transaction
342
343
  const results = await Promise.allSettled([
343
344
  db.user.validate(userData, options),
@@ -362,10 +363,7 @@ export async function userCreate({ name, business, email, password }) {
362
363
 
363
364
  } catch (err) {
364
365
  if (!isArray(err)) throw err
365
- throw err.map((o) => {
366
- if (o.title == 'firstName') o.title = 'name'
367
- return o
368
- })
366
+ else throw err //...
369
367
  }
370
368
  }
371
369
 
@@ -1,4 +1,4 @@
1
- import { Topbar, Field, FormError, Button, util } from 'nitro-web'
1
+ import { Topbar, Field, FormError, Button, request } from 'nitro-web'
2
2
  import { Errors } from 'nitro-web/types'
3
3
 
4
4
  export function ResetInstructions() {
@@ -9,7 +9,7 @@ export function ResetInstructions() {
9
9
 
10
10
  async function onSubmit (event: React.FormEvent<HTMLFormElement>) {
11
11
  try {
12
- await util.request('post /api/reset-instructions', state, event, isLoading)
12
+ await request('post /api/reset-instructions', state, event, isLoading, setState)
13
13
  setStore((s) => ({ ...s, message: 'Done! Please check your email.' }))
14
14
  navigate('/signin')
15
15
  } catch (e) {
@@ -52,7 +52,7 @@ export function ResetPassword() {
52
52
 
53
53
  async function onSubmit (event: React.FormEvent<HTMLFormElement>) {
54
54
  try {
55
- const data = await util.request('post /api/reset-password', state, event, isLoading)
55
+ const data = await request('post /api/reset-password', state, event, isLoading, setState)
56
56
  setStore((s) => ({ ...s, ...data }))
57
57
  navigate('/')
58
58
  } catch (e) {
@@ -1,4 +1,4 @@
1
- import { Topbar, Field, Button, FormError, util, injectedConfig, updateJwt } from 'nitro-web'
1
+ import { Topbar, Field, Button, FormError, request, queryObject, injectedConfig, updateJwt } from 'nitro-web'
2
2
  import { Errors } from 'nitro-web/types'
3
3
 
4
4
  export function Signin() {
@@ -15,7 +15,7 @@ export function Signin() {
15
15
 
16
16
  useEffect(() => {
17
17
  // Autofill the email input from ?email=
18
- const query = util.queryObject(location.search, true)
18
+ const query = queryObject(location.search, true)
19
19
  if (query.email) setState({ ...state, email: query.email as string })
20
20
  }, [location.search])
21
21
 
@@ -33,7 +33,7 @@ export function Signin() {
33
33
 
34
34
  async function onSubmit (e: React.FormEvent<HTMLFormElement>) {
35
35
  try {
36
- const data = await util.request('post /api/signin', state, e, isLoading)
36
+ const data = await request('post /api/signin', state, e, isLoading, setState)
37
37
  // Keep it loading until we navigate
38
38
  isLoading[1](true)
39
39
  setStore((s) => ({ ...s, ...data }))
@@ -1,4 +1,4 @@
1
- import { Button, Field, FormError, Topbar, util, injectedConfig } from 'nitro-web'
1
+ import { Button, Field, FormError, Topbar, request, injectedConfig } from 'nitro-web'
2
2
  import { Errors } from 'nitro-web/types'
3
3
 
4
4
  export function Signup() {
@@ -15,12 +15,11 @@ export function Signup() {
15
15
 
16
16
  async function onSubmit (e: React.FormEvent<HTMLFormElement>) {
17
17
  try {
18
- setState({ ...state, errors: [] }) // clear errors (optional)
19
- const data = await util.request('post /api/signup', state, e, isLoading)
20
- setStore((s) => ({ ...s, ...data }))
18
+ const data = await request('post /api/signup', state, e, isLoading, setState)
19
+ setStore((prev) => ({ ...prev, ...data }))
21
20
  setTimeout(() => navigate('/'), 0) // wait for setStore
22
21
  } catch (e) {
23
- return setState({ ...state, errors: e as Errors })
22
+ setState((prev) => ({ ...prev, errors: e as Errors }))
24
23
  }
25
24
  }
26
25
 
@@ -32,7 +31,10 @@ export function Signup() {
32
31
  <div class="grid grid-cols-2 gap-6">
33
32
  <div>
34
33
  <label for="name">Your Name</label>
35
- <Field name="name" placeholder="E.g. Bruce Wayne" state={state} onChange={(e) => onChange(setState, e)} />
34
+ <Field name="name" placeholder="E.g. Bruce Wayne" state={state}
35
+ onChange={(e) => onChange(setState, e)}
36
+ errorTitle={/^(name|firstName|lastName)$/} // if different from `name`
37
+ />
36
38
  </div>
37
39
  <div>
38
40
  <label for="business.name">Company Name</label>
@@ -16,14 +16,16 @@ type CheckboxProps = React.InputHTMLAttributes<HTMLInputElement> & {
16
16
  checkboxClassName?: string
17
17
  svgClassName?: string
18
18
  labelClassName?: string
19
+ /** title used to find related error messages */
20
+ errorTitle?: string|RegExp
19
21
  }
20
22
 
21
23
  export function Checkbox({
22
- state, size, subtext, text, type='checkbox', className, checkboxClassName, svgClassName, labelClassName, ...props
24
+ state, size, subtext, text, type='checkbox', className, checkboxClassName, svgClassName, labelClassName, errorTitle, ...props
23
25
  }: CheckboxProps) {
24
26
  // Checkbox/radio/toggle component
25
27
  let value!: boolean
26
- const error = getErrorFromState(state, props.name)
28
+ const error = getErrorFromState(state, errorTitle || props.name)
27
29
  const id = props.id || props.name
28
30
 
29
31
  if (!props.name) throw new Error('Checkbox requires a `name` prop')
@@ -21,16 +21,18 @@ type DropProps = {
21
21
  errors?: Errors
22
22
  [key: string]: unknown
23
23
  }
24
+ /** title used to find related error messages */
25
+ errorTitle?: string|RegExp
24
26
  /** Props to pass to the input element */
25
27
  [key: string]: unknown
26
28
  }
27
29
 
28
30
  type Image = File | FileList | MonasteryImage | null
29
31
 
30
- export function Drop({ awsUrl, className, id, name, onChange, multiple, state, ...props }: DropProps) {
32
+ export function Drop({ awsUrl, className, id, name, onChange, multiple, state, errorTitle, ...props }: DropProps) {
31
33
  if (!name) throw new Error('Drop component requires a `name` prop')
32
34
  let value: Image = null
33
- const error = getErrorFromState(state, name)
35
+ const error = getErrorFromState(state, errorTitle || name)
34
36
  const inputId = id || name
35
37
  const [urls, setUrls] = useState([])
36
38
  const stateRef = useRef(state)
@@ -25,6 +25,8 @@ type FieldExtraProps = {
25
25
  /** Dependencies to break the implicit memoization of onChange/onInputChange */
26
26
  deps?: unknown[]
27
27
  placeholder?: string
28
+ /** title used to find related error messages */
29
+ errorTitle?: string|RegExp
28
30
  }
29
31
  type IconWrapperProps = {
30
32
  iconPos: 'left' | 'right'
@@ -43,17 +45,18 @@ type IsFieldCachedProps = {
43
45
  name: string
44
46
  state?: FieldProps['state']
45
47
  deps?: FieldProps['deps']
48
+ errorTitle?: FieldProps['errorTitle']
46
49
  }
47
50
 
48
51
  export const Field = memo(FieldBase, (prev, next) => {
49
52
  return isFieldCached(prev, next)
50
53
  })
51
54
 
52
- function FieldBase({ state, icon, iconPos: ip, ...props }: FieldProps) {
55
+ function FieldBase({ state, icon, iconPos: ip, errorTitle, ...props }: FieldProps) {
53
56
  // `type` must be kept as props.type for TS to be happy and follow the conditions below
54
57
  let value!: string
55
58
  let Icon!: React.ReactNode
56
- const error = getErrorFromState(state, props.name)
59
+ const error = getErrorFromState(state, errorTitle || props.name)
57
60
  const type = props.type
58
61
  const iconPos = ip == 'left' || (type == 'color' && !ip) ? 'left' : 'right'
59
62
  const id = props.id || props.name
@@ -184,13 +187,9 @@ function ColorSvg({ hex }: { hex?: string }) {
184
187
  export function isFieldCached(prev: IsFieldCachedProps, next: IsFieldCachedProps) {
185
188
  // Check if the field is cached, onChange/onInputChange doesn't affect the cache
186
189
  const path = prev.name
187
- const state = prev.state || {}
188
- // If the state value has changed, re-render!
189
- if (deepFind(state, path) !== deepFind(next.state || {}, path)) return false
190
- // If the state error has changed, re-render!
191
- if (getErrorFromState(state, path) !== getErrorFromState(next.state || {}, path)) return false
192
- // If `deps` have changed, handy for onChange/onInputChange, re-render!
193
- if ((next.deps?.length !== prev.deps?.length) || next.deps?.some((v, i) => v !== prev.deps?.[i])) return false
190
+ const prevState = prev.state || {}
191
+ const nextState = next.state || {}
192
+ const errorTitle = next.errorTitle || path
194
193
 
195
194
  // Check if any prop has changed, except `onChange`/`onInputChange`
196
195
  const allKeys = new Set([...Object.keys(prev), ...Object.keys(next)])
@@ -201,6 +200,16 @@ export function isFieldCached(prev: IsFieldCachedProps, next: IsFieldCachedProps
201
200
  return false
202
201
  }
203
202
  }
203
+
204
+ // If `deps` have changed, handy for onChange/onInputChange, re-render!
205
+ if ((next.deps?.length !== prev.deps?.length) || next.deps?.some((v, i) => v !== prev.deps?.[i])) return false
206
+
207
+ // If the state value has changed, re-render!
208
+ if (deepFind(prevState, path) !== deepFind(nextState, path)) return false
209
+
210
+ // If the state error has changed, re-render!
211
+ if (getErrorFromState(prevState, errorTitle) !== getErrorFromState(nextState, errorTitle)) return false
212
+
204
213
  // All good, use cached version
205
214
  return true
206
215
  }
@@ -43,6 +43,8 @@ export type SelectProps = {
43
43
  mode?: 'country'|'customer'|''
44
44
  /** Pass dependencies to break memoization, handy for onChange/onInputChange **/
45
45
  deps?: unknown[]
46
+ /** title used to find related error messages */
47
+ errorTitle?: string|RegExp
46
48
  /** All other props are passed to react-select **/
47
49
  [key: string]: unknown
48
50
  }
@@ -51,9 +53,11 @@ export const Select = memo(SelectBase, (prev, next) => {
51
53
  return isFieldCached(prev, next)
52
54
  })
53
55
 
54
- function SelectBase({ id, containerId, minMenuWidth, name, prefix='', onChange, options, state, mode='', ...props }: SelectProps) {
56
+ function SelectBase({
57
+ id, containerId, minMenuWidth, name, prefix='', onChange, options, state, mode='', errorTitle, ...props
58
+ }: SelectProps) {
55
59
  let value: unknown|unknown[]
56
- const error = getErrorFromState(state, name)
60
+ const error = getErrorFromState(state, errorTitle || name)
57
61
  if (!name) throw new Error('Select component requires a `name` and `options` prop')
58
62
 
59
63
  // Get value from value or state
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nitro-web",
3
- "version": "0.0.83",
3
+ "version": "0.0.85",
4
4
  "repository": "github:boycce/nitro-web",
5
5
  "homepage": "https://boycce.github.io/nitro-web/",
6
6
  "description": "Nitro is a battle-tested, modular base project to turbocharge your projects, styled using Tailwind 🚀",
package/server/index.js CHANGED
@@ -20,10 +20,8 @@ export { sendEmail } from './email/index.js'
20
20
 
21
21
  // Export API controllers
22
22
  export * from '../components/auth/auth.api.js'
23
- export * from '../components/settings/settings.api.js'
24
23
  export * from '../components/billing/stripe.api.js'
25
24
 
26
25
  export { routes as authRoutes } from '../components/auth/auth.api.js'
27
- export { routes as settingsRoutes } from '../components/settings/settings.api.js'
28
26
  export { routes as stripeRoutes } from '../components/billing/stripe.api.js'
29
27
 
@@ -8,7 +8,7 @@ export default {
8
8
  company: { model: 'company', required: true },
9
9
  email: { type: 'email', required: true, index: 'unique' },
10
10
  firstName: { type: 'string', required: true },
11
- lastName: { type: 'string' },
11
+ lastName: { type: 'string', required: true },
12
12
  password: { type: 'string', minLength: 6 },
13
13
  resetToken: { type: 'string' },
14
14
  status: { type: 'string', default: 'active', enum: ['active', 'deleted', 'inactive'] },
@@ -22,6 +22,12 @@ export default {
22
22
  findBL: ['password', 'resetToken'],
23
23
  updateBL: ['company', 'password', 'resetToken', 'status', 'stripeSubscription', 'type', 'usedFreeTrial'],
24
24
 
25
+ messages: {
26
+ lastName: {
27
+ required: 'A full name is required',
28
+ },
29
+ },
30
+
25
31
  beforeValidate: [
26
32
  async function (data) {
27
33
  if (data.email) data.email = data.email.trim().toLowerCase()
@@ -32,7 +38,7 @@ export default {
32
38
 
33
39
  afterFind: [
34
40
  async function (data) {
35
- if (!data) return
41
+ if (!data) return data
36
42
  data.name = fullName(data)
37
43
  },
38
44
  ],
package/server/router.js CHANGED
@@ -298,8 +298,8 @@ function resolveMiddleware (controllers, middleware, route, item) {
298
298
  }
299
299
 
300
300
  export const middleware = {
301
+ // Default middleware called before all /api/* routes
301
302
  order: [
302
- // Express middleware runtime order that are called for all API requests
303
303
  'loadAssets',
304
304
  'modifyRequest',
305
305
  'parseUrlEncoded',
@@ -308,6 +308,8 @@ export const middleware = {
308
308
  'beforeAPIRoute',
309
309
  ],
310
310
 
311
+ // --- Default middleware ---------------------
312
+
311
313
  modifyRequest: (req, res, next) => {
312
314
  // Handy boolean denoting that the request wants JSON returned
313
315
  // global.start = new Date().getTime()
@@ -345,4 +347,42 @@ export const middleware = {
345
347
  res.set('version', req.version)
346
348
  next()
347
349
  },
350
+
351
+ // --- Custom middleware ----------------------
352
+
353
+ isAdmin: (req, res, next) => {
354
+ // Still need to remove cookie matching in favour of uid..
355
+ // E.g. Cookie matching handy for rare issues, e.g. signout > signin (to a different user on another tab)
356
+ const user = req.user
357
+ let cookieMatch = user && (!req.headers.authid || user._id.toString() == req.headers.authid)
358
+ if (cookieMatch && (user.type?.match(/admin/) || user.isAdmin)) next()
359
+ else if (user && (user.type?.match(/admin/) || user.isAdmin)) res.unauthorized('Invalid cookie, please refresh your browser')
360
+ else if (user) res.unauthorized('You are not authorised to make this request.')
361
+ else res.unauthorized('Please sign in first.')
362
+ },
363
+ isCompanyOwner: (req, res, next) => {
364
+ let user = req.user || { companies: [] }
365
+ let cid = req.params.cid
366
+ let company = user.companies.find((o) => o._id.toString() == cid)
367
+ let companyUser = company?.users?.find((o) => o._id.toString() == user._id.toString())
368
+ if (!user._id) return res.unauthorized('Please sign in first.')
369
+ else if (!company || !companyUser) res.unauthorized('You are not authorised to make this request.')
370
+ else if (companyUser.type != 'owner') res.unauthorized('Only owners can make this request.')
371
+ else next()
372
+ },
373
+ isCompanyUser: (req, res, next) => {
374
+ let user = req.user || { companies: [] }
375
+ let cid = req.params.cid
376
+ let company = user.companies.find((o) => o._id.toString() == cid)
377
+ if (!user._id) return res.unauthorized('Please sign in first.')
378
+ else if (!company) res.unauthorized('You are not authorised to make this request.')
379
+ else next()
380
+ },
381
+ isUser: (req, res, next) => {
382
+ // todo: need to double check that uid is always defined
383
+ let uid = req.params.uid
384
+ if (req.user && (typeof uid == 'undefined' || req.user._id.toString() == uid)) next()
385
+ else if (req.user) res.unauthorized('You are not authorised to make this request.')
386
+ else res.unauthorized('Please sign in first.')
387
+ },
348
388
  }
package/types/util.d.ts CHANGED
@@ -266,7 +266,7 @@ export function getCurrencyOptions(currencies: {
266
266
  /**
267
267
  * Returns an error from a state object matching the path
268
268
  * @param {{ errors?: { title: string, detail: string }[] }|undefined} state
269
- * @param {string} path
269
+ * @param {string|RegExp} path
270
270
  * @returns {{ title: string, detail: string }|undefined}
271
271
  */
272
272
  export function getErrorFromState(state: {
@@ -274,7 +274,7 @@ export function getErrorFromState(state: {
274
274
  title: string;
275
275
  detail: string;
276
276
  }[];
277
- } | undefined, path: string): {
277
+ } | undefined, path: string | RegExp): {
278
278
  title: string;
279
279
  detail: string;
280
280
  } | undefined;
@@ -621,6 +621,7 @@ export function queryString(obj?: {
621
621
  * @param {{ [key: string]: any }} [data] - payload
622
622
  * @param {{preventDefault?: function}} [event] - event to prevent default
623
623
  * @param {[boolean, (value: boolean) => void]} [isLoading] - [isLoading, setIsLoading]
624
+ * @param {SetState} [setState] - if passed, state.errors will be reset before the request
624
625
  * @returns {Promise<any>}
625
626
  *
626
627
  * @example
@@ -631,7 +632,7 @@ export function request(route: string, data?: {
631
632
  [key: string]: any;
632
633
  }, event?: {
633
634
  preventDefault?: Function;
634
- }, isLoading?: [boolean, (value: boolean) => void]): Promise<any>;
635
+ }, isLoading?: [boolean, (value: boolean) => void], setState?: SetState): Promise<any>;
635
636
  /**
636
637
  * Removes undefined from an array or object
637
638
  * @param {[]|{[key: string]: any}} variable
@@ -784,4 +785,5 @@ export type Image = {
784
785
  base64?: string;
785
786
  date?: number;
786
787
  };
788
+ export type SetState = import("react").Dispatch<import("react").SetStateAction<any>>;
787
789
  //# sourceMappingURL=util.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../util.js"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../util.js"],"names":[],"mappings":"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"}
package/util.js CHANGED
@@ -4,6 +4,8 @@ import dateformat from 'dateformat'
4
4
  import { loadStripe } from '@stripe/stripe-js/pure.js' // pure removes ping
5
5
  import { twMerge as _twMerge } from 'tailwind-merge'
6
6
 
7
+ /** @typedef {import('react').Dispatch<import('react').SetStateAction<any>>} SetState */
8
+
7
9
  /** @type {{[key: string]: {[key: string]: string|true}}} */
8
10
  let queryObjectCache = {}
9
11
 
@@ -689,7 +691,7 @@ export function getCurrencyOptions (currencies) {
689
691
  /**
690
692
  * Returns an error from a state object matching the path
691
693
  * @param {{ errors?: { title: string, detail: string }[] }|undefined} state
692
- * @param {string} path
694
+ * @param {string|RegExp} path
693
695
  * @returns {{ title: string, detail: string }|undefined}
694
696
  */
695
697
  export function getErrorFromState (state, path) {
@@ -1403,13 +1405,14 @@ export function queryString (obj, _path='', _output) {
1403
1405
  * @param {{ [key: string]: any }} [data] - payload
1404
1406
  * @param {{preventDefault?: function}} [event] - event to prevent default
1405
1407
  * @param {[boolean, (value: boolean) => void]} [isLoading] - [isLoading, setIsLoading]
1408
+ * @param {SetState} [setState] - if passed, state.errors will be reset before the request
1406
1409
  * @returns {Promise<any>}
1407
1410
  *
1408
1411
  * @example
1409
1412
  * - request('post /api/user', { name: 'John' })
1410
1413
  * - request(`get /api/user/${id}`, undefined, e, isLoading)
1411
1414
  */
1412
- export async function request (route, data, event, isLoading) {
1415
+ export async function request (route, data, event, isLoading, setState) {
1413
1416
  try {
1414
1417
  if (event?.preventDefault) event.preventDefault()
1415
1418
  const uri = route.replace(/^(post|put|delete|get) /, '')
@@ -1426,7 +1429,7 @@ export async function request (route, data, event, isLoading) {
1426
1429
 
1427
1430
  // warning, not persisting through re-renders, but should be fine until loading is finished
1428
1431
  data = data || {}
1429
- delete data.errors
1432
+ if (setState) setState((/** @type {{[key: string]: any}} */prev) => ({ ...prev, errors: [] }))
1430
1433
 
1431
1434
  // Find out if the data has files?
1432
1435
  let hasFiles = false
@@ -1439,7 +1442,7 @@ export async function request (route, data, event, isLoading) {
1439
1442
 
1440
1443
  // If yes, convert to form data
1441
1444
  /** @type {FormData|undefined} */
1442
- const formData2 = hasFiles ? formData(data, { allowEmptyArrays: true, indices: true }) : undefined
1445
+ const formData2 = hasFiles ? formData({ ...data }, { allowEmptyArrays: true, indices: true }) : undefined
1443
1446
 
1444
1447
  // send the request
1445
1448
  const axiosPromise = (method === 'get' || method === 'delete')
@@ -1,139 +0,0 @@
1
- // @ts-nocheck
2
- // todo: finish tailwind conversion
3
- import * as util from 'nitro-web/util'
4
- import SvgTick from 'nitro-web/client/imgs/icons/tick.svg'
5
- import { Button, FormError, Field, Modal, Topbar, Tabbar } from 'nitro-web'
6
-
7
- export function SettingsAccount() {
8
- const isLoading = useState(false)
9
- const [removeModal, setRemoveModal] = useState()
10
- const [{user}, setStore] = sharedStore.useTracked()
11
- const [state, setState] = useState({
12
- avatar: user.avatar || '',
13
- email: user.email || '',
14
- firstName: user.firstName || '',
15
- lastName: user.lastName || '',
16
- })
17
-
18
- async function onSubmit (e) {
19
- try {
20
- const res = await util.request(`put /api/user/${user._id}?files=true`, state, e, isLoading)
21
- setStore((s) => ({ ...s, user: { ...s.user, ...res }, message: 'Saved successfully 👍️' }))
22
- } catch (errors) {
23
- return setState({ ...state, errors })
24
- }
25
- }
26
-
27
- return (
28
- <div css={style}>
29
- <Topbar
30
- title={<>Settings</>}
31
- submenu={
32
- <Tabbar class="is-underline"tabs={[
33
- { label: 'Business', path: '/settings/business' },
34
- { label: 'Team', path: '/settings/team' },
35
- { label: 'Account', path: '/settings/account' },
36
- ]} />
37
- }
38
- btns={
39
- <Button onClick={onSubmit} color="primary-sm" size="wide" IconLeft={SvgTick} isLoading={isLoading[0]}>
40
- Save Settings
41
- </Button>
42
- }
43
- />
44
- <div class="box p-box">
45
- <h3 class="h3">Account Info</h3>
46
-
47
- <form class="form" onSubmit={onSubmit}>
48
- <div class="cols cols-6 cols-gap-3">
49
- <div class="col">
50
- <label for="firstName">First Name(s)</label>
51
- <Field name="firstName" placeholder="E.g. Bruce" state={state} onChange={(e) => onChange(setState, e)} />
52
- </div>
53
- <div class="col">
54
- <label for="lastName">Last Name</label>
55
- <Field name="lastName" placeholder="E.g. Wayne" state={state} onChange={(e) => onChange(setState, e)} />
56
- </div>
57
- <div class="col">
58
- <label for="email">Email Address</label>
59
- <Field name="email" type="email" placeholder="Your email address..." state={state}
60
- onChange={(e) => onChange(setState, e)} />
61
- </div>
62
- <div class="col">
63
- <Link to="/reset" class="label-right link2 underline2 is-active">Reset Password?</Link>
64
- <label for="password">Password</label>
65
- <Field name="password" placeholder="•••••••••••" disabled={true} />
66
- </div>
67
- </div>
68
-
69
- <div class="py-0-5 mb-12">
70
- Warning: to remove all your data and delete your
71
- account, <a href="#" onClick={() => setRemoveModal(user)} class="link2 underline2 is-active">click here</a>.
72
- <FormError state={state} class="pt-2" />
73
- </div>
74
- </form>
75
- </div>
76
-
77
- <RemoveModal show={removeModal} setShow={setRemoveModal} />
78
- </div>
79
- )
80
- }
81
-
82
- export function RemoveModal ({ show, setShow }) {
83
- // @param {object} showModal - user
84
- const navigate = useNavigate()
85
- const isLoading = useState(false)
86
- const [, setStore] = sharedStore.useTracked()
87
- const [state, setState] = useState({})
88
-
89
- useEffect(() => {
90
- if (show?._id) setState({ _id: show._id })
91
- }, [show?._id])
92
-
93
- async function onSubmit (e) {
94
- try {
95
- await util.request(`delete /api/account/${state._id}`, null, e, isLoading)
96
- close()
97
- setStore(o => ({ ...o, message: 'Data deleted successfully, Goodbye 👋...' }))
98
- setTimeout(() => navigate('/signout'), 6000) // wait for setStore
99
- } catch (errors) {
100
- return setState({ ...state, errors })
101
- }
102
- }
103
-
104
- function close() {
105
- setShow(false)
106
- setTimeout(() => setState(false), 300)
107
- }
108
-
109
- return (
110
- <Modal show={show} setShow={close} css={style} class="p-modal-small" maxWidth={560} minHeight={0}>
111
- <h2 class="h2"><em>Delete</em> Your Account?</h2>
112
- <p class="text-paragraph py-2">
113
- This will remove all the data against your account and including all companies owned by you.<br/>
114
- <br/>
115
- <b>Warning:</b> This cannot be undone.
116
- </p>
117
- <form class="form" onSubmit={onSubmit}>
118
- <div class="py-0-5 mb-4">
119
- <FormError state={state} class="pt-2" />
120
- </div>
121
- <Button onClick={onSubmit} color="secondary-sm" isLoading={isLoading[0]}>
122
- Delete Account
123
- </Button>
124
- </form>
125
- </Modal>
126
- )
127
- }
128
-
129
- import { css } from 'twin.macro'
130
- const style = css`
131
- /* input[type='file'] {
132
- padding: 8px 18px;
133
- font-size: 12px;
134
- }
135
- .avatar {
136
- width: 38px;
137
- height: 38px;
138
- } */
139
- `
@@ -1,117 +0,0 @@
1
- //@ts-nocheck
2
- // todo: finish tailwind conversio
3
-
4
- ////// look at the select type error below
5
- import * as util from 'nitro-web/util'
6
- import SvgTick from 'nitro-web/client/imgs/icons/tick.svg'
7
- import { Button, Field, Select, Topbar, Tabbar, injectedConfig } from 'nitro-web'
8
-
9
- export function SettingsBusiness() {
10
- const isLoading = useState(false)
11
- const [{ user }, setStore] = sharedStore.useTracked()
12
- const [state, setState] = useState(() => {
13
- const company = user.company
14
- return {
15
- business: {
16
- address: company.business.address?.full || '',
17
- country: company.business.country || 'nz',
18
- currency: company.business.currency || 'nzd',
19
- name: company.business.name || '',
20
- number: company.business.number || '',
21
- phone: company.business.phone || '',
22
- website: company.business.website || '',
23
- },
24
- }
25
- })
26
-
27
- async function onSubmit (e) {
28
- try {
29
- const company = await util.request(`put /api/company/${user.company._id}`, state, e, isLoading)
30
- setStore((s) => ({ ...s, user: { ...s.user, company }, message: 'Saved successfully 👍️' }))
31
- } catch (errors) {
32
- console.log(errors)
33
- return setState({ ...state, errors })
34
- }
35
- }
36
-
37
- return (
38
- <div>
39
- <Topbar
40
- title={<>Settings</>}
41
- submenu={
42
- <Tabbar class="is-underline" tabs={[
43
- { label: 'Business', path: '/settings/business' },
44
- { label: 'Team', path: '/settings/team' },
45
- { label: 'Account', path: '/settings/account' },
46
- ]} />
47
- }
48
- btns={
49
- <Button onClick={onSubmit} color="primary-sm" size="wide" IconLeft={SvgTick} isLoading={isLoading[0]}>
50
- Save Settings
51
- </Button>
52
- }
53
- />
54
-
55
- <div class="box p-box">
56
- <h3 class="h3">Business Settings</h3>
57
-
58
- <form class="form" onSubmit={onSubmit}>
59
- <div class="cols cols-6 cols-gap-3">
60
- <div class="col">
61
- <label for="business.country">Country</label>
62
- <Select
63
- // https://github.com/lipis/flag-icons
64
- name="business.country"
65
- type="country"
66
- state={state}
67
- options={useMemo(() => util.getCountryOptions(injectedConfig.countries), [])}
68
- onChange={(e) => onChange(setState, e)}
69
- />
70
- </div>
71
- <div class="col">
72
- <label for="business.currency">Currency</label>
73
- <Select
74
- name="business.currency"
75
- type="country"
76
- state={state}
77
- options={useMemo(() => util.getCurrencyOptions(injectedConfig.currencies), [])}
78
- onChange={(e) => onChange(setState, e)}
79
- />
80
- </div>
81
- <div class="col">
82
- <label for="business.name">Trading Name</label>
83
- <Field name="business.name" placeholder="E.g. Wayne Enterprises" state={state} onChange={(e) => onChange(setState, e)} />
84
- </div>
85
- <div class="col">
86
- <Link to="#" class="label-right link2 underline2 is-active">Custom Address</Link>
87
- <label for="business.address">Address (Start Typing...)</label>
88
- <Field name="business.address.full" placeholder="" state={state} onChange={(e) => onChange(setState, e)} />
89
- </div>
90
- <div class="col">
91
- <label for="business.website">Website</label>
92
- <Field name="business.website" placeholder="https://" state={state} onChange={(e) => onChange(setState, e)} />
93
- </div>
94
- <div class="col">
95
- <label for="business.phone">Mobile Number</label>
96
- <Field name="business.phone" placeholder="" state={state} onChange={(e) => onChange(setState, e)} />
97
- </div>
98
- <div class="col">
99
- <Link to="#" class="label-right link2 underline2 is-active">What&apos;s this for?</Link>
100
- <label for="tax.number">GST Number</label>
101
- <Field class="mb-0" name="tax.number" placeholder="Appears on your documents" state={state}
102
- onChange={(e) => onChange(setState, e)} />
103
- </div>
104
- <div class="col">
105
- <Link to="#" class="label-right link2 underline2 is-active">What&apos;s this for?</Link>
106
- <label for="business.number">NZBN</label>
107
- <Field class="mb-0" name="business.number" type="text" rows="23" placeholder="Appears on your documents" state={state}
108
- onChange={(e) => onChange(setState, e)} />
109
- </div>
110
- </div>
111
- </form>
112
- </div>
113
-
114
- </div>
115
- )
116
- }
117
-
@@ -1,106 +0,0 @@
1
- // @ts-nocheck
2
- // todo: finish tailwind conversion
3
- import { Button, FormError, Field, Modal, Select, injectedConfig } from 'nitro-web'
4
- import SvgTick from 'nitro-web/client/imgs/icons/tick.svg'
5
-
6
- type SettingsTeamMemberProps = {
7
- showModal: boolean
8
- setShowModal: (showModal: boolean) => void
9
- }
10
-
11
- export function SettingsTeamMember ({ showModal, setShowModal }: SettingsTeamMemberProps) {
12
- // @param {object} showModal - user
13
- const [{ user }] = sharedStore.useTracked()
14
- const [isLoading] = useState(false)
15
- const [state, setState] = useState({
16
- business: {
17
- name: '',
18
- address: '',
19
- website: '',
20
- phone: '',
21
- },
22
- })
23
-
24
- // permit polit changes
25
- // typescripty,
26
-
27
- function onSubmit(_e) {
28
- //... save
29
- }
30
-
31
- return (
32
- <Modal show={showModal} setShow={setShowModal} class="p-modal">
33
-
34
- <h2 class="h2"><em>Add</em> Team Member</h2>
35
- <p class="subtitle">Invite a new team member to collaborate with you on {injectedConfig?.name || 'Nitro'}.</p>
36
-
37
- <form class="form" onSubmit={onSubmit}>
38
- <div class="cols cols-6 cols-gap-2-5">
39
- <div class="col">
40
- <label for="role">Member Role</label>
41
- <Select
42
- name="role"
43
- isSearchable={false}
44
- placeholder="Select a role"
45
- onChange={(e) => onChange(setState, e)}
46
- state={state}
47
- minMenuWidth={460}
48
- options={[
49
- {
50
- className: 'bb',
51
- value: 'owner',
52
- labelControl: 'Owner',
53
- label: <>
54
- <div class="mb-0-5"><b>Owner</b></div>
55
- <div>Full access.</div>
56
- </>,
57
- },
58
- {
59
- className: 'bb',
60
- value: 'manager',
61
- labelControl: 'Manager',
62
- label: <>
63
- <div class="mb-0-5"><b>Manager</b></div>
64
- <div>No access to billing or the ability to remove your account.</div>
65
- </>,
66
- },
67
- ]}
68
- />
69
- </div>
70
- <div class="col">
71
- <label for="email">Email Address</label>
72
- <Field
73
- name="email" type="email" placeholder="Your email address..." state={state}
74
- onChange={(e) => onChange(setState, e)}
75
- />
76
- </div>
77
- <div class="col">
78
- <label for="firstName">First Name</label>
79
- <Field name="firstName" placeholder="E.g. Bruce" state={state} onChange={(e) => onChange(setState, e)} />
80
- </div>
81
- <div class="col">
82
- <label for="lastName">Last Name</label>
83
- <Field name="lastName" placeholder="E.g. Wayne" state={state} onChange={(e) => onChange(setState, e)} />
84
- </div>
85
- <div class="col-12">
86
- <label for="message">Invitation Message</label>
87
- <Field
88
- name="message"
89
- type="textarea"
90
- placeholder={`${user.firstName} is inviting you to collaborate on ${injectedConfig?.name || 'Nitro'}.`}
91
- state={state}
92
- onChange={(e) => onChange(setState, e)}
93
- />
94
- </div>
95
- </div>
96
-
97
- <div class="py-0-5 mb-4">
98
- <FormError state={state} class="pt-2" />
99
- </div>
100
- <Button onClick={onSubmit} color="primary-sm" IconLeft={SvgTick} isLoading={isLoading[0]}>
101
- Send Invitation
102
- </Button>
103
- </form>
104
- </Modal>
105
- )
106
- }
@@ -1,72 +0,0 @@
1
- // @ts-nocheck
2
- // todo: finish tailwind conversion
3
- import * as util from 'nitro-web/util'
4
- import SvgPlus from 'nitro-web/client/imgs/icons/plus.svg'
5
- import { Button, Table, Avatar, Tabbar, Topbar, SettingsTeamMember, injectedConfig } from 'nitro-web'
6
-
7
- export function SettingsTeam() {
8
- const isLoading = useState(false)
9
- const [showModal, setShowModal] = useState()
10
- const [{ user }] = sharedStore.useTracked()
11
- const [state] = useState({
12
- users: user?.company?.users || [],
13
- })
14
-
15
- function addTeamMember() {
16
- //... open modal
17
- }
18
-
19
- return (
20
- <div>
21
- <Topbar
22
- title={<>Settings</>}
23
- submenu={
24
- <Tabbar class="is-underline" tabs={[
25
- { label: 'Business', path: '/settings/business' },
26
- { label: 'Team', path: '/settings/team' },
27
- { label: 'Account', path: '/settings/account' },
28
- ]} />
29
- }
30
- btns={
31
- <Button onClick={addTeamMember} color="primary-sm" IconLeft={SvgPlus} isLoading={isLoading[0]}>
32
- Add Team Member
33
- </Button>
34
- }
35
- />
36
-
37
- <Table
38
- columns={[
39
- { label: 'Member\'s Name', key: 'name', width: 1 },
40
- { label: 'Email', key: 'email' },
41
- { label: 'Joined On', key: 'joinedOn', align: 'center' },
42
- { label: 'Role', key: 'role', width: '110px' },
43
- ]}
44
- rowOnClick={(e, user) => setShowModal(user)}
45
- rows={
46
- state.users.map(user => ({
47
- ...user,
48
- key: user._id,
49
- name: (
50
- <>
51
- <Avatar awsUrl={injectedConfig.awsUrl} user={user} isRound={true} class="mt--1 mb--1" />
52
- <b>{util.ucFirst(user.name)}</b>
53
- {user.status != 'invited' && <span class="text-grey">(Invitation pending)</span>}
54
- </>
55
- ),
56
- joinedOn: user.status == 'invited' ? <a href="#">Resend Invite</a> : util.date(user.createdAt),
57
- role: util.ucFirst(user.role),
58
- }))
59
- }
60
- actions={[
61
- { label: 'Remove', onClick: (_row, _i) => console.log('remove') },
62
- ]}
63
- actionsAll={[
64
- { label: 'Remove All', onClick: () => console.log('remove all') },
65
- ]}
66
- />
67
-
68
- {/* Member modal */}
69
- <SettingsTeamMember showModal={showModal} setShowModal={setShowModal} />
70
- </div>
71
- )
72
- }
@@ -1,51 +0,0 @@
1
- // @ts-nocheck
2
- import db from 'monastery'
3
-
4
- export const routes = {
5
- 'put /api/company/:cid': ['isCompanyUser', update],
6
- 'put /api/user/:uid': ['isUser', updateUser],
7
- }
8
-
9
- async function update(req, res) {
10
- try {
11
- const update = await db.company.update({
12
- query: req.params.cid,
13
- data: req.body,
14
- files: req.query.files ? req.files : undefined,
15
- })
16
- if (!update) {
17
- throw new Error('Coudln\'t find the company to update')
18
- }
19
- const company = await db.company.findOne({
20
- query: req.params.cid,
21
- populate: db.company.loginPopulate(),
22
- _privateData: true,
23
- })
24
- res.json(company)
25
-
26
- } catch (errs) {
27
- res.error(errs)
28
- }
29
- }
30
-
31
- async function updateUser(req, res) {
32
- try {
33
- const update = await db.user.update({
34
- query: req.params.uid,
35
- data: req.body,
36
- files: req.query.files ? req.files : undefined,
37
- })
38
- if (!update) {
39
- throw new Error('Coudln\'t find the user to update')
40
- }
41
- const user = await db.user.findOne({
42
- query: req.params.uid,
43
- _privateData: true,
44
- blacklist: ['company'], // don't return the company id
45
- })
46
- res.json(user)
47
-
48
- } catch (errs) {
49
- res.error(errs)
50
- }
51
- }