Skip to content

Commit

Permalink
feat: add in snack bar exceptions
Browse files Browse the repository at this point in the history
feat: snackbar store
feat: raise snacks globally
  • Loading branch information
JTraill committed Jan 13, 2025
1 parent ef4e60e commit ebd6a2e
Show file tree
Hide file tree
Showing 6 changed files with 118 additions and 0 deletions.
23 changes: 23 additions & 0 deletions web/src/components/shared/Snackbar.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<template>
<v-snackbar
v-model="snackbarStore.isVisible"
:timeout="-1"
:color="snackbarStore.color"
location="bottom right"
>
<h3>{{ snackbarStore.title }}</h3>
<span>{{ snackbarStore.message }}</span>
<template v-slot:actions>
<v-icon class="mx-2" :icon="mdiCloseCircle" @click="close" />
</template>
</v-snackbar>
</template>

<script setup>
import { useSnackbarStore } from '@/stores/SnackBarStore';
import { mdiCloseCircle } from '@mdi/js';
const snackbarStore = useSnackbarStore();
const close = () => {
snackbarStore.isVisible = false;
};
</script>
10 changes: 10 additions & 0 deletions web/src/services/HttpService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import axios, {
InternalAxiosRequestConfig,
} from 'axios';
import redirectHandlerService from './RedirectHandlerService';
import { useSnackbarStore } from '@/stores/SnackBarStore';

export interface IHttpService {
get<T>(resource: string, queryParams?: Record<string, any>): Promise<T>;
Expand All @@ -17,6 +18,7 @@ export interface IHttpService {

export class HttpService implements IHttpService {
readonly client: AxiosInstance;
snackBarStore = useSnackbarStore();

constructor(baseURL: string) {
this.client = axios.create({
Expand Down Expand Up @@ -47,8 +49,16 @@ export class HttpService implements IHttpService {
private handleAuthError(error: any) {
console.error(error);
console.log('User unauthenticated.');
// todo: check for a 403 and handle it
if (error.response && error.response.status === 401) {
redirectHandlerService.handleUnauthorized(window.location.href);
} else {
// The user should be notified about unhandled server exceptions.
this.snackBarStore.showSnackbar(
'Something went wrong, please contact your Administrator.',
'#b84157',
'Error'
);
}
return Promise.reject(new Error(error));
}
Expand Down
18 changes: 18 additions & 0 deletions web/src/stores/SnackbarStore.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { defineStore } from 'pinia';
import { ref } from 'vue';

export const useSnackbarStore = defineStore('snackbar', () => {
const isVisible = ref(false);
const message = ref('');
const color = ref('success');
const title = ref('');

const showSnackbar = (msg = '', col = 'success', ti = '') => {
message.value = msg;
color.value = col;
title.value = ti;
isVisible.value = true;
};

return { isVisible, message, color, showSnackbar, title };
});
1 change: 1 addition & 0 deletions web/src/stores/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@ export { useCommonStore } from './CommonStore';
export { useCourtFileSearchStore } from './CourtFileSearchStore';
export { useCourtListStore } from './CourtListStore';
export { useCriminalFileStore } from './CriminalFileStore';
export { useSnackbarStore } from './SnackBarStore';
31 changes: 31 additions & 0 deletions web/tests/components/shared/Snackbar.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { mount } from '@vue/test-utils';
import { useSnackbarStore } from '@/stores/SnackBarStore';
import { describe, it, expect, beforeEach } from 'vitest';
import Snackbar from '@/components/shared/Snackbar.vue';
import { setActivePinia, createPinia } from 'pinia'

describe('Snackbar.vue', () => {
let store: ReturnType<typeof useSnackbarStore>;

beforeEach(() => {
setActivePinia(createPinia());
store = useSnackbarStore();
});

it('renders snackbar with correct props', () => {
store.showSnackbar('Test message', 'error', 'Test title');
const wrapper = mount(Snackbar);

expect(wrapper.find('h3').text()).toBe('Test title');
expect(wrapper.text()).toContain('Test message');
expect(store.isVisible).toBe(true);
});

it('closes snackbar when close button is clicked', async () => {
const wrapper = mount(Snackbar);

await wrapper.find('v-snackbar__actions v-icon').trigger('click');

expect(store.isVisible).toBe(false);
});
});
35 changes: 35 additions & 0 deletions web/tests/stores/SnackbarStore.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { setActivePinia, createPinia } from 'pinia'
import { useSnackbarStore } from '@/stores/SnackBarStore';
import { beforeEach, describe, expect, it } from 'vitest';

describe('SnackBarStore', () => {
let store: ReturnType<typeof useSnackbarStore>;

beforeEach(() => {
setActivePinia(createPinia())
store = useSnackbarStore();
});

it('initializes with default values', () => {
expect(store.isVisible).toBe(false);
expect(store.message).toBe('');
expect(store.color).toBe('success');
expect(store.title).toBe('');
});

it('shows snackbar with given message, color, and title', () => {
store.showSnackbar('Test message', 'error', 'Test title');
expect(store.isVisible).toBe(true);
expect(store.message).toBe('Test message');
expect(store.color).toBe('error');
expect(store.title).toBe('Test title');
});

it('shows snackbar with default values when no arguments are passed', () => {
store.showSnackbar();
expect(store.isVisible).toBe(true);
expect(store.message).toBe('');
expect(store.color).toBe('success');
expect(store.title).toBe('');
});
});

0 comments on commit ebd6a2e

Please sign in to comment.