nitro-web 0.0.29 → 0.0.31
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/components/auth/auth.api.js +296 -292
- package/components/auth/signup.tsx +2 -2
- package/components/billing/stripe.api.js +231 -229
- package/components/dashboard/dashboard.tsx +6 -5
- package/components/partials/styleguide.tsx +6 -6
- package/components/settings/settings-business.tsx +4 -4
- package/components/settings/settings-team--member.tsx +4 -6
- package/components/settings/settings-team.tsx +3 -3
- package/components/settings/settings.api.js +40 -40
- package/package.json +1 -1
- package/server/index.js +1 -1
- package/server/models/user.js +1 -1
|
@@ -13,256 +13,258 @@ export default {
|
|
|
13
13
|
'post /api/stripe/create-billing-portal-session': ['isUser', 'billingPortalSessionCreate'],
|
|
14
14
|
'get /api/stripe/upcoming-invoices': ['isUser', 'upcomingInvoicesFind'],
|
|
15
15
|
},
|
|
16
|
+
setup: setup,
|
|
17
|
+
stripeWebhook: stripeWebhook,
|
|
18
|
+
billingPortalSessionCreate: billingPortalSessionCreate,
|
|
19
|
+
upcomingInvoicesFind: upcomingInvoicesFind,
|
|
20
|
+
}
|
|
16
21
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
stripe = new Stripe(config.stripeSecretKey)
|
|
31
|
-
},
|
|
32
|
-
|
|
33
|
-
stripeWebhook: async function (req, res) {
|
|
34
|
-
try {
|
|
35
|
-
var event = config.env == 'development' ? req.body : stripe.webhooks.constructEvent(
|
|
36
|
-
req.rawBody,
|
|
37
|
-
req.rawHeaders['stripe-signature'],
|
|
38
|
-
config.stripeWebhookSecret
|
|
39
|
-
)
|
|
40
|
-
} catch (err) {
|
|
41
|
-
if (err && err.message) console.log(err.message)
|
|
42
|
-
else console.log(err)
|
|
43
|
-
return res.error(err)
|
|
22
|
+
function setup(middleware, _config) {
|
|
23
|
+
// Set config values
|
|
24
|
+
config = {
|
|
25
|
+
env: _config.env,
|
|
26
|
+
clientUrl: _config.clientUrl,
|
|
27
|
+
stripeSecretKey: _config.stripeSecretKey,
|
|
28
|
+
stripeWebhookSecret: _config.stripeWebhookSecret,
|
|
29
|
+
}
|
|
30
|
+
for (let key in config) {
|
|
31
|
+
if (!config[key]) {
|
|
32
|
+
throw new Error(`Missing config value for stripe.api.js: ${key}`)
|
|
44
33
|
}
|
|
34
|
+
}
|
|
35
|
+
stripe = new Stripe(config.stripeSecretKey)
|
|
36
|
+
}
|
|
45
37
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
38
|
+
async function stripeWebhook(req, res) {
|
|
39
|
+
try {
|
|
40
|
+
var event = config.env == 'development' ? req.body : stripe.webhooks.constructEvent(
|
|
41
|
+
req.rawBody,
|
|
42
|
+
req.rawHeaders['stripe-signature'],
|
|
43
|
+
config.stripeWebhookSecret
|
|
44
|
+
)
|
|
45
|
+
} catch (err) {
|
|
46
|
+
if (err && err.message) console.log(err.message)
|
|
47
|
+
else console.log(err)
|
|
48
|
+
return res.error(err)
|
|
49
|
+
}
|
|
49
50
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
51
|
+
if (!event.data || !event.data.object) {
|
|
52
|
+
return res.status(400).send(`Missing webhook data: ${event}.`)
|
|
53
|
+
}
|
|
53
54
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
case 'customer.subscription.updated':
|
|
58
|
-
case 'customer.subscription.deleted':
|
|
59
|
-
// Subscriptions can be renewed which resurrects cancelled subscriptions.. ignore this
|
|
60
|
-
console.log('Event: ' + event.type)
|
|
61
|
-
this._webhookSubUpdated(req, res, event)
|
|
62
|
-
break
|
|
63
|
-
case 'customer.created': // customer created by subscribing
|
|
64
|
-
case 'customer.updated': // payment method changes
|
|
65
|
-
console.log('Event: ' + event.type)
|
|
66
|
-
this._webhookCustomerCreatedUpdated(req, res, event)
|
|
67
|
-
break
|
|
68
|
-
default:
|
|
69
|
-
res.status(400).send(`Unsupported type: ${event}.`)
|
|
70
|
-
break
|
|
71
|
-
}
|
|
72
|
-
},
|
|
55
|
+
// useful for cleaning failed webhooks
|
|
56
|
+
if (req.query.success) return true
|
|
57
|
+
// console.log('event.type: ', event.type)
|
|
73
58
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
59
|
+
// Stripe cannot guarantee event order
|
|
60
|
+
switch (event.type) {
|
|
61
|
+
case 'customer.subscription.created':
|
|
62
|
+
case 'customer.subscription.updated':
|
|
63
|
+
case 'customer.subscription.deleted':
|
|
64
|
+
// Subscriptions can be renewed which resurrects cancelled subscriptions.. ignore this
|
|
65
|
+
console.log('Event: ' + event.type)
|
|
66
|
+
webhookSubUpdated(req, res, event)
|
|
67
|
+
break
|
|
68
|
+
case 'customer.created': // customer created by subscribing
|
|
69
|
+
case 'customer.updated': // payment method changes
|
|
70
|
+
console.log('Event: ' + event.type)
|
|
71
|
+
webhookCustomerCreatedUpdated(req, res, event)
|
|
72
|
+
break
|
|
73
|
+
default:
|
|
74
|
+
res.status(400).send(`Unsupported type: ${event}.`)
|
|
75
|
+
break
|
|
76
|
+
}
|
|
77
|
+
}
|
|
88
78
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
customer: req.user.stripeCustomer.id,
|
|
94
|
-
})
|
|
95
|
-
res.json(nextInvoice)
|
|
96
|
-
} catch (err) {
|
|
97
|
-
if (err.code == 'invoice_upcoming_none') return res.json({})
|
|
98
|
-
this._error(req, res, err)
|
|
79
|
+
async function billingPortalSessionCreate(req, res) {
|
|
80
|
+
try {
|
|
81
|
+
if (!req.user.stripeCustomer?.id) {
|
|
82
|
+
throw new Error('No stripe customer found for the user')
|
|
99
83
|
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
84
|
+
const session = await stripe.billingPortal.sessions.create({
|
|
85
|
+
customer: req.user.stripeCustomer.id,
|
|
86
|
+
return_url: config.clientUrl + '/subscriptions',
|
|
87
|
+
})
|
|
88
|
+
res.json(session.url)
|
|
89
|
+
} catch (err) {
|
|
90
|
+
error(req, res, err)
|
|
91
|
+
}
|
|
92
|
+
}
|
|
103
93
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
})
|
|
117
|
-
res.json({})
|
|
118
|
-
} catch (err) {
|
|
119
|
-
console.log(err)
|
|
120
|
-
this._error(req, res, err)
|
|
121
|
-
}
|
|
122
|
-
},
|
|
94
|
+
async function upcomingInvoicesFind(req, res) {
|
|
95
|
+
try {
|
|
96
|
+
if (!req.user.stripeCustomer?.id) return res.json({})
|
|
97
|
+
const nextInvoice = await stripe.invoices.retrieveUpcoming({
|
|
98
|
+
customer: req.user.stripeCustomer.id,
|
|
99
|
+
})
|
|
100
|
+
res.json(nextInvoice)
|
|
101
|
+
} catch (err) {
|
|
102
|
+
if (err.code == 'invoice_upcoming_none') return res.json({})
|
|
103
|
+
error(req, res, err)
|
|
104
|
+
}
|
|
105
|
+
}
|
|
123
106
|
|
|
124
|
-
|
|
125
|
-
// Update the subscription on the company
|
|
126
|
-
try {
|
|
127
|
-
const subData = event.data.object
|
|
128
|
-
// webhook from deleting a company?
|
|
129
|
-
if (subData.cancellation_details.comment == 'company deleted') {
|
|
130
|
-
return res.json({})
|
|
131
|
-
}
|
|
107
|
+
/* Private webhook actions */
|
|
132
108
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
109
|
+
async function webhookCustomerCreatedUpdated(req, res, event) {
|
|
110
|
+
try {
|
|
111
|
+
const customer = event.data.object
|
|
112
|
+
const user = await getUserFromEvent(event)
|
|
113
|
+
const customerExpanded = await stripe.customers.retrieve(
|
|
114
|
+
customer.id,
|
|
115
|
+
{ expand: ['invoice_settings.default_payment_method'] }
|
|
116
|
+
)
|
|
117
|
+
await db.user.update({
|
|
118
|
+
query: user._id,
|
|
119
|
+
data: { stripeCustomer: customerExpanded },
|
|
120
|
+
blacklist: ['-stripeCustomer'],
|
|
121
|
+
})
|
|
122
|
+
res.json({})
|
|
123
|
+
} catch (err) {
|
|
124
|
+
console.log(err)
|
|
125
|
+
error(req, res, err)
|
|
126
|
+
}
|
|
127
|
+
}
|
|
137
128
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
} else if (subData.created < (user.company.stripeSubscription?.created || 0)) {
|
|
146
|
-
return res.json({ ignoringOldSubscriptions: true })
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
// Update company with the updated subscription and users
|
|
150
|
-
const sub = await stripe.subscriptions.retrieve(
|
|
151
|
-
subData.id,
|
|
152
|
-
{ expand: ['latest_invoice.payment_intent'] }
|
|
153
|
-
)
|
|
154
|
-
await db.company.update({
|
|
155
|
-
query: user.company._id,
|
|
156
|
-
data: { stripeSubscription: sub, users: user.company.users },
|
|
157
|
-
blacklist: ['-stripeSubscription', '-users'],
|
|
158
|
-
})
|
|
159
|
-
|
|
160
|
-
res.json({})
|
|
161
|
-
} catch (err) {
|
|
162
|
-
console.error(err)
|
|
163
|
-
this._error(req, res, err)
|
|
129
|
+
async function webhookSubUpdated(req, res, event) {
|
|
130
|
+
// Update the subscription on the company
|
|
131
|
+
try {
|
|
132
|
+
const subData = event.data.object
|
|
133
|
+
// webhook from deleting a company?
|
|
134
|
+
if (subData.cancellation_details.comment == 'company deleted') {
|
|
135
|
+
return res.json({})
|
|
164
136
|
}
|
|
165
|
-
},
|
|
166
|
-
|
|
167
|
-
/* Private */
|
|
168
137
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
let object = event.data.object
|
|
173
|
-
let customerId = object.object == 'customer'? object.id : object.customer
|
|
174
|
-
if (customerId) {
|
|
175
|
-
var user = await db.user.findOne({
|
|
176
|
-
query: { 'stripeCustomer.id': customerId },
|
|
177
|
-
populate: db.user.populate({}),
|
|
178
|
-
blacklist: false, // ['-company.users.inviteToken'],
|
|
179
|
-
})
|
|
138
|
+
const user = await getUserFromEvent(event)
|
|
139
|
+
if (!user.company) {
|
|
140
|
+
throw new Error(`Subscription user has no company to update the subscription (${subData.id}) onto`)
|
|
180
141
|
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
user.company.users = user.company.users.map(o => {
|
|
192
|
-
if (o.role == 'owner' && o._id.toString() == user._id.toString()) {
|
|
193
|
-
o.firstName = user.firstName
|
|
194
|
-
o.name = user.name
|
|
195
|
-
o.email = user.email
|
|
196
|
-
}
|
|
197
|
-
return o
|
|
198
|
-
})
|
|
142
|
+
|
|
143
|
+
// Ignoring incomplete subscriptions
|
|
144
|
+
if (subData.status.match(/incomplete/)) {
|
|
145
|
+
return res.json({})
|
|
146
|
+
// Ignoring subscriptions without companyId (e.g. manual subscriptions)
|
|
147
|
+
} else if (!subData.metadata.companyId) {
|
|
148
|
+
return res.json({ ignoringManualSubscriptions: true })
|
|
149
|
+
// Ignoring old subscriptions
|
|
150
|
+
} else if (subData.created < (user.company.stripeSubscription?.created || 0)) {
|
|
151
|
+
return res.json({ ignoringOldSubscriptions: true })
|
|
199
152
|
}
|
|
200
|
-
|
|
201
|
-
|
|
153
|
+
|
|
154
|
+
// Update company with the updated subscription and users
|
|
155
|
+
const sub = await stripe.subscriptions.retrieve(
|
|
156
|
+
subData.id,
|
|
157
|
+
{ expand: ['latest_invoice.payment_intent'] }
|
|
158
|
+
)
|
|
159
|
+
await db.company.update({
|
|
160
|
+
query: user.company._id,
|
|
161
|
+
data: { stripeSubscription: sub, users: user.company.users },
|
|
162
|
+
blacklist: ['-stripeSubscription', '-users'],
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
res.json({})
|
|
166
|
+
} catch (err) {
|
|
167
|
+
console.error(err)
|
|
168
|
+
error(req, res, err)
|
|
169
|
+
}
|
|
170
|
+
}
|
|
202
171
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
172
|
+
async function getUserFromEvent(event) {
|
|
173
|
+
// User retreived from the event's customer.
|
|
174
|
+
// The customer is created before the paymentIntent and subscriptionIntent is set up
|
|
175
|
+
let object = event.data.object
|
|
176
|
+
let customerId = object.object == 'customer'? object.id : object.customer
|
|
177
|
+
if (customerId) {
|
|
178
|
+
var user = await db.user.findOne({
|
|
179
|
+
query: { 'stripeCustomer.id': customerId },
|
|
180
|
+
populate: db.user.populate({}),
|
|
181
|
+
blacklist: false, // ['-company.users.inviteToken'],
|
|
182
|
+
})
|
|
183
|
+
}
|
|
184
|
+
if (!user) {
|
|
185
|
+
await db.log.insert({ data: {
|
|
186
|
+
date: Date.now(),
|
|
187
|
+
event: event.type,
|
|
188
|
+
message: `Cannot find user with id: ${customerId}.`,
|
|
189
|
+
}})
|
|
190
|
+
throw new Error(`Cannot find user with id: ${customerId}.`)
|
|
191
|
+
}
|
|
192
|
+
// populate company owner with user data (handy for _addSubscriptionBillingChange)
|
|
193
|
+
if (user.company?.users) {
|
|
194
|
+
user.company.users = user.company.users.map(o => {
|
|
195
|
+
if (o.role == 'owner' && o._id.toString() == user._id.toString()) {
|
|
196
|
+
o.firstName = user.firstName
|
|
197
|
+
o.name = user.name
|
|
198
|
+
o.email = user.email
|
|
213
199
|
}
|
|
200
|
+
return o
|
|
201
|
+
})
|
|
202
|
+
}
|
|
203
|
+
return user
|
|
204
|
+
}
|
|
214
205
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
...util.pick(price, ['id', 'product', 'nickname', 'recurring', 'unit_amount', 'tiers', 'tiers_mode']),
|
|
226
|
-
interval: price.recurring?.interval, // 'year', 'month', undefined
|
|
227
|
-
})),
|
|
228
|
-
})))
|
|
229
|
-
} catch (err) {
|
|
230
|
-
console.error(new Error(err)) // when stripe throws errors, the callstack is missing.
|
|
231
|
-
return []
|
|
206
|
+
export async function getProducts() {
|
|
207
|
+
/**
|
|
208
|
+
* Returns all products and caches it on the app
|
|
209
|
+
* @returns {Array} products
|
|
210
|
+
*/
|
|
211
|
+
try {
|
|
212
|
+
if (stripeProducts) return stripeProducts
|
|
213
|
+
if (!config.stripeSecretKey) {
|
|
214
|
+
stripeProducts = []
|
|
215
|
+
throw new Error('Missing process.env.stripeSecretKey for retrieving products')
|
|
232
216
|
}
|
|
233
|
-
},
|
|
234
217
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
* Creates or updates a stripe customer and saves it to the user
|
|
238
|
-
* @param {Object} user - user
|
|
239
|
-
* @param {String} paymentMethod - stripe payment method id to save to the customer
|
|
240
|
-
* @called before paymentIntent and subscriptionIntent, and after completion with paymentMethod
|
|
241
|
-
* @returns mutates user
|
|
242
|
-
*/
|
|
243
|
-
const data = {
|
|
244
|
-
email: user.email,
|
|
245
|
-
name: user.name,
|
|
246
|
-
address: { country: 'NZ' },
|
|
247
|
-
...(!paymentMethod ? {} : { invoice_settings: { default_payment_method: paymentMethod }}),
|
|
248
|
-
expand: ['invoice_settings.default_payment_method', 'tax'], // expands card object
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
if (user.stripeCustomer) var customer = await stripe.customers.update(user.stripeCustomer.id, data)
|
|
252
|
-
else customer = await stripe.customers.create({ ...data })
|
|
218
|
+
let products = (await stripe.products.list({ limit: 100, active: true })).data
|
|
219
|
+
let prices = (await stripe.prices.list({ limit: 100, active: true, expand: ['data.tiers'] })).data
|
|
253
220
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
221
|
+
return (stripeProducts = products.map((product) => ({
|
|
222
|
+
// remove default_price when new pricing is ready
|
|
223
|
+
...util.pick(product, ['id', 'created', 'default_price', 'description', 'name', 'metadata']),
|
|
224
|
+
type: product.name.match(/housing/i) ? 'project' : 'subscription', // overwriting, was 'service'
|
|
225
|
+
prices: prices
|
|
226
|
+
.filter((price) => price.product == product.id)
|
|
227
|
+
.map((price) => ({
|
|
228
|
+
...util.pick(price, ['id', 'product', 'nickname', 'recurring', 'unit_amount', 'tiers', 'tiers_mode']),
|
|
229
|
+
interval: price.recurring?.interval, // 'year', 'month', undefined
|
|
230
|
+
})),
|
|
231
|
+
})))
|
|
232
|
+
} catch (err) {
|
|
233
|
+
console.error(new Error(err)) // when stripe throws errors, the callstack is missing.
|
|
234
|
+
return []
|
|
235
|
+
}
|
|
236
|
+
}
|
|
261
237
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
}
|
|
267
|
-
|
|
238
|
+
// async function createOrUpdateCustomer(user, paymentMethod=null) {
|
|
239
|
+
// /**
|
|
240
|
+
// * Creates or updates a stripe customer and saves it to the user
|
|
241
|
+
// * @param {Object} user - user
|
|
242
|
+
// * @param {String} paymentMethod - stripe payment method id to save to the customer
|
|
243
|
+
// * @called before paymentIntent and subscriptionIntent, and after completion with paymentMethod
|
|
244
|
+
// * @returns mutates user
|
|
245
|
+
// */
|
|
246
|
+
// const data = {
|
|
247
|
+
// email: user.email,
|
|
248
|
+
// name: user.name,
|
|
249
|
+
// address: { country: 'NZ' },
|
|
250
|
+
// ...(!paymentMethod ? {} : { invoice_settings: { default_payment_method: paymentMethod }}),
|
|
251
|
+
// expand: ['invoice_settings.default_payment_method', 'tax'], // expands card object
|
|
252
|
+
// }
|
|
253
|
+
|
|
254
|
+
// if (user.stripeCustomer) var customer = await stripe.customers.update(user.stripeCustomer.id, data)
|
|
255
|
+
// else customer = await stripe.customers.create({ ...data })
|
|
256
|
+
|
|
257
|
+
// user.stripeCustomer = customer
|
|
258
|
+
// await db.user.update({
|
|
259
|
+
// query: user._id,
|
|
260
|
+
// data: { stripeCustomer: customer },
|
|
261
|
+
// blacklist: ['-stripeCustomer'],
|
|
262
|
+
// })
|
|
263
|
+
// }
|
|
264
|
+
|
|
265
|
+
async function error(req, res, err) {
|
|
266
|
+
if (err && err.response && err.response.body) console.log(err.response.body)
|
|
267
|
+
if (util.isString(err) && err.match(/Cannot find company with id/)) {
|
|
268
|
+
res.json({ user: 'no company found' })
|
|
269
|
+
} else res.error(err)
|
|
268
270
|
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { css, theme } from 'twin.macro'
|
|
2
|
+
import { injectedConfig } from 'nitro-web'
|
|
2
3
|
|
|
3
|
-
export function Dashboard(
|
|
4
|
+
export function Dashboard() {
|
|
4
5
|
const [store] = useTracked()
|
|
5
|
-
const textColor = store.apiAvailable ? 'text-green-700' :
|
|
6
|
-
const fillColor = store.apiAvailable ? 'fill-green-500' :
|
|
7
|
-
const bgColor = store.apiAvailable ? 'bg-green-100' :
|
|
6
|
+
const textColor = store.apiAvailable ? 'text-green-700' : injectedConfig.isStatic ? 'text-gray-700' : 'text-pink-700'
|
|
7
|
+
const fillColor = store.apiAvailable ? 'fill-green-500' : injectedConfig.isStatic ? 'fill-gray-500' : 'fill-pink-500'
|
|
8
|
+
const bgColor = store.apiAvailable ? 'bg-green-100' : injectedConfig.isStatic ? 'bg-[#eeeeee]' : 'bg-pink-100'
|
|
8
9
|
|
|
9
10
|
return (
|
|
10
11
|
<div css={style}>
|
|
@@ -17,7 +18,7 @@ export function Dashboard({ config }: { config: { isStatic?: boolean } }) {
|
|
|
17
18
|
<svg viewBox="0 0 6 6" aria-hidden="true" className={`size-1.5 ${fillColor}`}>
|
|
18
19
|
<circle r={3} cx={3} cy={3} />
|
|
19
20
|
</svg>
|
|
20
|
-
{ store.apiAvailable ? 'API Available' : `API Unavailable${
|
|
21
|
+
{ store.apiAvailable ? 'API Available' : `API Unavailable${injectedConfig.isStatic ? ' (Static Example)' : ''}` }
|
|
21
22
|
</span>
|
|
22
23
|
</p>
|
|
23
24
|
</div>
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { Drop, Dropdown, Field, Select, Button, Checkbox, GithubLink, Modal, Calendar, injectedConfig } from 'nitro-web'
|
|
2
2
|
import { getCountryOptions, getCurrencyOptions, ucFirst } from 'nitro-web/util'
|
|
3
3
|
import { CheckIcon } from '@heroicons/react/20/solid'
|
|
4
|
-
import { Config } from 'nitro-web/types'
|
|
5
4
|
|
|
6
|
-
export function Styleguide(
|
|
5
|
+
export function Styleguide() {
|
|
7
6
|
const [customerSearch, setCustomerSearch] = useState('')
|
|
8
7
|
const [showModal1, setShowModal1] = useState(false)
|
|
9
8
|
const [state, setState] = useState({
|
|
@@ -167,7 +166,7 @@ export function Styleguide({ config }: { config: Config }) {
|
|
|
167
166
|
name="country"
|
|
168
167
|
type="country"
|
|
169
168
|
state={state}
|
|
170
|
-
options={useMemo(() => getCountryOptions(
|
|
169
|
+
options={useMemo(() => getCountryOptions(injectedConfig.countries), [])}
|
|
171
170
|
onChange={onInputChange}
|
|
172
171
|
/>
|
|
173
172
|
</div>
|
|
@@ -204,7 +203,7 @@ export function Styleguide({ config }: { config: Config }) {
|
|
|
204
203
|
<Select
|
|
205
204
|
name="currency"
|
|
206
205
|
state={state}
|
|
207
|
-
options={useMemo(() => getCurrencyOptions(
|
|
206
|
+
options={useMemo(() => getCurrencyOptions(injectedConfig.currencies), [])}
|
|
208
207
|
onChange={onInputChange}
|
|
209
208
|
/>
|
|
210
209
|
</div>
|
|
@@ -249,7 +248,8 @@ export function Styleguide({ config }: { config: Config }) {
|
|
|
249
248
|
</div>
|
|
250
249
|
<div>
|
|
251
250
|
<label for="amount">Amount ({state.amount})</label>
|
|
252
|
-
<Field name="amount" type="currency" state={state} currency={state.currency || 'nzd'} onChange={onInputChange}
|
|
251
|
+
<Field name="amount" type="currency" state={state} currency={state.currency || 'nzd'} onChange={onInputChange}
|
|
252
|
+
config={injectedConfig} />
|
|
253
253
|
</div>
|
|
254
254
|
</div>
|
|
255
255
|
|
|
@@ -273,7 +273,7 @@ export function Styleguide({ config }: { config: Config }) {
|
|
|
273
273
|
<div class="grid grid-cols-3 gap-x-6 mb-4">
|
|
274
274
|
<div>
|
|
275
275
|
<label for="avatar">Avatar</label>
|
|
276
|
-
<Drop class="is-small" name="avatar" state={state} onChange={onInputChange} awsUrl={
|
|
276
|
+
<Drop class="is-small" name="avatar" state={state} onChange={onInputChange} awsUrl={injectedConfig.awsUrl} />
|
|
277
277
|
</div>
|
|
278
278
|
<div>
|
|
279
279
|
<label for="calendar">Calendar</label>
|
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
////// look at the select type error below
|
|
5
5
|
import * as util from 'nitro-web/util'
|
|
6
6
|
import SvgTick from 'nitro-web/client/imgs/icons/tick.svg'
|
|
7
|
-
import { Button, Field, Select, Topbar, Tabbar } from 'nitro-web'
|
|
7
|
+
import { Button, Field, Select, Topbar, Tabbar, injectedConfig } from 'nitro-web'
|
|
8
8
|
|
|
9
|
-
export function SettingsBusiness(
|
|
9
|
+
export function SettingsBusiness() {
|
|
10
10
|
const isLoading = useState(false)
|
|
11
11
|
const [{ user }, setStore] = sharedStore.useTracked()
|
|
12
12
|
const [state, setState] = useState(() => {
|
|
@@ -64,7 +64,7 @@ export function SettingsBusiness({ config }) {
|
|
|
64
64
|
name="business.country"
|
|
65
65
|
type="country"
|
|
66
66
|
state={state}
|
|
67
|
-
options={useMemo(() => util.getCountryOptions(
|
|
67
|
+
options={useMemo(() => util.getCountryOptions(injectedConfig.countries), [])}
|
|
68
68
|
onChange={onChange.bind(setState)}
|
|
69
69
|
/>
|
|
70
70
|
</div>
|
|
@@ -74,7 +74,7 @@ export function SettingsBusiness({ config }) {
|
|
|
74
74
|
name="business.currency"
|
|
75
75
|
type="country"
|
|
76
76
|
state={state}
|
|
77
|
-
options={useMemo(() => util.getCurrencyOptions(
|
|
77
|
+
options={useMemo(() => util.getCurrencyOptions(injectedConfig.currencies), [])}
|
|
78
78
|
onChange={onChange.bind(setState)}
|
|
79
79
|
/>
|
|
80
80
|
</div>
|
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
// @ts-nocheck
|
|
2
2
|
// todo: finish tailwind conversion
|
|
3
|
-
import { Button, FormError, Field, Modal, Select } from 'nitro-web'
|
|
3
|
+
import { Button, FormError, Field, Modal, Select, injectedConfig } from 'nitro-web'
|
|
4
4
|
import SvgTick from 'nitro-web/client/imgs/icons/tick.svg'
|
|
5
|
-
import { Config } from 'nitro-web/types'
|
|
6
5
|
|
|
7
6
|
type SettingsTeamMemberProps = {
|
|
8
7
|
showModal: boolean
|
|
9
8
|
setShowModal: (showModal: boolean) => void
|
|
10
|
-
config?: Config
|
|
11
9
|
}
|
|
12
10
|
|
|
13
|
-
export function SettingsTeamMember ({ showModal, setShowModal
|
|
11
|
+
export function SettingsTeamMember ({ showModal, setShowModal }: SettingsTeamMemberProps) {
|
|
14
12
|
// @param {object} showModal - user
|
|
15
13
|
const [{ user }] = sharedStore.useTracked()
|
|
16
14
|
const [isLoading] = useState(false)
|
|
@@ -34,7 +32,7 @@ export function SettingsTeamMember ({ showModal, setShowModal, config }: Setting
|
|
|
34
32
|
<Modal show={showModal} setShow={setShowModal} class="p-modal">
|
|
35
33
|
|
|
36
34
|
<h2 class="h2"><em>Add</em> Team Member</h2>
|
|
37
|
-
<p class="subtitle">Invite a new team member to collaborate with you on {
|
|
35
|
+
<p class="subtitle">Invite a new team member to collaborate with you on {injectedConfig?.name || 'Nitro'}.</p>
|
|
38
36
|
|
|
39
37
|
<form class="form" onSubmit={onSubmit}>
|
|
40
38
|
<div class="cols cols-6 cols-gap-2-5">
|
|
@@ -89,7 +87,7 @@ export function SettingsTeamMember ({ showModal, setShowModal, config }: Setting
|
|
|
89
87
|
<Field
|
|
90
88
|
name="message"
|
|
91
89
|
type="textarea"
|
|
92
|
-
placeholder={`${user.firstName} is inviting you to collaborate on ${
|
|
90
|
+
placeholder={`${user.firstName} is inviting you to collaborate on ${injectedConfig?.name || 'Nitro'}.`}
|
|
93
91
|
state={state} onChange={onChange.bind(setState)}
|
|
94
92
|
/>
|
|
95
93
|
</div>
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
// todo: finish tailwind conversion
|
|
3
3
|
import * as util from 'nitro-web/util'
|
|
4
4
|
import SvgPlus from 'nitro-web/client/imgs/icons/plus.svg'
|
|
5
|
-
import { Button, Table, Avatar, Tabbar, Topbar, SettingsTeamMember } from 'nitro-web'
|
|
5
|
+
import { Button, Table, Avatar, Tabbar, Topbar, SettingsTeamMember, injectedConfig } from 'nitro-web'
|
|
6
6
|
|
|
7
|
-
export function SettingsTeam(
|
|
7
|
+
export function SettingsTeam() {
|
|
8
8
|
const isLoading = useState(false)
|
|
9
9
|
const [showModal, setShowModal] = useState()
|
|
10
10
|
const [{ user }] = sharedStore.useTracked()
|
|
@@ -48,7 +48,7 @@ export function SettingsTeam({ config }) {
|
|
|
48
48
|
key: user._id,
|
|
49
49
|
name: (
|
|
50
50
|
<>
|
|
51
|
-
<Avatar awsUrl={
|
|
51
|
+
<Avatar awsUrl={injectedConfig.awsUrl} user={user} isRound={true} class="mt--1 mb--1" />
|
|
52
52
|
<b>{util.ucFirst(user.name)}</b>
|
|
53
53
|
{user.status != 'invited' && <span class="text-grey">(Invitation pending)</span>}
|
|
54
54
|
</>
|