@sbc-connect/nuxt-base 0.2.0 → 0.4.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/CHANGELOG.md CHANGED
@@ -1,5 +1,28 @@
1
1
  # @sbc-connect/nuxt-base
2
2
 
3
+ ## 0.4.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#106](https://github.com/bcgov/connect-nuxt/pull/106) [`f5d34df`](https://github.com/bcgov/connect-nuxt/commit/f5d34df0bbb2b37cbeda8a9dd39c6bce098c564f) Thanks [@deetz99](https://github.com/deetz99)! - <br>
8
+ - ConnectModalError component
9
+ - getErrorStatus util
10
+ - getErrorMessage util
11
+
12
+ ## 0.3.0
13
+
14
+ ### Minor Changes
15
+
16
+ - [#102](https://github.com/bcgov/connect-nuxt/pull/102) [`d714785`](https://github.com/bcgov/connect-nuxt/commit/d71478573f1fe11be34d5d588d68fb75eb5fd159) Thanks [@deetz99](https://github.com/deetz99)! - - ConnectTransitionFade component
17
+ - configure UContainer app config with default spacing and padding
18
+ - configure UAlert subtle/warning app config with correct bg styling
19
+
20
+ ### Patch Changes
21
+
22
+ - [#104](https://github.com/bcgov/connect-nuxt/pull/104) [`5f1b383`](https://github.com/bcgov/connect-nuxt/commit/5f1b3839ed993654a7f20fe161047ba38ab86274) Thanks [@deetz99](https://github.com/deetz99)! - - change overflow-hidden to overflow-clip in main layout component, allowing position sticky to work correctly
23
+ - cancel and close i18n translations
24
+ - Type aliases for Dates
25
+
3
26
  ## 0.2.0
4
27
 
5
28
  ### Minor Changes
package/app/app.config.ts CHANGED
@@ -19,7 +19,7 @@ export default defineAppConfig({
19
19
  color: 'warning',
20
20
  variant: 'subtle',
21
21
  class: {
22
- root: 'text-highlighted bg-yellow-400/25 ring-2 ring-yellow-400',
22
+ root: 'py-4 px-6 relative bg-white text-highlighted ring-2 ring-brand-inverted before:content-[""] before:absolute before:inset-0 before:bg-brand-inverted/10 before:z-10',
23
23
  icon: 'text-warning',
24
24
  title: 'font-bold text-nuetral-highlighted',
25
25
  description: 'text-nuetral-highlighted'
@@ -166,6 +166,9 @@ export default defineAppConfig({
166
166
  }
167
167
  ]
168
168
  },
169
+ container: {
170
+ base: 'px-0 sm:px-0 lg:px-0 py-6 space-y-6 sm:py-10 sm:space-y-10'
171
+ },
169
172
  dropdownMenu: {
170
173
  slots: {
171
174
  content: 'min-w-32 max-h-[75dvh] shadow-lg rounded-sm',
@@ -12,7 +12,7 @@
12
12
  <slot name="bottomHeaders" />
13
13
  <main class="app-body px-2 sm:px-4">
14
14
  <slot name="body">
15
- <div class="app-inner-container grow flex flex-col overflow-hidden">
15
+ <div class="app-inner-container grow flex flex-col overflow-clip">
16
16
  <slot />
17
17
  </div>
18
18
  </slot>
@@ -0,0 +1,76 @@
1
+ <script setup lang="ts">
2
+ import type { VNode } from 'vue'
3
+ // https://vue-i18n.intlify.dev/api/composition.html#te-key-locale
4
+ const { t, te } = useI18n()
5
+
6
+ interface ErrorModalProps {
7
+ error: unknown
8
+ i18nPrefix: string
9
+ buttons?: ConnectModalButton[]
10
+ contactInfo?: VNode
11
+ preferErrorMessage?: boolean
12
+ }
13
+
14
+ const {
15
+ error,
16
+ i18nPrefix,
17
+ buttons = [
18
+ { label: useNuxtApp().$i18n.t('label.close'), shouldClose: true }
19
+ ],
20
+ preferErrorMessage = false
21
+ } = defineProps<ErrorModalProps>()
22
+ defineEmits<{ close: [] }>()
23
+
24
+ const status = getErrorStatus(error)
25
+ const message = getErrorMessage(error)
26
+
27
+ const titleKey = `${i18nPrefix}.${status}.title`
28
+ const descKey = `${i18nPrefix}.${status}.description`
29
+
30
+ const title = te(titleKey) ? t(titleKey) : t(`${i18nPrefix}.undefined.title`)
31
+ const i18nDescription = te(descKey) ? t(descKey) : t(`${i18nPrefix}.undefined.description`)
32
+
33
+ const description = (preferErrorMessage && message)
34
+ ? message
35
+ : i18nDescription
36
+ </script>
37
+
38
+ <template>
39
+ <UModal
40
+ id="connect-error-modal"
41
+ overlay
42
+ :title
43
+ :description
44
+ :dismissible="false"
45
+ >
46
+ <template #content>
47
+ <div class="p-10 flex flex-col gap-6">
48
+ <h2 class="text-2xl font-semibold text-neutral-highlighted">
49
+ {{ title }}
50
+ </h2>
51
+ <p>{{ description }}</p>
52
+ <component :is="contactInfo" v-if="contactInfo" />
53
+ <div class="flex flex-wrap items-center justify-center gap-4">
54
+ <template
55
+ v-for="button in buttons"
56
+ :key="button.label"
57
+ >
58
+ <UButton
59
+ v-if="button.shouldClose"
60
+ v-bind="button"
61
+ size="xl"
62
+ class="w-full justify-center sm:w-min sm:justify-normal"
63
+ @click="$emit('close')"
64
+ />
65
+ <UButton
66
+ v-else
67
+ v-bind="button"
68
+ size="xl"
69
+ class="w-full justify-center sm:w-min sm:justify-normal"
70
+ />
71
+ </template>
72
+ </div>
73
+ </div>
74
+ </template>
75
+ </UModal>
76
+ </template>
@@ -0,0 +1,31 @@
1
+ <template>
2
+ <Transition name="fade">
3
+ <slot />
4
+ </Transition>
5
+ </template>
6
+
7
+ <style>
8
+ .fade-enter-active {
9
+ transition: opacity 0.3s ease-in-out;
10
+ }
11
+ .fade-leave-active {
12
+ transition: opacity 0s ease-in-out;
13
+ }
14
+ .fade-enter-from,
15
+ .fade-leave-to {
16
+ opacity: 0;
17
+ }
18
+
19
+ @media (prefers-reduced-motion) {
20
+ .fade-enter-active {
21
+ transition: opacity 0s ease-in-out;
22
+ }
23
+ .fade-leave-active {
24
+ transition: opacity 0s ease-in-out;
25
+ }
26
+ .fade-enter-from,
27
+ .fade-leave-to {
28
+ opacity: 0;
29
+ }
30
+ }
31
+ </style>
@@ -1,11 +1,13 @@
1
- import { ConnectModal } from '#components'
1
+ import { ConnectModal, ConnectModalError } from '#components'
2
2
 
3
3
  export const useConnectModal = () => {
4
4
  const overlay = useOverlay()
5
5
 
6
6
  const baseModal = overlay.create(ConnectModal)
7
+ const errorModal = overlay.create(ConnectModalError)
7
8
 
8
9
  return {
9
- baseModal
10
+ baseModal,
11
+ errorModal
10
12
  }
11
13
  }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * ISO date string (YYYY-MM-DD), assumed to be in Pacific timezone.
3
+ * @example "2021-12-31"
4
+ */
5
+ export type IsoDatePacific = string
6
+
7
+ /**
8
+ * API date-time string (YYYY-MM-DDTHH:MM:SS[.MMMMMM]+00.00), assumed to be in UTC.
9
+ * @example "2022-05-09T16:26:14.839032+00:00"
10
+ */
11
+ export type ApiDateTimeUtc = string
12
+
13
+ /**
14
+ * Formatted date-time string with explicit GMT timezone (ie, UTC).
15
+ * @example "Tue, 19 Apr 2022 16:40:18 GMT"
16
+ */
17
+ export type FormattedDateTimeGmt = string
@@ -0,0 +1,42 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+
3
+ /**
4
+ * Safely extracts an error message from an unknown error object.
5
+ *
6
+ * The search order (Highest Priority to Lowest) is:
7
+ * 1. Current object's 'message' (must be a non-empty string).
8
+ * 2. Current object's 'detail' (must be a non-empty string).
9
+ * 3. Recursively searches the 'error.data.rootCause' object.
10
+ * 4. Recursively searches the 'error.data' object.
11
+ *
12
+ * This function will return the first valid, non-empty string message found.
13
+ *
14
+ * @param error - The error object (unknown).
15
+ * @returns The error message if found, else 'undefined'.
16
+ */
17
+ export function getErrorMessage(error: unknown): string | undefined {
18
+ if (error && typeof error === 'object') {
19
+ const errorObject = error as Record<string, any>
20
+
21
+ const message = errorObject.message
22
+ if (message && typeof message === 'string' && message.length > 0) {
23
+ return message
24
+ }
25
+ const detail = errorObject.detail
26
+ if (detail && typeof detail === 'string' && detail.length > 0) {
27
+ return detail
28
+ }
29
+
30
+ const rootCauseMessage = getErrorMessage(errorObject?.data?.rootCause)
31
+ if (rootCauseMessage) {
32
+ return rootCauseMessage
33
+ }
34
+
35
+ const dataMessage = getErrorMessage(errorObject?.data)
36
+ if (dataMessage) {
37
+ return dataMessage
38
+ }
39
+ }
40
+
41
+ return undefined
42
+ }
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Safely extracts the HTTP status code from a thrown error.
3
+ *
4
+ * This provides a reliable way to get a status code from an `unknown` error type.
5
+ *
6
+ * @param error - The error object to inspect. It can be of any type.
7
+ * @returns The numeric HTTP status code if found, otherwise `undefined`.
8
+ *
9
+ * @example
10
+ * getErrorStatus({ response: { status: 404 } }); // returns 404
11
+ *
12
+ * @example
13
+ * getErrorStatus({ statusCode: 403 }); // returns 403
14
+ *
15
+ * @example
16
+ * getErrorStatus(new Error("Network failed")); // returns undefined
17
+ */
18
+ export function getErrorStatus(error: unknown): number | undefined {
19
+ // check if error is valid object
20
+ if (
21
+ error
22
+ && typeof error === 'object'
23
+ && ('statusCode' in error || 'response' in error)
24
+ ) {
25
+ // handle case where error.response.status
26
+ if ('response' in error && error.response && typeof error.response === 'object' && 'status' in error.response) {
27
+ return error.response.status as number
28
+ }
29
+ // handle case where no error.response.status, fallback to error.statusCode
30
+ if ('statusCode' in error) {
31
+ return error.statusCode as number
32
+ }
33
+ }
34
+
35
+ // return undefined if no status or statusCode
36
+ return undefined
37
+ }
@@ -21,6 +21,8 @@ export default {
21
21
  appVersion: 'App Version',
22
22
  bcApp: 'A BC Online Application',
23
23
  bcGovLogo: 'Government of British Columbia Logo',
24
+ cancel: 'Cancel',
25
+ close: 'Close',
24
26
  contactInformation: 'Contact Information',
25
27
  copyright: 'Copyright',
26
28
  disclaimer: 'Disclaimer',
@@ -38,6 +38,7 @@
38
38
  --ui-shade-highlighted: var(--color-blue-50);
39
39
  --ui-shade-warning: #FAE9E9;
40
40
  --ui-radius: 0.1667rem;
41
+ --ui-container: var(--container-bcGovLg);
41
42
  }
42
43
 
43
44
  /* FUTURE: add dark themed colours (currently set to be the same) */
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@sbc-connect/nuxt-base",
3
3
  "type": "module",
4
- "version": "0.2.0",
4
+ "version": "0.4.0",
5
5
  "repository": "github:bcgov/connect-nuxt",
6
6
  "license": "BSD-3-Clause",
7
7
  "main": "./nuxt.config.ts",