@nuxt-customer-portal/planning 0.7.0 → 0.8.1

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/CHANGELOG.md CHANGED
@@ -1,5 +1,26 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.8.1
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies
8
+ - @nuxt-customer-portal/products@0.8.1
9
+ - @nuxt-customer-portal/core@0.8.1
10
+ - @nuxt-customer-portal/ui@0.8.1
11
+
12
+ ## 0.8.0
13
+
14
+ ### Patch Changes
15
+
16
+ - e558791: Support store-wide online and deferred invoice payment choices with immediate purchase fulfillment, linked unsent invoice drafts, inclusive tax review, and manual payment progress. Add unlisted products with server-generated unguessable slugs, excluded from catalog discovery while remaining available through individual product pages.
17
+ - Updated dependencies [175854d]
18
+ - Updated dependencies [e558791]
19
+ - Updated dependencies [28bb7af]
20
+ - @nuxt-customer-portal/core@0.8.0
21
+ - @nuxt-customer-portal/ui@0.8.0
22
+ - @nuxt-customer-portal/products@0.8.0
23
+
3
24
  ## 0.7.0
4
25
 
5
26
  ### Patch Changes
@@ -3,6 +3,8 @@ import type { HoldResult } from '../../composables/usePlanning'
3
3
  import { resolveCheckoutQuery, resolveCheckoutReturnUrl } from '@nuxt-customer-portal/products/shared/checkout-query'
4
4
  import { formatMoney } from '@nuxt-customer-portal/products/shared/money'
5
5
 
6
+ useHead({ meta: [{ name: 'robots', content: 'noindex, nofollow' }] })
7
+
6
8
  definePageMeta({ path: '/store/:slug/book', public: true, layout: 'store', key: (route) => `booking:${route.path}` })
7
9
  const route = useRoute(),
8
10
  { t, locale, setLocale } = useI18n(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nuxt-customer-portal/planning",
3
- "version": "0.7.0",
3
+ "version": "0.8.1",
4
4
  "license": "MIT",
5
5
  "homepage": "https://nuxt-customer-portal.com",
6
6
  "repository": {
@@ -40,9 +40,9 @@
40
40
  "drizzle-orm": "^0.45.2",
41
41
  "pg": "^8.22.0",
42
42
  "zod": "^4.4.3",
43
- "@nuxt-customer-portal/core": "^0.7.0",
44
- "@nuxt-customer-portal/ui": "^0.7.0",
45
- "@nuxt-customer-portal/products": "^0.7.0"
43
+ "@nuxt-customer-portal/core": "^0.8.1",
44
+ "@nuxt-customer-portal/products": "^0.8.1",
45
+ "@nuxt-customer-portal/ui": "^0.8.1"
46
46
  },
47
47
  "peerDependencies": {
48
48
  "nuxt": "^4.5.1",
@@ -1,6 +1,6 @@
1
1
  export default {
2
2
  id: 'planning',
3
- version: '0.7.0',
3
+ version: '0.8.1',
4
4
  source: '@nuxt-customer-portal/planning',
5
5
  dependsOn: ['core', 'ui', 'products'],
6
6
  schema: './server/db/schema/planning.ts',
@@ -5,7 +5,7 @@ import { rows } from '@nuxt-customer-portal/products/server/utils/database'
5
5
  export default defineEventHandler(async (event) => {
6
6
  const scope = await appointmentScope(event)
7
7
  return rows(
8
- `SELECT slug,COALESCE(NULLIF(data->'content'->'en'->>'title',''),data->'content'->'nl'->>'title') AS title FROM products.product WHERE store_id=$1 AND data->>'status'='published' AND data->>'type'='service' AND data->'planning'->>'enabled'='true' ORDER BY title,id`,
8
+ `SELECT slug,COALESCE(NULLIF(data->'content'->'en'->>'title',''),data->'content'->'nl'->>'title') AS title FROM products.product WHERE store_id=$1 AND data->>'status'='published' AND COALESCE(data->>'visibility','listed')='listed' AND data->>'type'='service' AND data->'planning'->>'enabled'='true' ORDER BY title,id`,
9
9
  [scope.storeId]
10
10
  )
11
11
  })
@@ -485,8 +485,8 @@ export async function prepareOrder(order: Order) {
485
485
  let failure: string | undefined
486
486
  if (
487
487
  hold.status !== 'reserved' ||
488
- !order.snapshot.paymentCompletedAt ||
489
- Date.parse(order.snapshot.paymentCompletedAt) > hold.expires_at.getTime()
488
+ !(order.snapshot.acceptedAt || order.snapshot.paymentCompletedAt) ||
489
+ Date.parse((order.snapshot.acceptedAt || order.snapshot.paymentCompletedAt)!) > hold.expires_at.getTime()
490
490
  ) {
491
491
  failure = 'Reservation expired before payment completed'
492
492
  } else {
@@ -544,8 +544,8 @@ export async function confirm(tx: PoolClient, order: Order) {
544
544
  if (
545
545
  order.snapshot.planningFailure ||
546
546
  hold.status !== 'reserved' ||
547
- !order.snapshot.paymentCompletedAt ||
548
- Date.parse(order.snapshot.paymentCompletedAt) > hold.expires_at.getTime()
547
+ !(order.snapshot.acceptedAt || order.snapshot.paymentCompletedAt) ||
548
+ Date.parse((order.snapshot.acceptedAt || order.snapshot.paymentCompletedAt)!) > hold.expires_at.getTime()
549
549
  ) {
550
550
  await tx.query(
551
551
  "UPDATE products.orders SET snapshot=jsonb_set(snapshot,'{planningFailure}',to_jsonb($2::text)) WHERE id=$1",
@@ -1,3 +1,5 @@
1
+ import { orderDownloadToken, purchaseDownloads } from '@nuxt-customer-portal/products/server/utils/downloads'
2
+ import { downloadEmailMarkdown } from '@nuxt-customer-portal/products/shared/downloads'
1
3
  import { randomUUID } from 'node:crypto'
2
4
  import { pool } from '@nuxt-customer-portal/core/server/utils/db'
3
5
  import { sendPortalEmail } from '@nuxt-customer-portal/core/server/utils/portal-email'
@@ -181,7 +183,19 @@ async function appointmentEffects(
181
183
  timezone: a.snapshot.customerTimezone,
182
184
  meetingUrl: a.meeting_url || appointmentUrl,
183
185
  meetingDetails,
184
- instructions: order.lines[0]!.snapshot.product.nextSteps[locale] || '',
186
+ instructions: [
187
+ order.lines[0]!.snapshot.product.nextSteps[locale] || '',
188
+ order.snapshot.paymentMethod === 'invoice' && a.status !== 'cancelled'
189
+ ? locale === 'nl'
190
+ ? 'Je ontvangt de factuur later apart.'
191
+ : 'Your invoice will follow separately.'
192
+ : '',
193
+ a.status === 'cancelled'
194
+ ? ''
195
+ : downloadEmailMarkdown(await purchaseDownloads(order, await orderDownloadToken(order.id)), locale)
196
+ ]
197
+ .filter(Boolean)
198
+ .join('\n\n'),
185
199
  url: appointmentUrl
186
200
  }
187
201
  const invitation = calendarInvitation({
@@ -584,7 +598,7 @@ export async function expireReservations() {
584
598
  await reconcileCheckout(order.checkout_id)
585
599
  }
586
600
  const current = await getOrder(hold.order_id)
587
- if (current?.status === 'paid') {
601
+ if (current && ['paid', 'accepted'].includes(current.status)) {
588
602
  await processOrder(current.id)
589
603
  continue
590
604
  }
@@ -383,7 +383,7 @@ export async function abandonChange(event: H3Event, id: string) {
383
383
  [hold.order_id],
384
384
  tx
385
385
  )
386
- if (order?.status === 'paid') {
386
+ if (order && ['paid', 'accepted'].includes(order.status)) {
387
387
  return
388
388
  }
389
389
  await tx.query("UPDATE products.orders SET status='expired' WHERE id=$1 AND status='pending'", [hold.order_id])