diff --git a/.env.local.sample b/.env.local.sample
index f041a760..e991c4e5 100644
--- a/.env.local.sample
+++ b/.env.local.sample
@@ -1,2 +1 @@
-VITE_GPT_URL=""
-VITE_GPT_AUTH=""
\ No newline at end of file
+VITE_FIREBASE_CONFIG=""
\ No newline at end of file
diff --git a/__tests__/setup.js b/__tests__/setup.js
index f96675f3..addfaa66 100644
--- a/__tests__/setup.js
+++ b/__tests__/setup.js
@@ -1,6 +1,20 @@
import { config } from '@vue/test-utils';
import i18n from '@/utils/plugins/i18n.js';
import UnnnicSystemPlugin from '@/utils/plugins/UnnnicSystem.js';
+import { vi } from 'vitest';
+
+vi.mock('firebase/app', () => ({
+ initializeApp: vi.fn(() => ({
+ name: 'mockApp',
+ })),
+}));
+
+vi.mock('firebase/firestore', () => ({
+ getFirestore: vi.fn(() => ({
+ collection: vi.fn(),
+ doc: vi.fn(),
+ })),
+}));
config.global.plugins = [i18n, UnnnicSystemPlugin];
config.global.mocks = {
diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh
index 79755b5c..f288a33f 100755
--- a/docker-entrypoint.sh
+++ b/docker-entrypoint.sh
@@ -1,10 +1,13 @@
#!/bin/sh
+
+ESCAPED_FIREBASE_CONFIG=$(echo "${VITE_FIREBASE_CONFIG}" | sed 's/\//\\\//g')
+
export JSON_STRING='window.configs = { \
- "VITE_GPT_URL":"'${VITE_GPT_URL}'", \
- "VITE_GPT_AUTH":"'${VITE_GPT_AUTH}'", \
- "VITE_INSIGHTS_API_URL":"'${VITE_INSIGHTS_API_URL}'", \
- "VITE_HOTJAR_ID":"'${VITE_HOTJAR_ID}'", \
+ "VITE_INSIGHTS_API_URL": "'${VITE_INSIGHTS_API_URL}'", \
+ "VITE_FIREBASE_CONFIG": '${ESCAPED_FIREBASE_CONFIG}', \
+ "VITE_HOTJAR_ID": "'${VITE_HOTJAR_ID}'" \
}'
+
sed "s|//CONFIGURATIONS_PLACEHOLDER|${JSON_STRING}|" /usr/share/nginx/html/insights/index.html.tmpl > /tmp/index.html
exec "$@"
\ No newline at end of file
diff --git a/docker/Dockerfile b/docker/Dockerfile
index dd05e640..e8567e9c 100644
--- a/docker/Dockerfile
+++ b/docker/Dockerfile
@@ -7,12 +7,9 @@ FROM node:${NODE_VERSION}-${BASE_VERSION} as builder
WORKDIR /app
-ARG VITE_GPT_URL
-ARG VITE_GPT_AUTH
-ARG VITE_HOTJAR_ID
+ARG VITE_FIREBASE_CONFIG
-ENV VITE_GPT_URL $VITE_GPT_URL
-ENV VITE_GPT_AUTH $VITE_GPT_AUTH
+ENV VITE_FIREBASE_CONFIG $VITE_FIREBASE_CONFIG
ENV VITE_HOTJAR_ID $VITE_HOTJAR_ID
RUN apk --no-cache add git
diff --git a/package.json b/package.json
index 8c78da3c..a02ac71b 100644
--- a/package.json
+++ b/package.json
@@ -20,7 +20,11 @@
"axios": "^1.7.2",
"chart.js": "^4.4.2",
"chartjs-plugin-datalabels": "^2.2.0",
+ "dompurify": "^3.1.7",
"emoji-mart": "^5.6.0",
+ "firebase": "^10.13.1",
+ "marked": "^14.1.2",
+ "mitt": "^3.0.1",
"moment": "^2.30.1",
"vue": "^3.3.11",
"vue-i18n": "9",
@@ -56,4 +60,4 @@
"resolutions": {
"strip-ansi": "6.0.1"
}
-}
\ No newline at end of file
+}
diff --git a/src/App.vue b/src/App.vue
index b5e8cd41..da8f1481 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -104,6 +104,7 @@ export default {
setProject: 'config/setProject',
checkEnableCreateCustomDashboards:
'config/checkEnableCreateCustomDashboards',
+ setEmail: 'user/setEmail',
}),
...mapMutations({
@@ -141,6 +142,10 @@ export default {
const sessionUserEmail = parseJwt(newToken)?.email || null;
+ if (sessionUserEmail) {
+ this.setEmail(sessionUserEmail);
+ }
+
initHotjar(sessionUserEmail);
await this.checkEnableCreateCustomDashboards();
diff --git a/src/assets/images/shine.svg b/src/assets/images/shine.svg
new file mode 100644
index 00000000..cceb36de
--- /dev/null
+++ b/src/assets/images/shine.svg
@@ -0,0 +1,8 @@
+
diff --git a/src/components/Markdown.vue b/src/components/Markdown.vue
new file mode 100644
index 00000000..b5556753
--- /dev/null
+++ b/src/components/Markdown.vue
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
diff --git a/src/components/insights/Layout/Header.vue b/src/components/insights/Layout/Header.vue
index 850c7a04..b18826df 100644
--- a/src/components/insights/Layout/Header.vue
+++ b/src/components/insights/Layout/Header.vue
@@ -17,10 +17,11 @@
+
@@ -33,6 +34,7 @@ import HeaderSelectDashboard from './HeaderSelectDashboard/index.vue';
import HeaderTagLive from './HeaderTagLive.vue';
import InsightsLayoutHeaderFilters from './HeaderFilters/index.vue';
import HeaderDashboardSettings from './HeaderDashboardSettings.vue';
+import HeaderGenerateInsightButton from './HeaderGenerateInsights/HeaderGenerateInsightButton.vue';
import moment from 'moment';
@@ -43,8 +45,8 @@ export default {
HeaderSelectDashboard,
HeaderTagLive,
InsightsLayoutHeaderFilters,
-
HeaderDashboardSettings,
+ HeaderGenerateInsightButton,
},
computed: {
...mapState({
diff --git a/src/components/insights/Layout/HeaderGenerateInsights/HeaderGenerateInsightButton.vue b/src/components/insights/Layout/HeaderGenerateInsights/HeaderGenerateInsightButton.vue
new file mode 100644
index 00000000..ce4c0200
--- /dev/null
+++ b/src/components/insights/Layout/HeaderGenerateInsights/HeaderGenerateInsightButton.vue
@@ -0,0 +1,78 @@
+
+
+
+
+
+
+
diff --git a/src/components/insights/Layout/HeaderGenerateInsights/HeaderGenerateInsightModal.vue b/src/components/insights/Layout/HeaderGenerateInsights/HeaderGenerateInsightModal.vue
new file mode 100644
index 00000000..1623793c
--- /dev/null
+++ b/src/components/insights/Layout/HeaderGenerateInsights/HeaderGenerateInsightModal.vue
@@ -0,0 +1,358 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/insights/Layout/HeaderGenerateInsights/HeaderGenerateInsightText.vue b/src/components/insights/Layout/HeaderGenerateInsights/HeaderGenerateInsightText.vue
new file mode 100644
index 00000000..10eaf0c4
--- /dev/null
+++ b/src/components/insights/Layout/HeaderGenerateInsights/HeaderGenerateInsightText.vue
@@ -0,0 +1,123 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/insights/Layout/HeaderGenerateInsights/InsightModalFooter.vue b/src/components/insights/Layout/HeaderGenerateInsights/InsightModalFooter.vue
new file mode 100644
index 00000000..82a39765
--- /dev/null
+++ b/src/components/insights/Layout/HeaderGenerateInsights/InsightModalFooter.vue
@@ -0,0 +1,308 @@
+
+
+
+
+
+
+
diff --git a/src/components/insights/Layout/HeaderGenerateInsights/__tests__/HeaderGenerateInsightButton.spec.js b/src/components/insights/Layout/HeaderGenerateInsights/__tests__/HeaderGenerateInsightButton.spec.js
new file mode 100644
index 00000000..372ba613
--- /dev/null
+++ b/src/components/insights/Layout/HeaderGenerateInsights/__tests__/HeaderGenerateInsightButton.spec.js
@@ -0,0 +1,89 @@
+import { beforeEach, describe, expect, it } from 'vitest';
+import { mount } from '@vue/test-utils';
+import HeaderGenerateInsightButton from '@/components/insights/Layout/HeaderGenerateInsights/HeaderGenerateInsightButton.vue';
+import { createStore } from 'vuex';
+
+const createWrapper = (props = {}, storeState = {}) => {
+ const store = createStore({
+ state: {
+ config: { token: 'default-token' },
+ widgets: { isLoadingCurrentDashboardWidgets: false },
+ ...storeState,
+ },
+ });
+
+ return mount(HeaderGenerateInsightButton, {
+ global: {
+ plugins: [store],
+ stubs: { HeaderGenerateInsightModal: true },
+ },
+ props,
+ });
+};
+
+describe('HeaderGenerateInsightButton', () => {
+ let wrapper;
+
+ beforeEach(() => {
+ wrapper = createWrapper();
+ });
+
+ it('should render the button with the correct text from i18n', () => {
+ expect(wrapper.text()).toContain(
+ wrapper.vm.$t('insights_header.generate_insight.title'),
+ );
+ });
+
+ it('should disable the button when isDisableBtn is true', async () => {
+ wrapper = createWrapper(
+ {},
+ { widgets: { isLoadingCurrentDashboardWidgets: true } },
+ );
+ const button = wrapper.find('button');
+ expect(button.element.disabled).toBe(true);
+ });
+
+ it('should enable the button when isDisableBtn is false', async () => {
+ const button = wrapper.find('button');
+ expect(button.element.disabled).toBe(false);
+ });
+
+ it('should open the modal when the button is clicked', async () => {
+ const button = wrapper.find('button');
+ await button.trigger('click');
+ expect(wrapper.vm.isGenerateInsightModalOpen).toBe(true);
+ expect(
+ wrapper.findComponent({ name: 'HeaderGenerateInsightModal' }).exists(),
+ ).toBe(true);
+ });
+
+ it('should close the modal when the "close" event is emitted', async () => {
+ wrapper.vm.isGenerateInsightModalOpen = true;
+ await wrapper.vm.$nextTick();
+
+ const modal = wrapper.findComponent({ name: 'HeaderGenerateInsightModal' });
+ await modal.vm.$emit('close');
+
+ expect(wrapper.vm.isGenerateInsightModalOpen).toBe(false);
+ });
+
+ it('should pass the correct prop "show" to the HeaderGenerateInsightModal', async () => {
+ wrapper.vm.isGenerateInsightModalOpen = true;
+ await wrapper.vm.$nextTick();
+
+ const modal = wrapper.findComponent({ name: 'HeaderGenerateInsightModal' });
+ expect(modal.props('show')).toBe(true);
+ });
+
+ it('should render the image inside the button', () => {
+ const img = wrapper.find('img');
+ expect(img.attributes('src')).toBe('/src/assets/images/shine.svg');
+ });
+
+ it('should compute the correct token from Vuex store', async () => {
+ expect(wrapper.vm.token).toBe('default-token');
+
+ wrapper = createWrapper({}, { config: { token: 'new-token' } });
+ expect(wrapper.vm.token).toBe('new-token');
+ });
+});
diff --git a/src/components/insights/Layout/HeaderGenerateInsights/__tests__/HeaderGenerateInsightModal.spec.js b/src/components/insights/Layout/HeaderGenerateInsights/__tests__/HeaderGenerateInsightModal.spec.js
new file mode 100644
index 00000000..35222b57
--- /dev/null
+++ b/src/components/insights/Layout/HeaderGenerateInsights/__tests__/HeaderGenerateInsightModal.spec.js
@@ -0,0 +1,185 @@
+import { mount } from '@vue/test-utils';
+import { describe, it, expect, vi, beforeEach } from 'vitest';
+import HeaderGenerateInsightModal from '@/components/insights/Layout/HeaderGenerateInsights/HeaderGenerateInsightModal.vue';
+import InsightModalFooter from '@/components/insights/Layout/HeaderGenerateInsights/InsightModalFooter.vue';
+import HeaderGenerateInsightText from '@/components/insights/Layout/HeaderGenerateInsights/HeaderGenerateInsightText.vue';
+import firebaseService from '@/services/api/resources/GPT';
+
+vi.mock('@/services/api/resources/GPT');
+
+describe('HeaderGenerateInsightModal.vue', () => {
+ let wrapper;
+ let mockStore;
+
+ beforeEach(() => {
+ mockStore = {
+ state: {
+ widgets: {
+ currentDashboardWidgets: [
+ {
+ type: 'card',
+ name: 'Widget1',
+ config: { data_type: 'sec' },
+ data: { value: 60 },
+ },
+ ],
+ },
+ gpt: {
+ insights: [{ received: { value: 'Sample Insight' } }],
+ },
+ user: {
+ email: 'test@example.com',
+ },
+ },
+ dispatch: vi.fn(),
+ };
+
+ wrapper = mount(HeaderGenerateInsightModal, {
+ global: {
+ stubs: {
+ UnnnicIcon: true,
+ Transition: true,
+ },
+ mocks: {
+ $store: mockStore,
+ $t: (key) => key,
+ },
+ },
+ props: {
+ show: true,
+ },
+ });
+ });
+
+ it('renders modal when show is true', () => {
+ expect(wrapper.find('.header-generate-insight-modal').exists()).toBe(true);
+ });
+
+ it('emits close event when close button is clicked', async () => {
+ await wrapper.find('.header__close-button').trigger('click');
+ expect(wrapper.emitted('close')).toBeTruthy();
+ });
+
+ it('renders HeaderGenerateInsightText component', () => {
+ expect(wrapper.findComponent(HeaderGenerateInsightText).exists()).toBe(
+ true,
+ );
+ });
+
+ it('renders InsightModalFooter component', () => {
+ expect(wrapper.findComponent(InsightModalFooter).exists()).toBe(true);
+ });
+
+ it('calls generateInsight when show prop changes to true', async () => {
+ const generateInsightSpy = vi.spyOn(wrapper.vm, 'generateInsight');
+ await wrapper.setProps({ show: false });
+ await wrapper.setProps({ show: true });
+ expect(generateInsightSpy).toHaveBeenCalled();
+ });
+
+ it('handles typing complete event', async () => {
+ await wrapper
+ .findComponent(HeaderGenerateInsightText)
+ .vm.$emit('typing-complete');
+ expect(wrapper.vm.isRenderFeedback).toBe(true);
+ });
+
+ it('handles positive feedback', async () => {
+ await wrapper.vm.handlePositiveFeedback();
+ expect(wrapper.vm.isBtnYesActive).toBe(true);
+ expect(wrapper.vm.isBtnNoActive).toBe(false);
+ });
+
+ it('handles negative feedback', async () => {
+ await wrapper.vm.handleNegativeFeedback();
+ expect(wrapper.vm.isBtnNoActive).toBe(true);
+ expect(wrapper.vm.isBtnYesActive).toBe(false);
+ });
+
+ it('submits review successfully', async () => {
+ firebaseService.createReview.mockResolvedValue();
+ wrapper.vm.isBtnYesActive = true;
+ wrapper.vm.feedbackText = 'Great insight!';
+ await wrapper.vm.submitReview();
+ expect(firebaseService.createReview).toHaveBeenCalledWith({
+ helpful: true,
+ comment: 'Great insight!',
+ user: 'test@example.com',
+ });
+ expect(wrapper.vm.isFeedbackSent).toBe(true);
+ });
+
+ it('handles dynamic param correctly', () => {
+ const widget = {
+ type: 'card',
+ name: 'TestWidget',
+ config: { data_type: 'sec' },
+ data: { value: 120 },
+ };
+ const result = wrapper.vm.handleDynamicParam(widget);
+ expect(result).toBe('TestWidget 2m');
+ });
+
+ it('handles dynamic params data undefined correctly', () => {
+ const widget = {
+ type: 'card',
+ name: 'TestWidget',
+ config: { data_type: 'other' },
+ data: { value: null },
+ };
+ const result = wrapper.vm.handleDynamicParam(widget);
+ expect(result).toBe('0 TestWidget');
+ });
+
+ it('generates insight correctly', async () => {
+ mockStore.dispatch.mockResolvedValue();
+ await wrapper.vm.generateInsight();
+ expect(mockStore.dispatch).toHaveBeenCalledWith('gpt/getInsights', {
+ prompt: expect.any(String),
+ });
+ expect(wrapper.vm.generatedInsight).toBe('Sample Insight');
+ });
+
+ it('handles generate insight error', async () => {
+ mockStore.dispatch.mockRejectedValue(new Error('API Error'));
+ await wrapper.vm.generateInsight();
+ expect(wrapper.vm.generatedInsight).toBe(
+ wrapper.vm.$t('insights_header.generate_insight.error'),
+ );
+ expect(wrapper.vm.generateInsightError).toBe(true);
+ });
+
+ it('cleans up observer on component unmount', () => {
+ const disconnectMock = vi.fn();
+ wrapper.vm.observer = { disconnect: disconnectMock };
+ wrapper.unmount();
+ expect(disconnectMock).toHaveBeenCalled();
+ });
+
+ it('returns positive placeholder when isBtnYesActive is true', () => {
+ wrapper.vm.isBtnYesActive = true;
+ expect(wrapper.vm.handlePlaceholderTextArea()).toBe(
+ wrapper.vm.$t(
+ 'insights_header.generate_insight.input.placeholder_positive',
+ ),
+ );
+ });
+
+ it('returns negative placeholder when isBtnYesActive is false', () => {
+ wrapper.vm.isBtnYesActive = false;
+ expect(wrapper.vm.handlePlaceholderTextArea()).toBe(
+ wrapper.vm.$t(
+ 'insights_header.generate_insight.input.placeholder_negative',
+ ),
+ );
+ });
+
+ it('returns negative placeholder when isBtnYesActive is undefined', () => {
+ wrapper.vm.isBtnYesActive = undefined;
+ expect(wrapper.vm.handlePlaceholderTextArea()).toBe(
+ wrapper.vm.$t(
+ 'insights_header.generate_insight.input.placeholder_negative',
+ ),
+ );
+ });
+});
diff --git a/src/components/insights/Layout/HeaderGenerateInsights/__tests__/HeaderGenerateInsightText.spec.js b/src/components/insights/Layout/HeaderGenerateInsights/__tests__/HeaderGenerateInsightText.spec.js
new file mode 100644
index 00000000..e6011ce5
--- /dev/null
+++ b/src/components/insights/Layout/HeaderGenerateInsights/__tests__/HeaderGenerateInsightText.spec.js
@@ -0,0 +1,109 @@
+import { beforeEach, describe, expect, it, vi } from 'vitest';
+import { mount } from '@vue/test-utils';
+import HeaderGenerateInsightText from '@/components/insights/Layout/HeaderGenerateInsights/HeaderGenerateInsightText.vue';
+import Markdown from '@/components/Markdown.vue';
+
+const createWrapper = (props = {}) => {
+ return mount(HeaderGenerateInsightText, {
+ global: {
+ stubs: { Markdown: true },
+ },
+ props,
+ });
+};
+
+describe('HeaderGenerateInsightText', () => {
+ let wrapper;
+
+ beforeEach(() => {
+ wrapper = createWrapper();
+ });
+
+ it('should render the "generating insights" section when the text is not displayed', () => {
+ expect(
+ wrapper.find('.header-generate-insight-text__generating').exists(),
+ ).toBe(true);
+ expect(wrapper.text()).toContain(
+ wrapper.vm.$t('insights_header.generate_insight.generating_insights'),
+ );
+ });
+
+ it('should render the three generation animation points', () => {
+ const dots = wrapper.findAll('.generating__dot');
+ expect(dots.length).toBe(3);
+ });
+
+ it('should render Markdown content when text is displayed', async () => {
+ wrapper = createWrapper({ text: 'Generated Insight Text' });
+ await wrapper.vm.$nextTick();
+
+ expect(
+ wrapper.find('.header-generate-insight-text__generated').exists(),
+ ).toBe(true);
+ const markdown = wrapper.findComponent(Markdown);
+ expect(markdown.exists()).toBe(true);
+ expect(markdown.props('content')).toBe('Generated Insight Text');
+ });
+
+ it('should correctly compute "displayedText" based on animation or full text', async () => {
+ wrapper = createWrapper({ text: 'Full Generated Text' });
+ await wrapper.vm.$nextTick();
+
+ expect(wrapper.vm.displayedText).toBe('Full Generated Text');
+
+ wrapper.vm.isTyping = true;
+ wrapper.vm.animatedText = 'Typing...';
+ await wrapper.vm.$nextTick();
+ expect(wrapper.vm.displayedText).toBe('Typing...');
+ });
+
+ it('should call the typeWriter method and display text according to the animation', async () => {
+ wrapper = createWrapper();
+
+ const typeWriterSpy = vi.spyOn(wrapper.vm, 'typeWriter');
+ await wrapper.setProps({ text: 'Typing Test' });
+
+ expect(typeWriterSpy).toHaveBeenCalledWith('Typing Test', 1);
+ });
+
+ it('should run the typing animation correctly', async () => {
+ const text = 'Insight';
+ const wrapper = createWrapper();
+
+ await wrapper.vm.typeWriter(text, 1);
+
+ expect(wrapper.vm.animatedText).toBe(text);
+ expect(wrapper.vm.isTyping).toBe(false);
+ expect(wrapper.emitted('typingComplete')).toBeTruthy();
+ });
+
+ it('should output “typingComplete” after the typing animation', async () => {
+ const wrapper = createWrapper();
+ wrapper.setProps({ text: 'Test Text' });
+
+ await wrapper.vm.typeWriter('Test Text', 1);
+ expect(wrapper.vm.isTyping).toBe(false);
+ expect(wrapper.emitted().typingComplete).toBeTruthy();
+ });
+
+ it('should start the typing animation when modifying the “text” prop', async () => {
+ const wrapper = createWrapper({ text: '' });
+
+ const typeWriterSpy = vi.spyOn(wrapper.vm, 'typeWriter');
+ await wrapper.setProps({ text: 'New Text' });
+
+ expect(typeWriterSpy).toHaveBeenCalledWith('New Text', 1);
+ });
+
+ it('should not render the “generating insights” section if text is available', async () => {
+ wrapper = createWrapper({ text: 'Some Insight Text' });
+ await wrapper.vm.$nextTick();
+
+ expect(
+ wrapper.find('.header-generate-insight-text__generating').exists(),
+ ).toBe(false);
+ expect(
+ wrapper.find('.header-generate-insight-text__generated').exists(),
+ ).toBe(true);
+ });
+});
diff --git a/src/components/insights/Layout/HeaderGenerateInsights/__tests__/InsightModalFooter.spec.js b/src/components/insights/Layout/HeaderGenerateInsights/__tests__/InsightModalFooter.spec.js
new file mode 100644
index 00000000..42037a0e
--- /dev/null
+++ b/src/components/insights/Layout/HeaderGenerateInsights/__tests__/InsightModalFooter.spec.js
@@ -0,0 +1,231 @@
+import { mount, flushPromises } from '@vue/test-utils';
+import InsightModalFooter from '@/components/insights/Layout/HeaderGenerateInsights/InsightModalFooter.vue';
+import { describe, it, expect, vi } from 'vitest';
+
+describe('InsightModalFooter.vue', () => {
+ const defaultProps = {
+ generatedInsight: 'Sample Insight',
+ isFeedbackSent: false,
+ isRenderFooterFeedback: true,
+ isBtnYesActive: false,
+ isBtnNoActive: false,
+ feedbackText: '',
+ isSubmitFeedbackLoading: false,
+ };
+
+ const mountComponent = (props = {}, stubs = {}) => {
+ return mount(InsightModalFooter, {
+ attachTo: document.body,
+ global: {
+ stubs: {
+ UnnnicButton: true,
+ ...stubs,
+ },
+ },
+ props: {
+ ...defaultProps,
+ ...props,
+ },
+ });
+ };
+
+ beforeAll(() => {
+ Element.prototype.scrollIntoView = vi.fn();
+ });
+
+ it('renders footer when generatedInsight is present', () => {
+ const wrapper = mountComponent();
+ expect(wrapper.find('.content__footer').exists()).toBe(true);
+ });
+
+ it('shows feedback sent message when isFeedbackSent is true', () => {
+ const wrapper = mountComponent({ isFeedbackSent: true });
+ expect(wrapper.find('.feedback_sent').exists()).toBe(true);
+ expect(wrapper.find('.feedback_sent').text()).toContain(
+ `✨${wrapper.vm.$t('insights_header.generate_insight.feedback.complete')}`,
+ );
+ });
+
+ it('does not show feedback sent message when isFeedbackSent is false', () => {
+ const wrapper = mountComponent({ isFeedbackSent: false });
+ expect(wrapper.find('.feedback_sent').exists()).toBe(false);
+ });
+
+ it('emits "handle-positive-feedback" when positive feedback button is clicked', async () => {
+ const wrapper = mountComponent();
+ await wrapper
+ .findComponent('[data-test="feedback-positive-btn"]')
+ .trigger('click');
+ expect(wrapper.emitted('handle-positive-feedback')).toBeTruthy();
+ });
+
+ it('emits "handle-negative-feedback" when negative feedback button is clicked', async () => {
+ const wrapper = mountComponent();
+ await wrapper
+ .findComponent('[data-test="feedback-negative-btn"]')
+ .trigger('click');
+ expect(wrapper.emitted('handle-negative-feedback')).toBeTruthy();
+ });
+
+ it('displays feedback textarea and send button when either button is active', () => {
+ const wrapper = mountComponent({
+ isBtnYesActive: true,
+ isBtnNoActive: false,
+ });
+ expect(wrapper.find('textarea').exists()).toBe(true);
+ expect(wrapper.find('.footer__feedback__btn_send').exists()).toBe(true);
+ });
+
+ it('emits "submit-review" when the send button is clicked', async () => {
+ const wrapper = mountComponent({
+ isBtnYesActive: true,
+ });
+ await wrapper.find('.footer__feedback__btn_send').trigger('click');
+ expect(wrapper.emitted('submit-review')).toBeTruthy();
+
+ const secondWrapper = mountComponent({
+ isBtnNoActive: true,
+ });
+
+ await secondWrapper.find('.footer__feedback__btn_send').trigger('click');
+ expect(secondWrapper.emitted('submit-review')).toBeTruthy();
+ });
+
+ it('handles placeholder text area correctly', async () => {
+ const wrapper = mountComponent({ isBtnYesActive: true });
+ expect(wrapper.vm.handlePlaceholderTextArea()).toContain(
+ wrapper.vm.$t(
+ 'insights_header.generate_insight.input.placeholder_positive',
+ ),
+ );
+ await wrapper.setProps({ isBtnYesActive: false });
+ expect(wrapper.vm.handlePlaceholderTextArea()).toContain(
+ wrapper.vm.$t(
+ 'insights_header.generate_insight.input.placeholder_negative',
+ ),
+ );
+ });
+
+ it('scrolls to the end when isFeedbackSent changes to true', async () => {
+ const wrapper = mountComponent({
+ isFeedbackSent: false,
+ });
+
+ await wrapper.setProps({ isFeedbackSent: true });
+
+ const scrollTargetElement = wrapper.find({ ref: 'scrollTarget' });
+
+ const scrollIntoViewMock = vi.fn();
+
+ Object.defineProperty(scrollTargetElement.element, 'scrollIntoView', {
+ value: scrollIntoViewMock,
+ writable: true,
+ });
+
+ await wrapper.vm.$nextTick();
+
+ expect(scrollIntoViewMock).toHaveBeenCalledWith({
+ behavior: 'smooth',
+ });
+ });
+
+ it('scrolls to the end when isBtnYesActive changes to true', async () => {
+ const wrapper = mountComponent({
+ isBtnYesActive: false,
+ });
+
+ await wrapper.setProps({ isBtnYesActive: true });
+
+ const scrollTargetElement = wrapper.find({ ref: 'scrollTarget' });
+
+ const scrollIntoViewMock = vi.fn();
+ Object.defineProperty(scrollTargetElement.element, 'scrollIntoView', {
+ value: scrollIntoViewMock,
+ writable: true,
+ });
+
+ await wrapper.vm.$nextTick();
+
+ expect(scrollIntoViewMock).toHaveBeenCalledWith({
+ behavior: 'smooth',
+ });
+ });
+
+ it('scrolls to the end when isBtnNoActive changes to true', async () => {
+ const wrapper = mountComponent({
+ isBtnNoActive: false,
+ });
+
+ await wrapper.setProps({ isBtnNoActive: true });
+
+ const scrollTargetElement = wrapper.find({ ref: 'scrollTarget' });
+
+ const scrollIntoViewMock = vi.fn();
+ Object.defineProperty(scrollTargetElement.element, 'scrollIntoView', {
+ value: scrollIntoViewMock,
+ writable: true,
+ });
+
+ await wrapper.vm.$nextTick();
+
+ expect(scrollIntoViewMock).toHaveBeenCalledWith({
+ behavior: 'smooth',
+ });
+ });
+
+ it('scrollToEnd calls scrollIntoView when scrollTarget exists', () => {
+ const wrapper = mountComponent({
+ isFeedbackSent: true,
+ });
+
+ const scrollTargetElement = wrapper.find({ ref: 'scrollTarget' });
+
+ const scrollIntoViewMock = vi.fn();
+ Object.defineProperty(scrollTargetElement.element, 'scrollIntoView', {
+ value: scrollIntoViewMock,
+ writable: true,
+ });
+
+ wrapper.vm.scrollToEnd();
+
+ expect(scrollIntoViewMock).toHaveBeenCalledWith({
+ behavior: 'smooth',
+ });
+ });
+
+ it('does not call scrollIntoView when scrollTarget does not exist', async () => {
+ const wrapper = mountComponent({
+ isFeedbackSent: false,
+ });
+
+ const scrollIntoViewMock = vi.fn();
+
+ await wrapper.vm.$nextTick();
+
+ wrapper.vm.scrollToEnd();
+
+ expect(scrollIntoViewMock).not.toHaveBeenCalled();
+ });
+
+ it('calls the appropriate method when isFeedbackSent changes to true', async () => {
+ const wrapper = mountComponent({
+ isFeedbackSent: false,
+ });
+
+ const scrollTargetElement = wrapper.find({ ref: 'scrollTarget' });
+
+ expect(scrollTargetElement.exists()).toBe(false);
+
+ const scrollToEndMock = vi.spyOn(wrapper.vm, 'scrollToEnd');
+
+ await wrapper.setProps({ isFeedbackSent: true });
+
+ await wrapper.vm.$nextTick();
+
+ expect(wrapper.find({ ref: 'scrollTarget' }).exists()).toBe(true);
+
+ expect(scrollToEndMock).toHaveBeenCalled();
+
+ expect(scrollToEndMock).toHaveBeenCalledTimes(1);
+ });
+});
diff --git a/src/locales/translations.json b/src/locales/translations.json
index 601fe0ad..f03beabf 100644
--- a/src/locales/translations.json
+++ b/src/locales/translations.json
@@ -721,9 +721,82 @@
"pt-br": "Falha ao definir o Dashboard {dashboard} como página inicial",
"en": "Failure when defining the Dashboard {dashboard} as homepage",
"es": "Falla al definir el Tablero {dashboard} como página de inicio"
+ },
+ "generate_insight": {
+ "title": {
+ "pt-br": "Gerar insight",
+ "en": "Generate insight",
+ "es": "Generar insight"
+ },
+ "generating_insights": {
+ "pt-br": "Gerando insights",
+ "en": "Generating insights",
+ "es": "Generando insights"
+ },
+ "input": {
+ "placeholder_positive": {
+ "pt-br": "Compartilhe sua experiência",
+ "en": "Share your experience",
+ "es": "Comparta su experiencia"
+ },
+ "placeholder_negative": {
+ "pt-br": "Como podemos melhorar?",
+ "en": "How can we improve?",
+ "es": "¿Cómo podemos mejorar?"
+ }
+ },
+ "feedback": {
+ "question": {
+ "pt-br": "O Insight fornecido foi útil?",
+ "en": "Was the Insight provided useful?",
+ "es": "¿Fue útil la información proporcionada?"
+ },
+ "complete": {
+ "pt-br": "Obrigado pelo feedback!",
+ "en": "Thanks for the feedback!",
+ "es": "¡Gracias por los comentarios!"
+ }
+ },
+ "button": {
+ "yes": {
+ "pt-br": "Sim",
+ "en": "Yes",
+ "es": "Sí"
+ },
+ "no": {
+ "pt-br": "Não",
+ "en": "No",
+ "es": "No"
+ },
+ "send": {
+ "pt-br": "Enviar",
+ "en": "Send",
+ "es": "No"
+ }
+ },
+ "by_ai": {
+ "pt-br": "por Meta Llama",
+ "en": "by Meta Llama",
+ "es": "por Meta Llama"
+ },
+ "error": {
+ "pt-br": "Lamentamos, mas infelizmente não foi possível gerar um Insight, verifique sua conexão com internet ou tente novamente mais tarde",
+ "en": "We're sorry, but unfortunately we were unable to generate an Insight, please check your internet connection or try again later",
+ "es": "Lo sentimos, pero desafortunadamente no hemos podido generar un Insight, por favor compruebe su conexión a Internet o inténtelo de nuevo más tarde."
+ },
+ "prompt": {
+ "pt-br": "Analise o desempenho de um dashboard de resultados de uma operação de atendimento humano. O dashboard exibe três categorias principais: atendimentos em andamento, encerrados e aguardando. Além disso, avalie as seguintes métricas: tempo de primeira resposta, tempo de espera e tempo de interação. Identifique padrões de eficiência, possíveis gargalos no fluxo de atendimento e ofereça sugestões para otimizar o tempo de resposta e a produtividade da equipe. Dados: {values}",
+ "en": "Analyze the performance of a human service operation's results dashboard. The dashboard displays three main categories: calls in progress, closed and waiting. In addition, evaluate the following metrics: first response time, waiting time and interaction time. Identify efficiency patterns, possible bottlenecks in the service flow and offer suggestions for optimizing response time and team productivity. Data: {values}",
+ "es": "Analice el rendimiento del cuadro de mando de resultados de una operación de servicios humanos. El cuadro de mandos muestra tres categorías principales: llamadas en curso, cerradas y en espera. Además, evalúa las siguientes métricas: tiempo de primera respuesta, tiempo de espera y tiempo de interacción. Identifique patrones de eficiencia, posibles cuellos de botella en el flujo de servicio y ofrezca sugerencias para optimizar los tiempos de respuesta y la productividad del equipo. Datos: {values}"
+ },
+ "prompt_language": {
+ "pt-br": "Retorne em Português brasileiro",
+ "en": "Return in English",
+ "es": "Regreso en español"
+ }
}
},
- "new_dashboard": {
+ "new_dashboard": {
"title": {
"pt-br": "Novo dashboard",
"en": "New dashboard",
diff --git a/src/main.js b/src/main.js
index d1f96be8..9eff811e 100644
--- a/src/main.js
+++ b/src/main.js
@@ -6,6 +6,7 @@ import store from './store';
import Unnnic from './utils/plugins/UnnnicSystem';
import i18n from './utils/plugins/i18n';
import './utils/plugins/Hotjar.js';
+import './utils/plugins/Firebase.js';
import '@weni/unnnic-system/dist/style.css';
diff --git a/src/services/api/resources/GPT.js b/src/services/api/resources/GPT.js
index c6972e67..48aad27c 100644
--- a/src/services/api/resources/GPT.js
+++ b/src/services/api/resources/GPT.js
@@ -1,24 +1,31 @@
-import axios from 'axios';
-import env from '@/utils/env';
-
-const http = axios.create({
- headers: {
- Authorization: env('VITE_GPT_AUTH'),
- },
-});
+import { db as firebaseDB } from '@/utils/plugins/Firebase.js';
+import { collection, addDoc } from 'firebase/firestore';
+import http from '@/services/api/http';
+import Config from '@/store/modules/config';
export default {
async getInsights(prompt) {
- const response = await http.post(env('VITE_GPT_URL'), {
- messages: [
- {
- role: 'user',
- content: `${prompt}`,
- },
- ],
- model: 'llama3-8b-8192',
- });
+ const { project } = Config.state;
+ const response = await http.post(
+ `/projects/${project.uuid}/sources/chat_completion/search/`,
+ {
+ prompt,
+ },
+ );
+
+ return response;
+ },
- return response.data?.choices?.[0].message?.content;
+ async createReview({ user, helpful, comment }) {
+ try {
+ return await addDoc(collection(firebaseDB, 'AI Reviews'), {
+ user,
+ helpful,
+ comment,
+ timestamp: new Date(),
+ });
+ } catch (error) {
+ console.error('Error writing AI Reviews document: ', error);
+ }
},
};
diff --git a/src/store/index.js b/src/store/index.js
index 3af67e2f..8f1af312 100644
--- a/src/store/index.js
+++ b/src/store/index.js
@@ -8,6 +8,7 @@ import reports from './modules/reports';
import project from './modules/project';
import widgets from './modules/widgets.ts';
import onboarding from './modules/onboarding';
+import user from './modules/user';
const store = createStore({
modules: {
@@ -20,6 +21,7 @@ const store = createStore({
project,
widgets,
onboarding,
+ user,
},
});
diff --git a/src/store/modules/user.js b/src/store/modules/user.js
new file mode 100644
index 00000000..cd72d0b6
--- /dev/null
+++ b/src/store/modules/user.js
@@ -0,0 +1,21 @@
+export default {
+ namespaced: true,
+ state: {
+ email: '',
+ },
+ mutations: {
+ SET_EMAIL(state, email) {
+ state.email = email;
+ },
+ },
+ actions: {
+ setEmail({ commit }, email) {
+ commit('SET_EMAIL', email);
+ },
+ },
+ getters: {
+ getEmail(state) {
+ return state.email;
+ },
+ },
+};
diff --git a/src/utils/env.js b/src/utils/env.js
index 5a2dcc0b..50c88a95 100644
--- a/src/utils/env.js
+++ b/src/utils/env.js
@@ -1,4 +1,7 @@
export default function env(name) {
+ if (import.meta.env[name] && name === 'VITE_FIREBASE_CONFIG')
+ return JSON.parse(import.meta.env[name]);
+
return (
window?.configs?.[name] ||
import.meta.env[name] ||
diff --git a/src/utils/plugins/Firebase.js b/src/utils/plugins/Firebase.js
new file mode 100644
index 00000000..a68630f7
--- /dev/null
+++ b/src/utils/plugins/Firebase.js
@@ -0,0 +1,8 @@
+import { initializeApp } from 'firebase/app';
+import { getFirestore } from 'firebase/firestore';
+import env from '../env';
+
+const app = initializeApp(env('VITE_FIREBASE_CONFIG') || '');
+const db = getFirestore(app);
+
+export { db };
diff --git a/yarn.lock b/yarn.lock
index b7ad3231..973719cd 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -699,11 +699,413 @@
minimatch "^3.0.4"
strip-json-comments "^3.1.1"
+"@firebase/analytics-compat@0.2.13":
+ version "0.2.13"
+ resolved "https://registry.yarnpkg.com/@firebase/analytics-compat/-/analytics-compat-0.2.13.tgz#de8858e578616c4b7a2e209ba14d21cef03e0f9d"
+ integrity sha512-aZ4wGfNDMsCxhKzDbK2g1aV0JKsdQ9FbeIsjpNJPzhahV0XYj+z36Y4RNLPpG/6hHU4gxnezxs+yn3HhHkNL8w==
+ dependencies:
+ "@firebase/analytics" "0.10.7"
+ "@firebase/analytics-types" "0.8.2"
+ "@firebase/component" "0.6.8"
+ "@firebase/util" "1.9.7"
+ tslib "^2.1.0"
+
+"@firebase/analytics-types@0.8.2":
+ version "0.8.2"
+ resolved "https://registry.yarnpkg.com/@firebase/analytics-types/-/analytics-types-0.8.2.tgz#947f85346e404332aac6c996d71fd4a89cd7f87a"
+ integrity sha512-EnzNNLh+9/sJsimsA/FGqzakmrAUKLeJvjRHlg8df1f97NLUlFidk9600y0ZgWOp3CAxn6Hjtk+08tixlUOWyw==
+
+"@firebase/analytics@0.10.7":
+ version "0.10.7"
+ resolved "https://registry.yarnpkg.com/@firebase/analytics/-/analytics-0.10.7.tgz#6b6e51002d7a4c8f87c0a8c933cc5c26b37b205a"
+ integrity sha512-GE29uTT6y/Jv2EP0OjpTezeTQZ5FTCTaZXKrrdVGjb/t35AU4u/jiU+hUwUPpuK8fqhhiHkS/AawE3a3ZK/a9Q==
+ dependencies:
+ "@firebase/component" "0.6.8"
+ "@firebase/installations" "0.6.8"
+ "@firebase/logger" "0.4.2"
+ "@firebase/util" "1.9.7"
+ tslib "^2.1.0"
+
+"@firebase/app-check-compat@0.3.14":
+ version "0.3.14"
+ resolved "https://registry.yarnpkg.com/@firebase/app-check-compat/-/app-check-compat-0.3.14.tgz#503593a6cf23b7d318f64dfa1f48f759325602eb"
+ integrity sha512-kK3bPfojAfXE53W+20rxMqIxrloFswXG9vh4kEdYL6Wa2IB3sD5++2dPiK3yGxl8oQiqS8qL2wcKB5/xLpEVEg==
+ dependencies:
+ "@firebase/app-check" "0.8.7"
+ "@firebase/app-check-types" "0.5.2"
+ "@firebase/component" "0.6.8"
+ "@firebase/logger" "0.4.2"
+ "@firebase/util" "1.9.7"
+ tslib "^2.1.0"
+
+"@firebase/app-check-interop-types@0.3.2":
+ version "0.3.2"
+ resolved "https://registry.yarnpkg.com/@firebase/app-check-interop-types/-/app-check-interop-types-0.3.2.tgz#455b6562c7a3de3ef75ea51f72dfec5829ad6997"
+ integrity sha512-LMs47Vinv2HBMZi49C09dJxp0QT5LwDzFaVGf/+ITHe3BlIhUiLNttkATSXplc89A2lAaeTqjgqVkiRfUGyQiQ==
+
+"@firebase/app-check-types@0.5.2":
+ version "0.5.2"
+ resolved "https://registry.yarnpkg.com/@firebase/app-check-types/-/app-check-types-0.5.2.tgz#1221bd09b471e11bb149252f16640a0a51043cbc"
+ integrity sha512-FSOEzTzL5bLUbD2co3Zut46iyPWML6xc4x+78TeaXMSuJap5QObfb+rVvZJtla3asN4RwU7elaQaduP+HFizDA==
+
+"@firebase/app-check@0.8.7":
+ version "0.8.7"
+ resolved "https://registry.yarnpkg.com/@firebase/app-check/-/app-check-0.8.7.tgz#b5f01116b99eb7d12d57a1cb1595796f8196ca7f"
+ integrity sha512-EkOeJcMKVR0zZ6z/jqcFTqHb/xq+TVIRIuBNGHdpcIuFU1czhSlegvqv2+nC+nFrkD8M6Xvd3tAlUOkdbMeS6A==
+ dependencies:
+ "@firebase/component" "0.6.8"
+ "@firebase/logger" "0.4.2"
+ "@firebase/util" "1.9.7"
+ tslib "^2.1.0"
+
+"@firebase/app-compat@0.2.40":
+ version "0.2.40"
+ resolved "https://registry.yarnpkg.com/@firebase/app-compat/-/app-compat-0.2.40.tgz#d8cc5d2e5a00e2d36b074b405c9472e84a27b750"
+ integrity sha512-2L5MW4MH2ya7Wvw0hzWy3ZWeB4SqC5gYXDAV5AS1lBTL4zL3k8dsqJmry/cFV00GgkCI01WJbcXvFMCXJvgyow==
+ dependencies:
+ "@firebase/app" "0.10.10"
+ "@firebase/component" "0.6.8"
+ "@firebase/logger" "0.4.2"
+ "@firebase/util" "1.9.7"
+ tslib "^2.1.0"
+
+"@firebase/app-types@0.9.2":
+ version "0.9.2"
+ resolved "https://registry.yarnpkg.com/@firebase/app-types/-/app-types-0.9.2.tgz#8cbcceba784753a7c0066a4809bc22f93adee080"
+ integrity sha512-oMEZ1TDlBz479lmABwWsWjzHwheQKiAgnuKxE0pz0IXCVx7/rtlkx1fQ6GfgK24WCrxDKMplZrT50Kh04iMbXQ==
+
+"@firebase/app@0.10.10":
+ version "0.10.10"
+ resolved "https://registry.yarnpkg.com/@firebase/app/-/app-0.10.10.tgz#acdccd79b895cfa4d082d74d02594380444e2292"
+ integrity sha512-sDqkdeFdVn5uygQm5EuIKOQ6/wxTcX/qKfm0MR46AiwLRHGLCDUMrXBkc8GhkK3ca2d6mPUSfPmndggo43D6PQ==
+ dependencies:
+ "@firebase/component" "0.6.8"
+ "@firebase/logger" "0.4.2"
+ "@firebase/util" "1.9.7"
+ idb "7.1.1"
+ tslib "^2.1.0"
+
+"@firebase/auth-compat@0.5.13":
+ version "0.5.13"
+ resolved "https://registry.yarnpkg.com/@firebase/auth-compat/-/auth-compat-0.5.13.tgz#59ec6d35c1efd4fb834c6130a8e1b76962c65c1c"
+ integrity sha512-rV6TMxUU6wBBZ2ouDMtjJsJXeewtvYvVzslzt3/P7O/kxiWlreHT/2M/1guMiXKo3zk52XK3GqP0uM2bN7fEow==
+ dependencies:
+ "@firebase/auth" "1.7.8"
+ "@firebase/auth-types" "0.12.2"
+ "@firebase/component" "0.6.8"
+ "@firebase/util" "1.9.7"
+ tslib "^2.1.0"
+ undici "6.19.7"
+
+"@firebase/auth-interop-types@0.2.3":
+ version "0.2.3"
+ resolved "https://registry.yarnpkg.com/@firebase/auth-interop-types/-/auth-interop-types-0.2.3.tgz#927f1f2139a680b55fef0bddbff2c982b08587e8"
+ integrity sha512-Fc9wuJGgxoxQeavybiuwgyi+0rssr76b+nHpj+eGhXFYAdudMWyfBHvFL/I5fEHniUM/UQdFzi9VXJK2iZF7FQ==
+
+"@firebase/auth-types@0.12.2":
+ version "0.12.2"
+ resolved "https://registry.yarnpkg.com/@firebase/auth-types/-/auth-types-0.12.2.tgz#f12d890585866e53b6ab18b16fa4d425c52eee6e"
+ integrity sha512-qsEBaRMoGvHO10unlDJhaKSuPn4pyoTtlQuP1ghZfzB6rNQPuhp/N/DcFZxm9i4v0SogjCbf9reWupwIvfmH6w==
+
+"@firebase/auth@1.7.8":
+ version "1.7.8"
+ resolved "https://registry.yarnpkg.com/@firebase/auth/-/auth-1.7.8.tgz#554e90620c2d6a8db332898879dbdab861f969b7"
+ integrity sha512-1KJlDrTrEEFTIBj9MxjAWjQ4skecBD4bmoayQ0l14QDbNc1a8qGbi+MFSJkH7O6VnGE6bTMcWSw6RrQNecqKaw==
+ dependencies:
+ "@firebase/component" "0.6.8"
+ "@firebase/logger" "0.4.2"
+ "@firebase/util" "1.9.7"
+ tslib "^2.1.0"
+ undici "6.19.7"
+
+"@firebase/component@0.6.8":
+ version "0.6.8"
+ resolved "https://registry.yarnpkg.com/@firebase/component/-/component-0.6.8.tgz#899b9318c0ce0586580e8cda7eaf61296f7fb43b"
+ integrity sha512-LcNvxGLLGjBwB0dJUsBGCej2fqAepWyBubs4jt1Tiuns7QLbXHuyObZ4aMeBjZjWx4m8g1LoVI9QFpSaq/k4/g==
+ dependencies:
+ "@firebase/util" "1.9.7"
+ tslib "^2.1.0"
+
+"@firebase/database-compat@1.0.7":
+ version "1.0.7"
+ resolved "https://registry.yarnpkg.com/@firebase/database-compat/-/database-compat-1.0.7.tgz#5c761bea1a78daea76fecc4bf5de5d6915a1c3b4"
+ integrity sha512-R/3B+VVzEFN5YcHmfWns3eitA8fHLTL03io+FIoMcTYkajFnrBdS3A+g/KceN9omP7FYYYGTQWF9lvbEx6eMEg==
+ dependencies:
+ "@firebase/component" "0.6.8"
+ "@firebase/database" "1.0.7"
+ "@firebase/database-types" "1.0.4"
+ "@firebase/logger" "0.4.2"
+ "@firebase/util" "1.9.7"
+ tslib "^2.1.0"
+
+"@firebase/database-types@1.0.4":
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/@firebase/database-types/-/database-types-1.0.4.tgz#dc507f7838ed29ac3235c68ebae5fd42a562e3e8"
+ integrity sha512-mz9ZzbH6euFXbcBo+enuJ36I5dR5w+enJHHjy9Y5ThCdKUseqfDjW3vCp1YxE9zygFCSjJJ/z1cQ+zodvUcwPQ==
+ dependencies:
+ "@firebase/app-types" "0.9.2"
+ "@firebase/util" "1.9.7"
+
+"@firebase/database@1.0.7":
+ version "1.0.7"
+ resolved "https://registry.yarnpkg.com/@firebase/database/-/database-1.0.7.tgz#0794801ab1e63336eda69401131228bb85aa6776"
+ integrity sha512-wjXr5AO8RPxVVg7rRCYffT7FMtBjHRfJ9KMwi19MbOf0vBf0H9YqW3WCgcnLpXI6ehiUcU3z3qgPnnU0nK6SnA==
+ dependencies:
+ "@firebase/app-check-interop-types" "0.3.2"
+ "@firebase/auth-interop-types" "0.2.3"
+ "@firebase/component" "0.6.8"
+ "@firebase/logger" "0.4.2"
+ "@firebase/util" "1.9.7"
+ faye-websocket "0.11.4"
+ tslib "^2.1.0"
+
+"@firebase/firestore-compat@0.3.36":
+ version "0.3.36"
+ resolved "https://registry.yarnpkg.com/@firebase/firestore-compat/-/firestore-compat-0.3.36.tgz#a776d410942176babecfda096a47f5d12ceb3fb7"
+ integrity sha512-NtoIm7CT9f+SFB7cPMCtyCSxZReh/+SII5X4TQH394S3dwhru9HIfvEOKAMuAnXsSsLH72jXPUgdsEAUqg6Oug==
+ dependencies:
+ "@firebase/component" "0.6.8"
+ "@firebase/firestore" "4.7.1"
+ "@firebase/firestore-types" "3.0.2"
+ "@firebase/util" "1.9.7"
+ tslib "^2.1.0"
+
+"@firebase/firestore-types@3.0.2":
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/@firebase/firestore-types/-/firestore-types-3.0.2.tgz#75c301acc5fa33943eaaa9570b963c55398cad2a"
+ integrity sha512-wp1A+t5rI2Qc/2q7r2ZpjUXkRVPtGMd6zCLsiWurjsQpqPgFin3AhNibKcIzoF2rnToNa/XYtyWXuifjOOwDgg==
+
+"@firebase/firestore@4.7.1":
+ version "4.7.1"
+ resolved "https://registry.yarnpkg.com/@firebase/firestore/-/firestore-4.7.1.tgz#43ef10038200e0fe96bbc31e095b3da2d0878aa9"
+ integrity sha512-WliQNa8GVcH6EWkH0NAf+uAnxNiBuH+G8Buzr2ZS1NznOhJDK/q6Hsjv5TzNrijLTAdEfj/wk9VEv994KDSjxg==
+ dependencies:
+ "@firebase/component" "0.6.8"
+ "@firebase/logger" "0.4.2"
+ "@firebase/util" "1.9.7"
+ "@firebase/webchannel-wrapper" "1.0.1"
+ "@grpc/grpc-js" "~1.9.0"
+ "@grpc/proto-loader" "^0.7.8"
+ tslib "^2.1.0"
+ undici "6.19.7"
+
+"@firebase/functions-compat@0.3.13":
+ version "0.3.13"
+ resolved "https://registry.yarnpkg.com/@firebase/functions-compat/-/functions-compat-0.3.13.tgz#074c8311ee3b9d0af403115e6812042824635ce3"
+ integrity sha512-qcZvJO2ed6PAD+18DanVztw7WyQVQK43HoRhxusHAwDFvK/xY+mcGpj+IpfdxTNMBGCOIxKFp4Xqk/c2nubBlQ==
+ dependencies:
+ "@firebase/component" "0.6.8"
+ "@firebase/functions" "0.11.7"
+ "@firebase/functions-types" "0.6.2"
+ "@firebase/util" "1.9.7"
+ tslib "^2.1.0"
+
+"@firebase/functions-types@0.6.2":
+ version "0.6.2"
+ resolved "https://registry.yarnpkg.com/@firebase/functions-types/-/functions-types-0.6.2.tgz#03b4ec9259d2f57548a3909d6a35ae35ad243552"
+ integrity sha512-0KiJ9lZ28nS2iJJvimpY4nNccV21rkQyor5Iheu/nq8aKXJqtJdeSlZDspjPSBBiHRzo7/GMUttegnsEITqR+w==
+
+"@firebase/functions@0.11.7":
+ version "0.11.7"
+ resolved "https://registry.yarnpkg.com/@firebase/functions/-/functions-0.11.7.tgz#0de6fe8c0445385e00b54a31fb2a68474e8fd777"
+ integrity sha512-xaUsGI2kYrI8zJXgrNB7SrJKB8v1vJqR16YYi6g6dFTgBz4+VzWJFqqVU60BbdAWm6fXnUrg9gjlJQeqomT2Vg==
+ dependencies:
+ "@firebase/app-check-interop-types" "0.3.2"
+ "@firebase/auth-interop-types" "0.2.3"
+ "@firebase/component" "0.6.8"
+ "@firebase/messaging-interop-types" "0.2.2"
+ "@firebase/util" "1.9.7"
+ tslib "^2.1.0"
+ undici "6.19.7"
+
+"@firebase/installations-compat@0.2.8":
+ version "0.2.8"
+ resolved "https://registry.yarnpkg.com/@firebase/installations-compat/-/installations-compat-0.2.8.tgz#ebc908afe84db2754b19a62f7655608911e13819"
+ integrity sha512-pI2q8JFHB7yIq/szmhzGSWXtOvtzl6tCUmyykv5C8vvfOVJUH6mP4M4iwjbK8S1JotKd/K70+JWyYlxgQ0Kpyw==
+ dependencies:
+ "@firebase/component" "0.6.8"
+ "@firebase/installations" "0.6.8"
+ "@firebase/installations-types" "0.5.2"
+ "@firebase/util" "1.9.7"
+ tslib "^2.1.0"
+
+"@firebase/installations-types@0.5.2":
+ version "0.5.2"
+ resolved "https://registry.yarnpkg.com/@firebase/installations-types/-/installations-types-0.5.2.tgz#4d4949e0e83ced7f36cbee009355cd305a36e158"
+ integrity sha512-que84TqGRZJpJKHBlF2pkvc1YcXrtEDOVGiDjovP/a3s6W4nlbohGXEsBJo0JCeeg/UG9A+DEZVDUV9GpklUzA==
+
+"@firebase/installations@0.6.8":
+ version "0.6.8"
+ resolved "https://registry.yarnpkg.com/@firebase/installations/-/installations-0.6.8.tgz#f9c9d493bce04b04ca28814e926ef3ed71f033d6"
+ integrity sha512-57V374qdb2+wT5v7+ntpLXBjZkO6WRgmAUbVkRfFTM/4t980p0FesbqTAcOIiM8U866UeuuuF8lYH70D3jM/jQ==
+ dependencies:
+ "@firebase/component" "0.6.8"
+ "@firebase/util" "1.9.7"
+ idb "7.1.1"
+ tslib "^2.1.0"
+
+"@firebase/logger@0.4.2":
+ version "0.4.2"
+ resolved "https://registry.yarnpkg.com/@firebase/logger/-/logger-0.4.2.tgz#74dfcfeedee810deb8a7080d5b7eba56aa16ffa2"
+ integrity sha512-Q1VuA5M1Gjqrwom6I6NUU4lQXdo9IAQieXlujeHZWvRt1b7qQ0KwBaNAjgxG27jgF9/mUwsNmO8ptBCGVYhB0A==
+ dependencies:
+ tslib "^2.1.0"
+
+"@firebase/messaging-compat@0.2.10":
+ version "0.2.10"
+ resolved "https://registry.yarnpkg.com/@firebase/messaging-compat/-/messaging-compat-0.2.10.tgz#08711f75e2d517fd209bfbc65b1f754b09b2121c"
+ integrity sha512-FXQm7rcowkDm8kFLduHV35IRYCRo+Ng0PIp/t1+EBuEbyplaKkGjZ932pE+owf/XR+G/60ku2QRBptRGLXZydg==
+ dependencies:
+ "@firebase/component" "0.6.8"
+ "@firebase/messaging" "0.12.10"
+ "@firebase/util" "1.9.7"
+ tslib "^2.1.0"
+
+"@firebase/messaging-interop-types@0.2.2":
+ version "0.2.2"
+ resolved "https://registry.yarnpkg.com/@firebase/messaging-interop-types/-/messaging-interop-types-0.2.2.tgz#81042f7e9739733fa4571d17f6eb6869522754d0"
+ integrity sha512-l68HXbuD2PPzDUOFb3aG+nZj5KA3INcPwlocwLZOzPp9rFM9yeuI9YLl6DQfguTX5eAGxO0doTR+rDLDvQb5tA==
+
+"@firebase/messaging@0.12.10":
+ version "0.12.10"
+ resolved "https://registry.yarnpkg.com/@firebase/messaging/-/messaging-0.12.10.tgz#29909f909b9588d44864732377d88de11f3b3ed3"
+ integrity sha512-fGbxJPKpl2DIKNJGhbk4mYPcM+qE2gl91r6xPoiol/mN88F5Ym6UeRdMVZah+pijh9WxM55alTYwXuW40r1Y2Q==
+ dependencies:
+ "@firebase/component" "0.6.8"
+ "@firebase/installations" "0.6.8"
+ "@firebase/messaging-interop-types" "0.2.2"
+ "@firebase/util" "1.9.7"
+ idb "7.1.1"
+ tslib "^2.1.0"
+
+"@firebase/performance-compat@0.2.8":
+ version "0.2.8"
+ resolved "https://registry.yarnpkg.com/@firebase/performance-compat/-/performance-compat-0.2.8.tgz#d97bab3fd0c147c7f796e9b8f78712bc0b83699c"
+ integrity sha512-o7TFClRVJd3VIBoY7KZQqtCeW0PC6v9uBzM6Lfw3Nc9D7hM6OonqecYvh7NwJ6R14k+xM27frLS4BcCvFHKw2A==
+ dependencies:
+ "@firebase/component" "0.6.8"
+ "@firebase/logger" "0.4.2"
+ "@firebase/performance" "0.6.8"
+ "@firebase/performance-types" "0.2.2"
+ "@firebase/util" "1.9.7"
+ tslib "^2.1.0"
+
+"@firebase/performance-types@0.2.2":
+ version "0.2.2"
+ resolved "https://registry.yarnpkg.com/@firebase/performance-types/-/performance-types-0.2.2.tgz#7b78cd2ab2310bac89a63348d93e67e01eb06dd7"
+ integrity sha512-gVq0/lAClVH5STrIdKnHnCo2UcPLjJlDUoEB/tB4KM+hAeHUxWKnpT0nemUPvxZ5nbdY/pybeyMe8Cs29gEcHA==
+
+"@firebase/performance@0.6.8":
+ version "0.6.8"
+ resolved "https://registry.yarnpkg.com/@firebase/performance/-/performance-0.6.8.tgz#668b0fc207389f7829fd3bfb6614fe819b7db124"
+ integrity sha512-F+alziiIZ6Yn8FG47mxwljq+4XkgkT2uJIFRlkyViUQRLzrogaUJW6u/+6ZrePXnouKlKIwzqos3PVJraPEcCA==
+ dependencies:
+ "@firebase/component" "0.6.8"
+ "@firebase/installations" "0.6.8"
+ "@firebase/logger" "0.4.2"
+ "@firebase/util" "1.9.7"
+ tslib "^2.1.0"
+
+"@firebase/remote-config-compat@0.2.8":
+ version "0.2.8"
+ resolved "https://registry.yarnpkg.com/@firebase/remote-config-compat/-/remote-config-compat-0.2.8.tgz#a6df065c1fd0a943e84ee0e76acfc6c1bede42f9"
+ integrity sha512-UxSFOp6dzFj2AHB8Bq/BYtbq5iFyizKx4Rd6WxAdaKYM8cnPMeK+l2v+Oogtjae+AeyHRI+MfL2acsfVe5cd2A==
+ dependencies:
+ "@firebase/component" "0.6.8"
+ "@firebase/logger" "0.4.2"
+ "@firebase/remote-config" "0.4.8"
+ "@firebase/remote-config-types" "0.3.2"
+ "@firebase/util" "1.9.7"
+ tslib "^2.1.0"
+
+"@firebase/remote-config-types@0.3.2":
+ version "0.3.2"
+ resolved "https://registry.yarnpkg.com/@firebase/remote-config-types/-/remote-config-types-0.3.2.tgz#a5d1009c6fd08036c5cd4f28764e3cd694f966d5"
+ integrity sha512-0BC4+Ud7y2aPTyhXJTMTFfrGGLqdYXrUB9sJVAB8NiqJswDTc4/2qrE/yfUbnQJhbSi6ZaTTBKyG3n1nplssaA==
+
+"@firebase/remote-config@0.4.8":
+ version "0.4.8"
+ resolved "https://registry.yarnpkg.com/@firebase/remote-config/-/remote-config-0.4.8.tgz#b6a79acdf73554e0ee31c278162b85592fc8c1f3"
+ integrity sha512-AMLqe6wfIRnjc6FkCWOSUjhc1fSTEf8o+cv1NolFvbiJ/tU+TqN4pI7pT+MIKQzNiq5fxLehkOx+xtAQBxPJKQ==
+ dependencies:
+ "@firebase/component" "0.6.8"
+ "@firebase/installations" "0.6.8"
+ "@firebase/logger" "0.4.2"
+ "@firebase/util" "1.9.7"
+ tslib "^2.1.0"
+
+"@firebase/storage-compat@0.3.11":
+ version "0.3.11"
+ resolved "https://registry.yarnpkg.com/@firebase/storage-compat/-/storage-compat-0.3.11.tgz#94ac4cf4c147dc9388744227740c9c97696cc837"
+ integrity sha512-EEa9jgm/aRVIGSD0ByYAsZ0tvEKfVwSp9uFoa/97BISGWGjSNPIWjenaDvpDZ7aL8OxaGIpwuk700aHy7/T0Ug==
+ dependencies:
+ "@firebase/component" "0.6.8"
+ "@firebase/storage" "0.13.1"
+ "@firebase/storage-types" "0.8.2"
+ "@firebase/util" "1.9.7"
+ tslib "^2.1.0"
+
+"@firebase/storage-types@0.8.2":
+ version "0.8.2"
+ resolved "https://registry.yarnpkg.com/@firebase/storage-types/-/storage-types-0.8.2.tgz#edb321b8a3872a9f74e1f27de046f160021c8e1f"
+ integrity sha512-0vWu99rdey0g53lA7IShoA2Lol1jfnPovzLDUBuon65K7uKG9G+L5uO05brD9pMw+l4HRFw23ah3GwTGpEav6g==
+
+"@firebase/storage@0.13.1":
+ version "0.13.1"
+ resolved "https://registry.yarnpkg.com/@firebase/storage/-/storage-0.13.1.tgz#48e81a5b706a01d3fae6f1adeeec171ede2edee6"
+ integrity sha512-L6AJ5tWgHSi2g/gbc/2Pbm3qxmoEg9THmPIOpRsLwuz9LPeWbhyMQeGlqxWqtZGQO/z/LMjGYadNlupQj0HNfw==
+ dependencies:
+ "@firebase/component" "0.6.8"
+ "@firebase/util" "1.9.7"
+ tslib "^2.1.0"
+ undici "6.19.7"
+
+"@firebase/util@1.9.7":
+ version "1.9.7"
+ resolved "https://registry.yarnpkg.com/@firebase/util/-/util-1.9.7.tgz#c03b0ae065b3bba22800da0bd5314ef030848038"
+ integrity sha512-fBVNH/8bRbYjqlbIhZ+lBtdAAS4WqZumx03K06/u7fJSpz1TGjEMm1ImvKD47w+xaFKIP2ori6z8BrbakRfjJA==
+ dependencies:
+ tslib "^2.1.0"
+
+"@firebase/vertexai-preview@0.0.3":
+ version "0.0.3"
+ resolved "https://registry.yarnpkg.com/@firebase/vertexai-preview/-/vertexai-preview-0.0.3.tgz#73dea839439ebdbb5ccd946f297ede5b57e6e7e9"
+ integrity sha512-KVtUWLp+ScgiwkDKAvNkVucAyhLVQp6C6lhnVEuIg4mWhWcS3oerjAeVhZT4uNofKwWxRsOaB2Yec7DMTXlQPQ==
+ dependencies:
+ "@firebase/app-check-interop-types" "0.3.2"
+ "@firebase/component" "0.6.8"
+ "@firebase/logger" "0.4.2"
+ "@firebase/util" "1.9.7"
+ tslib "^2.1.0"
+
+"@firebase/webchannel-wrapper@1.0.1":
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/@firebase/webchannel-wrapper/-/webchannel-wrapper-1.0.1.tgz#0b62c9f47f557a5b4adc073bb0a47542ce6af4c4"
+ integrity sha512-jmEnr/pk0yVkA7mIlHNnxCi+wWzOFUg0WyIotgkKAb2u1J7fAeDBcVNSTjTihbAYNusCLQdW5s9IJ5qwnEufcQ==
+
"@gar/promisify@^1.0.1", "@gar/promisify@^1.1.3":
version "1.1.3"
resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6"
integrity sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==
+"@grpc/grpc-js@~1.9.0":
+ version "1.9.15"
+ resolved "https://registry.yarnpkg.com/@grpc/grpc-js/-/grpc-js-1.9.15.tgz#433d7ac19b1754af690ea650ab72190bd700739b"
+ integrity sha512-nqE7Hc0AzI+euzUwDAy0aY5hCp10r734gMGRdU+qOPX0XSceI2ULrcXB5U2xSc5VkWwalCj4M7GzCAygZl2KoQ==
+ dependencies:
+ "@grpc/proto-loader" "^0.7.8"
+ "@types/node" ">=12.12.47"
+
+"@grpc/proto-loader@^0.7.8":
+ version "0.7.13"
+ resolved "https://registry.yarnpkg.com/@grpc/proto-loader/-/proto-loader-0.7.13.tgz#f6a44b2b7c9f7b609f5748c6eac2d420e37670cf"
+ integrity sha512-AiXO/bfe9bmxBjxxtYxFAXGZvMaN5s8kO+jBHAJCON8rJoB5YS/D6X7ZNc6XQkuHNmyl4CYaMI1fJ/Gn27RGGw==
+ dependencies:
+ lodash.camelcase "^4.3.0"
+ long "^5.0.0"
+ protobufjs "^7.2.5"
+ yargs "^17.7.2"
+
"@humanwhocodes/config-array@^0.5.0":
version "0.5.0"
resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.5.0.tgz#1407967d4c6eecd7388f83acf1eaf4d0c6e58ef9"
@@ -886,6 +1288,59 @@
resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.25.tgz#f077fdc0b5d0078d30893396ff4827a13f99e817"
integrity sha512-j7P6Rgr3mmtdkeDGTe0E/aYyWEWVtc5yFXtHCRHs28/jptDEWfaVOc5T7cblqy1XKPPfCxJc/8DwQ5YgLOZOVQ==
+"@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2":
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf"
+ integrity sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==
+
+"@protobufjs/base64@^1.1.2":
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/@protobufjs/base64/-/base64-1.1.2.tgz#4c85730e59b9a1f1f349047dbf24296034bb2735"
+ integrity sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==
+
+"@protobufjs/codegen@^2.0.4":
+ version "2.0.4"
+ resolved "https://registry.yarnpkg.com/@protobufjs/codegen/-/codegen-2.0.4.tgz#7ef37f0d010fb028ad1ad59722e506d9262815cb"
+ integrity sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==
+
+"@protobufjs/eventemitter@^1.1.0":
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz#355cbc98bafad5978f9ed095f397621f1d066b70"
+ integrity sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==
+
+"@protobufjs/fetch@^1.1.0":
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/@protobufjs/fetch/-/fetch-1.1.0.tgz#ba99fb598614af65700c1619ff06d454b0d84c45"
+ integrity sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==
+ dependencies:
+ "@protobufjs/aspromise" "^1.1.1"
+ "@protobufjs/inquire" "^1.1.0"
+
+"@protobufjs/float@^1.0.2":
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/@protobufjs/float/-/float-1.0.2.tgz#5e9e1abdcb73fc0a7cb8b291df78c8cbd97b87d1"
+ integrity sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==
+
+"@protobufjs/inquire@^1.1.0":
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/@protobufjs/inquire/-/inquire-1.1.0.tgz#ff200e3e7cf2429e2dcafc1140828e8cc638f089"
+ integrity sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==
+
+"@protobufjs/path@^1.1.2":
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/@protobufjs/path/-/path-1.1.2.tgz#6cc2b20c5c9ad6ad0dccfd21ca7673d8d7fbf68d"
+ integrity sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==
+
+"@protobufjs/pool@^1.1.0":
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/@protobufjs/pool/-/pool-1.1.0.tgz#09fd15f2d6d3abfa9b65bc366506d6ad7846ff54"
+ integrity sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==
+
+"@protobufjs/utf8@^1.1.0":
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570"
+ integrity sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==
+
"@rollup/rollup-android-arm-eabi@4.14.1":
version "4.14.1"
resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.14.1.tgz#ca0501dd836894216cb9572848c5dde4bfca3bec"
@@ -1017,6 +1472,13 @@
resolved "https://registry.yarnpkg.com/@types/ms/-/ms-0.7.34.tgz#10964ba0dee6ac4cd462e2795b6bebd407303433"
integrity sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==
+"@types/node@>=12.12.47", "@types/node@>=13.7.0":
+ version "22.5.5"
+ resolved "https://registry.yarnpkg.com/@types/node/-/node-22.5.5.tgz#52f939dd0f65fc552a4ad0b392f3c466cc5d7a44"
+ integrity sha512-Xjs4y5UPO/CLdzpgR6GirZJx36yScjh73+2NlLlkFRSoQN8B0DpfXPdZGnvVmLRLOsqDpOfTNv7D9trgGhmOIA==
+ dependencies:
+ undici-types "~6.19.2"
+
"@types/normalize-package-data@^2.4.0":
version "2.4.4"
resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz#56e2cc26c397c038fab0e3a917a12d5c5909e901"
@@ -2069,6 +2531,11 @@ doctrine@^3.0.0:
dependencies:
esutils "^2.0.2"
+dompurify@^3.1.7:
+ version "3.1.7"
+ resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-3.1.7.tgz#711a8c96479fb6ced93453732c160c3c72418a6a"
+ integrity sha512-VaTstWtsneJY8xzy7DekmYWEOZcmzIe3Qb3zPd4STve1OBTa+e+WmS1ITQec1fZYXI3HCsOZZiSMpG6oxoWMWQ==
+
dot-prop@^5.2.0:
version "5.3.0"
resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-5.3.0.tgz#90ccce708cd9cd82cc4dc8c3ddd9abdd55b20e88"
@@ -2481,6 +2948,13 @@ fastq@^1.6.0:
dependencies:
reusify "^1.0.4"
+faye-websocket@0.11.4:
+ version "0.11.4"
+ resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.11.4.tgz#7f0d9275cfdd86a1c963dc8b65fcc451edcbb1da"
+ integrity sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==
+ dependencies:
+ websocket-driver ">=0.5.1"
+
fflate@^0.8.2:
version "0.8.2"
resolved "https://registry.yarnpkg.com/fflate/-/fflate-0.8.2.tgz#fc8631f5347812ad6028bbe4a2308b2792aa1dea"
@@ -2515,6 +2989,39 @@ find-up@^4.1.0:
locate-path "^5.0.0"
path-exists "^4.0.0"
+firebase@^10.13.1:
+ version "10.13.1"
+ resolved "https://registry.yarnpkg.com/firebase/-/firebase-10.13.1.tgz#c26d9e8d19cbeea6e6585c46a903a4a1175a3938"
+ integrity sha512-L5BSkmvB2dzCUMpr8i/O8WMJC3Nqj5Ld8Wj/qnak+tz2Ga+JH6/FO93xArg9IGhktCrPXVODoWp6t9ybdgmXCA==
+ dependencies:
+ "@firebase/analytics" "0.10.7"
+ "@firebase/analytics-compat" "0.2.13"
+ "@firebase/app" "0.10.10"
+ "@firebase/app-check" "0.8.7"
+ "@firebase/app-check-compat" "0.3.14"
+ "@firebase/app-compat" "0.2.40"
+ "@firebase/app-types" "0.9.2"
+ "@firebase/auth" "1.7.8"
+ "@firebase/auth-compat" "0.5.13"
+ "@firebase/database" "1.0.7"
+ "@firebase/database-compat" "1.0.7"
+ "@firebase/firestore" "4.7.1"
+ "@firebase/firestore-compat" "0.3.36"
+ "@firebase/functions" "0.11.7"
+ "@firebase/functions-compat" "0.3.13"
+ "@firebase/installations" "0.6.8"
+ "@firebase/installations-compat" "0.2.8"
+ "@firebase/messaging" "0.12.10"
+ "@firebase/messaging-compat" "0.2.10"
+ "@firebase/performance" "0.6.8"
+ "@firebase/performance-compat" "0.2.8"
+ "@firebase/remote-config" "0.4.8"
+ "@firebase/remote-config-compat" "0.2.8"
+ "@firebase/storage" "0.13.1"
+ "@firebase/storage-compat" "0.3.11"
+ "@firebase/util" "1.9.7"
+ "@firebase/vertexai-preview" "0.0.3"
+
flat-cache@^3.0.4:
version "3.2.0"
resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.2.0.tgz#2c0c2d5040c99b1632771a9d105725c0115363ee"
@@ -2823,6 +3330,11 @@ http-cache-semantics@^4.0.0, http-cache-semantics@^4.1.0:
resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a"
integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==
+http-parser-js@>=0.5.1:
+ version "0.5.8"
+ resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.5.8.tgz#af23090d9ac4e24573de6f6aecc9d84a48bf20e3"
+ integrity sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==
+
http-proxy-agent@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz#8a8c8ef7f5932ccf953c296ca8291b95aa74aa3a"
@@ -2884,6 +3396,11 @@ iconv-lite@0.6.3, iconv-lite@^0.6.2:
dependencies:
safer-buffer ">= 2.1.2 < 3.0.0"
+idb@7.1.1:
+ version "7.1.1"
+ resolved "https://registry.yarnpkg.com/idb/-/idb-7.1.1.tgz#d910ded866d32c7ced9befc5bfdf36f572ced72b"
+ integrity sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==
+
ignore@^4.0.6:
version "4.0.6"
resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc"
@@ -3230,6 +3747,11 @@ locate-path@^5.0.0:
dependencies:
p-locate "^4.1.0"
+lodash.camelcase@^4.3.0:
+ version "4.3.0"
+ resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6"
+ integrity sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==
+
lodash.merge@^4.6.2:
version "4.6.2"
resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
@@ -3245,6 +3767,11 @@ lodash@^4.17.11, lodash@^4.17.15, lodash@^4.17.21:
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
+long@^5.0.0:
+ version "5.2.3"
+ resolved "https://registry.yarnpkg.com/long/-/long-5.2.3.tgz#a3ba97f3877cf1d778eccbcb048525ebb77499e1"
+ integrity sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==
+
longest-streak@^3.0.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/longest-streak/-/longest-streak-3.1.0.tgz#62fa67cd958742a1574af9f39866364102d90cd4"
@@ -3387,6 +3914,11 @@ markdown-table@^3.0.0:
resolved "https://registry.yarnpkg.com/markdown-table/-/markdown-table-3.0.3.tgz#e6331d30e493127e031dd385488b5bd326e4a6bd"
integrity sha512-Z1NL3Tb1M9wH4XESsCDEksWoKTdlUafKc4pt0GRwjUyXaCFZ+dc3g2erqB6zm3szA2IUSi7VnPI+o/9jnxh9hw==
+marked@^14.1.2:
+ version "14.1.2"
+ resolved "https://registry.yarnpkg.com/marked/-/marked-14.1.2.tgz#3cbc26b2d6832be32b75ae0746e0968c781b6156"
+ integrity sha512-f3r0yqpz31VXiDB/wj9GaOB0a2PRLQl6vJmXiFrniNwjkKdvakqJRULhjFKJpxOchlCRiG5fcacoUZY5Xa6PEQ==
+
mdast-util-find-and-replace@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.1.tgz#a6fc7b62f0994e973490e45262e4bc07607b04e0"
@@ -4395,6 +4927,24 @@ proto-list@~1.2.1:
resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849"
integrity sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==
+protobufjs@^7.2.5:
+ version "7.4.0"
+ resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-7.4.0.tgz#7efe324ce9b3b61c82aae5de810d287bc08a248a"
+ integrity sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw==
+ dependencies:
+ "@protobufjs/aspromise" "^1.1.2"
+ "@protobufjs/base64" "^1.1.2"
+ "@protobufjs/codegen" "^2.0.4"
+ "@protobufjs/eventemitter" "^1.1.0"
+ "@protobufjs/fetch" "^1.1.0"
+ "@protobufjs/float" "^1.0.2"
+ "@protobufjs/inquire" "^1.1.0"
+ "@protobufjs/path" "^1.1.2"
+ "@protobufjs/pool" "^1.1.0"
+ "@protobufjs/utf8" "^1.1.0"
+ "@types/node" ">=13.7.0"
+ long "^5.0.0"
+
proxy-from-env@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2"
@@ -4619,16 +5169,16 @@ run-parallel@^1.1.9:
dependencies:
queue-microtask "^1.2.2"
+safe-buffer@>=5.1.0, safe-buffer@~5.2.0:
+ version "5.2.1"
+ resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
+ integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
+
safe-buffer@~5.1.0, safe-buffer@~5.1.1:
version "5.1.2"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
-safe-buffer@~5.2.0:
- version "5.2.1"
- resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
- integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
-
"safer-buffer@>= 2.1.2 < 3.0.0":
version "2.1.2"
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
@@ -5104,6 +5654,11 @@ ts-api-utils@^1.3.0:
resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.3.0.tgz#4b490e27129f1e8e686b45cc4ab63714dc60eea1"
integrity sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==
+tslib@^2.1.0:
+ version "2.7.0"
+ resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.7.0.tgz#d9b40c5c40ab59e8738f297df3087bf1a2690c01"
+ integrity sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==
+
tslib@^2.6.2:
version "2.6.2"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae"
@@ -5148,6 +5703,16 @@ typescript@^5.4.5:
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.4.5.tgz#42ccef2c571fdbd0f6718b1d1f5e6e5ef006f611"
integrity sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==
+undici-types@~6.19.2:
+ version "6.19.8"
+ resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.19.8.tgz#35111c9d1437ab83a7cdc0abae2f26d88eda0a02"
+ integrity sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==
+
+undici@6.19.7:
+ version "6.19.7"
+ resolved "https://registry.yarnpkg.com/undici/-/undici-6.19.7.tgz#7d4cf26dc689838aa8b6753a3c5c4288fc1e0216"
+ integrity sha512-HR3W/bMGPSr90i8AAp2C4DM3wChFdJPLrWYpIS++LxS8K+W535qftjt+4MyjNYHeWabMj1nvtmLIi7l++iq91A==
+
unified-translations@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/unified-translations/-/unified-translations-1.1.1.tgz#df48f84c9a0c0c25665b480e733d0ec59247317a"
@@ -5455,6 +6020,20 @@ webidl-conversions@^7.0.0:
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-7.0.0.tgz#256b4e1882be7debbf01d05f0aa2039778ea080a"
integrity sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==
+websocket-driver@>=0.5.1:
+ version "0.7.4"
+ resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.4.tgz#89ad5295bbf64b480abcba31e4953aca706f5760"
+ integrity sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==
+ dependencies:
+ http-parser-js ">=0.5.1"
+ safe-buffer ">=5.1.0"
+ websocket-extensions ">=0.1.1"
+
+websocket-extensions@>=0.1.1:
+ version "0.1.4"
+ resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.4.tgz#7f8473bc839dfd87608adb95d7eb075211578a42"
+ integrity sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==
+
whatwg-encoding@^3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz#d0f4ef769905d426e1688f3e34381a99b60b76e5"
@@ -5597,7 +6176,7 @@ yargs-parser@^21.1.1:
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35"
integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==
-yargs@^17.2.1:
+yargs@^17.2.1, yargs@^17.7.2:
version "17.7.2"
resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269"
integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==