@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 +23 -0
- package/app/app.config.ts +4 -1
- package/app/components/Connect/Layout/Index.vue +1 -1
- package/app/components/Connect/Modal/Error.vue +76 -0
- package/app/components/Connect/Transition/Fade.vue +31 -0
- package/app/composables/useConnectModal.ts +4 -2
- package/app/types/dates.ts +17 -0
- package/app/utils/getErrorMessage.ts +42 -0
- package/app/utils/getErrorStatus.ts +37 -0
- package/i18n/locales/en-CA.ts +2 -0
- package/modules/base-assets/runtime/assets/connect-base-tw.css +1 -0
- package/package.json +1 -1
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: '
|
|
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-
|
|
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
|
+
}
|
package/i18n/locales/en-CA.ts
CHANGED
|
@@ -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',
|