@curless/hotel-storefront 0.57.0 → 0.58.0
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/dist/account/index.d.ts +15 -1
- package/dist/account/index.d.ts.map +1 -1
- package/dist/account/index.js +48 -36
- package/dist/account/index.js.map +1 -1
- package/dist/account/member-login.d.ts +58 -0
- package/dist/account/member-login.d.ts.map +1 -0
- package/dist/account/member-login.js +240 -0
- package/dist/account/member-login.js.map +1 -0
- package/dist/account/pages.d.ts +38 -1
- package/dist/account/pages.d.ts.map +1 -1
- package/dist/account/pages.js +105 -27
- package/dist/account/pages.js.map +1 -1
- package/dist/card.generated.d.ts +1 -1
- package/dist/card.generated.js +2 -2
- package/dist/card.generated.js.map +1 -1
- package/dist/config.d.ts +1 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +2 -0
- package/dist/config.js.map +1 -1
- package/dist/dates.d.ts +8 -7
- package/dist/dates.d.ts.map +1 -1
- package/dist/dates.js +13 -12
- package/dist/dates.js.map +1 -1
- package/dist/guests.d.ts +24 -0
- package/dist/guests.d.ts.map +1 -1
- package/dist/guests.js +50 -0
- package/dist/guests.js.map +1 -1
- package/dist/server.d.ts +2 -2
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +61 -4
- package/dist/server.js.map +1 -1
- package/dist/surface.d.ts.map +1 -1
- package/dist/surface.js +6 -4
- package/dist/surface.js.map +1 -1
- package/dist/tools.d.ts +41 -9
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +143 -54
- package/dist/tools.js.map +1 -1
- package/package.json +1 -1
package/dist/tools.js
CHANGED
|
@@ -140,7 +140,7 @@ export const homeListing = (t, brand, count) => {
|
|
|
140
140
|
};
|
|
141
141
|
};
|
|
142
142
|
export const withheldTools = (n, caps) => new Set([
|
|
143
|
-
...(caps.members ? [] : [n.signIn, n.signOut, n.forgetPreferences]),
|
|
143
|
+
...(caps.members ? [] : [n.signIn, n.signOut, n.forgetPreferences, n.memberCard]),
|
|
144
144
|
...(caps.extras ? [] : [n.listExtras]),
|
|
145
145
|
...(caps.changes || caps.extras ? [] : [n.previewChange, n.changeBooking]),
|
|
146
146
|
...(caps.policies === false ? [n.policies] : []),
|
|
@@ -280,7 +280,7 @@ const forCapabilities = (tools, n, v, caps) => {
|
|
|
280
280
|
if (t.name === n.signIn)
|
|
281
281
|
out = {
|
|
282
282
|
...out,
|
|
283
|
-
description: out.description.replace('
|
|
283
|
+
description: out.description.replace('it answers with their tier, their point balance', 'it answers with their tier, their point balance, the coupons they hold'),
|
|
284
284
|
};
|
|
285
285
|
}
|
|
286
286
|
// ⚠️ THE PICKED ROOM'S BOOKING TAKES THE SAME GUEST FIELDS as the trip's:
|
|
@@ -887,19 +887,43 @@ const allTools = (n, v) => {
|
|
|
887
887
|
},
|
|
888
888
|
{
|
|
889
889
|
name: n.signIn,
|
|
890
|
-
// ⚠️
|
|
891
|
-
//
|
|
892
|
-
//
|
|
893
|
-
//
|
|
894
|
-
description: `
|
|
890
|
+
// ⚠️ NOTHING ABOUT THE MEMBER IS ASKED FOR HERE (0.58.0, robin 11-06: 「我们
|
|
891
|
+
// 自己的登陆也要改成外链」): the membership number and the surname are typed
|
|
892
|
+
// on our page (account/member-login.ts), never in the chat - so no schema
|
|
893
|
+
// here has a field a model could fill with them.
|
|
894
|
+
description: `Starts signing a ${v.brand} loyalty member in for this conversation, on ${v.brand}'s own sign-in page: answers with a one-time link and a short code the page shows too, where the guest types their membership number and the surname on it; the chat does not carry them. Once they have, it answers with their tier, their point balance and what the tier takes off the public rate; room rates and trip totals are then quoted at the member rate until they are signed out.`,
|
|
895
895
|
annotations: edits(`Sign in to ${v.brand}`, open),
|
|
896
896
|
inputSchema: {
|
|
897
897
|
type: 'object',
|
|
898
898
|
properties: {
|
|
899
|
-
|
|
900
|
-
|
|
899
|
+
lang: {
|
|
900
|
+
type: 'string',
|
|
901
|
+
enum: ['zh', 'en'],
|
|
902
|
+
description: 'The language the page opens in: zh for a guest writing Chinese, en otherwise. The page has a switch.',
|
|
903
|
+
},
|
|
901
904
|
},
|
|
902
|
-
|
|
905
|
+
},
|
|
906
|
+
},
|
|
907
|
+
{
|
|
908
|
+
name: n.memberCard,
|
|
909
|
+
// The card's own (app-only, server.ts): never offered to the assistant.
|
|
910
|
+
description: `The booking card's own way to start a ${v.brand} membership sign-in, or to see whether its page has been used yet - for the conversation the card was drawn for.`,
|
|
911
|
+
annotations: edits(`${v.brand} membership (card)`, open),
|
|
912
|
+
inputSchema: {
|
|
913
|
+
type: 'object',
|
|
914
|
+
properties: {
|
|
915
|
+
action: {
|
|
916
|
+
type: 'string',
|
|
917
|
+
enum: ['sign_in', 'view'],
|
|
918
|
+
description: 'sign_in: a new one-time sign-in page; view: whether this conversation is signed in yet',
|
|
919
|
+
},
|
|
920
|
+
lang: {
|
|
921
|
+
type: 'string',
|
|
922
|
+
enum: ['zh', 'en'],
|
|
923
|
+
description: 'The language the page opens in',
|
|
924
|
+
},
|
|
925
|
+
},
|
|
926
|
+
required: ['action'],
|
|
903
927
|
},
|
|
904
928
|
},
|
|
905
929
|
{
|
|
@@ -1532,13 +1556,35 @@ export const buildOutputSchemas = (n) => ({
|
|
|
1532
1556
|
[n.signIn]: {
|
|
1533
1557
|
type: 'object',
|
|
1534
1558
|
properties: {
|
|
1535
|
-
view: { enum: ['member', 'member-
|
|
1559
|
+
view: { enum: ['member', 'member-sign-in', 'error'] },
|
|
1536
1560
|
brand: STR,
|
|
1537
1561
|
toolNames: OBJ,
|
|
1538
1562
|
bookingMode: STR,
|
|
1539
1563
|
demo: BOOL,
|
|
1540
1564
|
demoNote: STR,
|
|
1541
1565
|
member: OBJ,
|
|
1566
|
+
signedIn: BOOL,
|
|
1567
|
+
url: STR,
|
|
1568
|
+
shortCode: STR,
|
|
1569
|
+
expiresAt: STR,
|
|
1570
|
+
expiresInSeconds: { type: 'number' },
|
|
1571
|
+
code: STR,
|
|
1572
|
+
},
|
|
1573
|
+
required: ['view'],
|
|
1574
|
+
},
|
|
1575
|
+
[n.memberCard]: {
|
|
1576
|
+
type: 'object',
|
|
1577
|
+
properties: {
|
|
1578
|
+
view: { enum: ['member', 'member-sign-in', 'no-member', 'error'] },
|
|
1579
|
+
brand: STR,
|
|
1580
|
+
toolNames: OBJ,
|
|
1581
|
+
bookingMode: STR,
|
|
1582
|
+
member: OBJ,
|
|
1583
|
+
signedIn: BOOL,
|
|
1584
|
+
waiting: BOOL,
|
|
1585
|
+
expiresAt: STR,
|
|
1586
|
+
expiresInSeconds: { type: 'number' },
|
|
1587
|
+
code: STR,
|
|
1542
1588
|
},
|
|
1543
1589
|
required: ['view'],
|
|
1544
1590
|
},
|
|
@@ -2329,6 +2375,36 @@ notSent = false) => c.returned
|
|
|
2329
2375
|
const POINTS_DO_NOT_PAY = 'Points cannot pay for a room here, not even in part: what a point is worth has not been published, so give no rate for them.';
|
|
2330
2376
|
/** The rule a coupon is used by, said once where coupons are listed. */
|
|
2331
2377
|
const COUPON_RULE = 'One coupon per booking, taken off after the member rate. Use one only when the guest names it or agrees to it - using it spends it - by passing its id as `coupon` to the trip view (to price it) and to the booking.';
|
|
2378
|
+
/** Beside a member sign-in's answer where a card is drawn: the link and its short code, the card's alone. */
|
|
2379
|
+
export const MEMBER_SIGN_IN_META_KEY = 'curless/member-sign-in';
|
|
2380
|
+
const MEMBER_NEVER_IN_CHAT = 'Never in this chat: do not ask for, or accept, a membership number or a surname here, and never write back one the guest typed - not even to say which to change.';
|
|
2381
|
+
/**
|
|
2382
|
+
* A signed-in member, as the model reads it (0.58.0): signed in, the tier and
|
|
2383
|
+
* what it takes off, the points and the coupons - what pricing and booking
|
|
2384
|
+
* need. ⚠️ NOT THE NAME OR THE NUMBER: those are the card's, beside the answer
|
|
2385
|
+
* (server.ts `memberApart`) - the model is told who is signed in by what the
|
|
2386
|
+
* membership does, not who it is.
|
|
2387
|
+
*/
|
|
2388
|
+
const memberAnswer = (deps, key, member, lead) => ({
|
|
2389
|
+
structured: {
|
|
2390
|
+
view: 'member',
|
|
2391
|
+
...common(deps, key),
|
|
2392
|
+
member: memberForCard(member, deps.vocab.brand),
|
|
2393
|
+
},
|
|
2394
|
+
text: [
|
|
2395
|
+
`${lead ? `${lead}: ` : ''}signed in as a ${member.programme} ${member.tier.name} member.`,
|
|
2396
|
+
`Rates now show the ${member.tier.name} member rate, ${Math.round(member.tier.discount * 100)}% off the public one.`,
|
|
2397
|
+
...((deps.backend.capabilities ?? FULL_CAPABILITIES).coupons
|
|
2398
|
+
? memberSaid(member, deps.vocab.brand)
|
|
2399
|
+
: [POINTS_DO_NOT_PAY]),
|
|
2400
|
+
member.preferences?.length
|
|
2401
|
+
? `Saved preferences on this membership: ${member.preferences.map((k) => REQUEST_WORDS[k]).join(', ')}. They are ticked on the booking form; mention them, and ask before booking with them.`
|
|
2402
|
+
: '',
|
|
2403
|
+
]
|
|
2404
|
+
.filter(Boolean)
|
|
2405
|
+
.join('\n')
|
|
2406
|
+
.replace(/^signed/, 'Signed'),
|
|
2407
|
+
});
|
|
2332
2408
|
/** A signed-in member's coupons and points, for the sign-in answer - see `memberCouponsSaid`. */
|
|
2333
2409
|
const memberSaid = (m, brand) => [
|
|
2334
2410
|
`Points: ${m.points.toLocaleString('en-GB')}.${m.pointsExpiring ? ` ${m.pointsExpiring.points.toLocaleString('en-GB')} of them lapse on ${m.pointsExpiring.on}.` : ''} ${POINTS_DO_NOT_PAY}`,
|
|
@@ -7072,64 +7148,77 @@ const answerTool = async (deps, name, rawArgs, ctx) => {
|
|
|
7072
7148
|
.join('\n'),
|
|
7073
7149
|
};
|
|
7074
7150
|
}
|
|
7075
|
-
if (name === t.signIn) {
|
|
7076
|
-
const
|
|
7077
|
-
|
|
7078
|
-
//
|
|
7079
|
-
|
|
7080
|
-
|
|
7081
|
-
|
|
7082
|
-
|
|
7083
|
-
|
|
7084
|
-
|
|
7085
|
-
|
|
7086
|
-
if (
|
|
7151
|
+
if (name === t.signIn || name === t.memberCard) {
|
|
7152
|
+
const fromCard = name === t.memberCard;
|
|
7153
|
+
// ⚠️ THE CARD'S OWN WHERE A CARD IS DRAWN (app-only, server.ts): a client
|
|
7154
|
+
// that draws none is refused it - the code it hands out is the card's alone.
|
|
7155
|
+
if (fromCard && ctx.noCards)
|
|
7156
|
+
return {
|
|
7157
|
+
structured: { view: 'error', ...common(deps, key), code: 'card_only' },
|
|
7158
|
+
text: `${t.memberCard} is the booking card's own. ${t.signIn} signs a member in from the chat.`,
|
|
7159
|
+
isError: true,
|
|
7160
|
+
};
|
|
7161
|
+
const signedIn = deps.members.read(key);
|
|
7162
|
+
if (signedIn)
|
|
7163
|
+
return memberAnswer(deps, key, signedIn, fromCard ? '' : 'Already signed in');
|
|
7164
|
+
if (fromCard && args.action !== 'sign_in') {
|
|
7087
7165
|
return {
|
|
7088
7166
|
structured: {
|
|
7089
|
-
view: 'member
|
|
7167
|
+
view: 'no-member',
|
|
7090
7168
|
...common(deps, key),
|
|
7091
|
-
|
|
7092
|
-
tooMany: true,
|
|
7169
|
+
...(deps.memberLogin?.waiting(key) ? { waiting: true } : {}),
|
|
7093
7170
|
},
|
|
7094
|
-
text:
|
|
7171
|
+
text: `Nobody is signed in as a ${deps.vocab.brand} member in this conversation.`,
|
|
7095
7172
|
};
|
|
7096
7173
|
}
|
|
7097
|
-
const
|
|
7098
|
-
if (!
|
|
7099
|
-
deps.misses?.miss(tries);
|
|
7100
|
-
if (!member) {
|
|
7101
|
-
// ⚠️ NO TIER FOR A NUMBER THAT DID NOT MATCH. Being generous here - "close
|
|
7102
|
-
// enough, have the discount" - is inventing a membership, and the guest
|
|
7103
|
-
// would find out at the desk.
|
|
7174
|
+
const login = deps.memberLogin;
|
|
7175
|
+
if (!login)
|
|
7104
7176
|
return {
|
|
7105
|
-
structured: { view: '
|
|
7106
|
-
text: `
|
|
7177
|
+
structured: { view: 'error', ...common(deps, key), code: 'no_sign_in_page' },
|
|
7178
|
+
text: `Signing in as a ${deps.vocab.brand} member is not open here.`,
|
|
7179
|
+
isError: true,
|
|
7107
7180
|
};
|
|
7108
|
-
}
|
|
7109
|
-
|
|
7181
|
+
const made = login.start(key, { noCards: ctx.noCards, lang: args.lang, ip: ctx.ip });
|
|
7182
|
+
if (!made.ok)
|
|
7183
|
+
return {
|
|
7184
|
+
structured: { view: 'error', ...common(deps, key), code: made.code },
|
|
7185
|
+
text: made.code === 'too_many_links'
|
|
7186
|
+
? `Too many sign-in links were asked for in this conversation just now. Wait a few minutes, then ask again. ${MEMBER_NEVER_IN_CHAT}`
|
|
7187
|
+
: 'Too many sign-ins are being started just now. Try again in a minute.',
|
|
7188
|
+
isError: true,
|
|
7189
|
+
};
|
|
7190
|
+
const said = { expiresAt: made.expiresAt, expiresInSeconds: made.expiresInSeconds };
|
|
7191
|
+
if (ctx.noCards)
|
|
7192
|
+
return {
|
|
7193
|
+
structured: {
|
|
7194
|
+
view: 'member-sign-in',
|
|
7195
|
+
...common(deps, key),
|
|
7196
|
+
signedIn: false,
|
|
7197
|
+
url: made.url,
|
|
7198
|
+
shortCode: made.short,
|
|
7199
|
+
...said,
|
|
7200
|
+
},
|
|
7201
|
+
text: `A one-time ${deps.vocab.brand} member sign-in page is ready: ${made.url} - the guest opens it in their browser and types their membership number and the surname on it there. ${MEMBER_NEVER_IN_CHAT} The page shows the code ${made.short}: tell the guest it must match the code you give them here, and to close the page if it does not. The link works once, for ${made.minutes} minutes. When they say they are done, call ${t.signIn} again: it then answers with their tier and member rate.`,
|
|
7202
|
+
};
|
|
7203
|
+
// ⚠️ THE LINK AND THE CODE ARE THE CARD'S (as the account's, Nexva's review
|
|
7204
|
+
// P1-1): where a card is drawn the model is never handed the short code, so
|
|
7205
|
+
// text a page slipped into the chat cannot show a matching one.
|
|
7110
7206
|
return {
|
|
7111
|
-
structured: {
|
|
7112
|
-
|
|
7113
|
-
|
|
7114
|
-
|
|
7207
|
+
structured: { view: 'member-sign-in', ...common(deps, key), signedIn: false, ...said },
|
|
7208
|
+
text: `A one-time ${deps.vocab.brand} member sign-in page is ready for the guest, opened from the card: they type their membership number and surname on that page. ${MEMBER_NEVER_IN_CHAT} The card shows a code the page shows too, and shows them signed in when they are done; the link works once, for ${made.minutes} minutes.`,
|
|
7209
|
+
meta: {
|
|
7210
|
+
[MEMBER_SIGN_IN_META_KEY]: {
|
|
7211
|
+
url: made.url,
|
|
7212
|
+
shortCode: made.short,
|
|
7213
|
+
expiresAt: made.expiresAt,
|
|
7214
|
+
},
|
|
7115
7215
|
},
|
|
7116
|
-
text: [
|
|
7117
|
-
`Signed in as ${member.name}${member.names?.zh ? ` (${member.names.zh})` : ''} · ${member.programme} ${member.tier.name} · ${member.points.toLocaleString('en-GB')} points.`,
|
|
7118
|
-
`Rates now show the ${member.tier.name} member rate, ${Math.round(member.tier.discount * 100)}% off the public one.`,
|
|
7119
|
-
...((deps.backend.capabilities ?? FULL_CAPABILITIES).coupons
|
|
7120
|
-
? memberSaid(member, deps.vocab.brand)
|
|
7121
|
-
: [POINTS_DO_NOT_PAY]),
|
|
7122
|
-
member.preferences?.length
|
|
7123
|
-
? `Saved preferences on this membership: ${member.preferences.map((k) => REQUEST_WORDS[k]).join(', ')}. They are ticked on the booking form; mention them, and ask before booking with them.`
|
|
7124
|
-
: '',
|
|
7125
|
-
]
|
|
7126
|
-
.filter(Boolean)
|
|
7127
|
-
.join('\n'),
|
|
7128
7216
|
};
|
|
7129
7217
|
}
|
|
7130
7218
|
if (name === t.signOut) {
|
|
7131
7219
|
const was = deps.members.read(key);
|
|
7132
7220
|
deps.members.clear(key);
|
|
7221
|
+
deps.memberLogin?.cancel(key);
|
|
7133
7222
|
return {
|
|
7134
7223
|
structured: { view: 'signed-out', ...common(deps, key) },
|
|
7135
7224
|
text: was
|