@romain130492/topchopsticks 0.1.2
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/README.md +85 -0
- package/package.json +27 -0
- package/src/api/client.ts +79 -0
- package/src/api/types.ts +86 -0
- package/src/i18n/en.ts +322 -0
- package/src/i18n/index.ts +41 -0
- package/src/i18n/zh.ts +322 -0
- package/src/index.ts +10 -0
- package/src/tokens/icons.ts +21 -0
- package/src/tokens/index.ts +97 -0
- package/src/utils/applicationStatus.ts +65 -0
- package/src/utils/booking.ts +49 -0
- package/src/utils/eventMeta.ts +83 -0
- package/src/utils/humanize.ts +12 -0
- package/src/utils/questionRules.ts +45 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/** snake_case → "Business network · New friends". Never show a raw enum. */
|
|
2
|
+
export const humanizeValue = (value: string): string =>
|
|
3
|
+
String(value || '')
|
|
4
|
+
.split('_')
|
|
5
|
+
.filter(Boolean)
|
|
6
|
+
.map((word, index) => (index === 0 ? word.charAt(0).toUpperCase() + word.slice(1) : word))
|
|
7
|
+
.join(' ')
|
|
8
|
+
|
|
9
|
+
export const humanizeList = (values: unknown, separator = ' · '): string => {
|
|
10
|
+
const list = Array.isArray(values) ? values : [values]
|
|
11
|
+
return list.filter(Boolean).map((v) => humanizeValue(String(v))).join(separator)
|
|
12
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { TemplateQuestion } from '../api/types'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Conditional questions.
|
|
5
|
+
*
|
|
6
|
+
* `event_price` only makes sense once someone picks "pay later", so it carries
|
|
7
|
+
* validation_rules: { visible_when: { key: 'booking_mode', equals: 'pay_later' } }
|
|
8
|
+
*
|
|
9
|
+
* A question whose rule does not hold is not rendered AND not required — those
|
|
10
|
+
* two must move together. Requiring something the flow never showed is what
|
|
11
|
+
* made every 1-to-1 submission fail with "Missing required organizer answer".
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const asArray = (value: unknown): unknown[] => (Array.isArray(value) ? value : [value])
|
|
15
|
+
|
|
16
|
+
export function isQuestionVisible(
|
|
17
|
+
question: TemplateQuestion | null | undefined,
|
|
18
|
+
answers: Record<string, unknown> = {}
|
|
19
|
+
): boolean {
|
|
20
|
+
const rule = question?.validation_rules?.visible_when
|
|
21
|
+
if (!rule || !rule.key) return true
|
|
22
|
+
|
|
23
|
+
const actual = answers[rule.key]
|
|
24
|
+
|
|
25
|
+
if (rule.equals !== undefined) {
|
|
26
|
+
return asArray(actual).some((entry) => String(entry) === String(rule.equals))
|
|
27
|
+
}
|
|
28
|
+
if (Array.isArray(rule.in)) {
|
|
29
|
+
return asArray(actual).some((entry) => rule.in!.map(String).includes(String(entry)))
|
|
30
|
+
}
|
|
31
|
+
// A rule naming a key with no comparison means "answered at all".
|
|
32
|
+
return actual !== undefined && actual !== null && actual !== ''
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** An answer counts only if it is really there. */
|
|
36
|
+
export function isAnswerFilled(value: unknown): boolean {
|
|
37
|
+
if (Array.isArray(value)) return value.length > 0
|
|
38
|
+
// A restaurant answer is an object; it counts only once it carries a real
|
|
39
|
+
// restaurants.id, so "typed something in the search box" is not an answer.
|
|
40
|
+
if (value && typeof value === 'object') {
|
|
41
|
+
const v = value as Record<string, unknown>
|
|
42
|
+
return Boolean(v.id || v.restaurant_id)
|
|
43
|
+
}
|
|
44
|
+
return value !== undefined && value !== null && value !== ''
|
|
45
|
+
}
|