diff --git a/ui/src/components/NavBar/NavBar.tests.ts b/ui/src/components/NavBar/NavBar.tests.ts
deleted file mode 100644
index e69de29b..00000000
diff --git a/ui/src/components/NavBar/NavBar.tsx b/ui/src/components/NavBar/NavBar.tsx
deleted file mode 100644
index be8f8b9a..00000000
--- a/ui/src/components/NavBar/NavBar.tsx
+++ /dev/null
@@ -1,10 +0,0 @@
-import './NavBar.css';
-import { ReposList } from '@/components/Header/ReposList';
-
-export const NavBar = () => (
-
-);
diff --git a/ui/src/components/OAuthCallback/OAuthCallback.test.tsx b/ui/src/components/OAuthCallback/OAuthCallback.test.tsx
new file mode 100644
index 00000000..7d63d76c
--- /dev/null
+++ b/ui/src/components/OAuthCallback/OAuthCallback.test.tsx
@@ -0,0 +1,57 @@
+import { describe } from 'vitest';
+import { render, screen } from '@test-utils';
+import { waitFor } from '@testing-library/react';
+import { useNavigate } from 'react-router-dom';
+import { useOAuth } from '@/api';
+import { OAuthCallback } from '@/components';
+import { useAuthStore } from '@/store';
+
+describe('OAuthCallback', () => {
+ test('displays loading message while fetching data', () => {
+ //@ts-ignore
+ useOAuth.mockReturnValue({ data: null, isLoading: true, isError: false });
+ render(
);
+ expect(screen.getByText('Loading...')).toBeInTheDocument();
+ });
+
+ test('sets token and navigates upon receving data', async () => {
+ const mockSetToken = vi.fn();
+ const mockNavigate = vi.fn();
+ //@ts-ignore
+ useOAuth.mockReturnValue({
+ data: { access_token: 'token' },
+ isLoading: false,
+ isError: false,
+ });
+ // @ts-ignore
+ useAuthStore.mockReturnValue({ setToken: mockSetToken });
+ // @ts-ignore
+ useNavigate.mockReturnValue(mockNavigate);
+
+ render(
);
+ await waitFor(
+ () => {
+ expect(mockSetToken).toHaveBeenCalledWith('token');
+ expect(mockNavigate).toHaveBeenCalledWith('/');
+ },
+ {
+ timeout: 1000,
+ }
+ );
+ });
+
+ test('displays error message if error occurs', () => {
+ const mockNavigate = vi.fn();
+ //@ts-ignore
+ useOAuth.mockReturnValue({
+ data: null,
+ isLoading: false,
+ isError: true,
+ error: { message: 'error' },
+ });
+ //@ts-ignore
+ useNavigate.mockReturnValue(mockNavigate);
+ render(
);
+ expect(mockNavigate).toHaveBeenCalledWith('/login');
+ });
+});
diff --git a/ui/src/components/OAuthCallback/OAuthCallback.tsx b/ui/src/components/OAuthCallback/OAuthCallback.tsx
new file mode 100644
index 00000000..2baff539
--- /dev/null
+++ b/ui/src/components/OAuthCallback/OAuthCallback.tsx
@@ -0,0 +1,34 @@
+import { FC, useEffect } from 'react';
+import { useNavigate } from 'react-router-dom';
+import { notifications } from '@mantine/notifications';
+import { useOAuth } from '@/api';
+import { useAuthStore } from '@/store';
+import { Loading } from '@/components';
+
+export const OAuthCallback: FC = () => {
+ const code = new URLSearchParams(window.location.search).get('code');
+ const { data, isLoading, isError, error } = useOAuth({ code });
+ const navigate = useNavigate();
+ const { setToken } = useAuthStore();
+
+ useEffect(() => {
+ if (data) {
+ setToken(data.access_token);
+ navigate('/');
+ }
+ }, [data, setToken, navigate]);
+
+ if (isLoading) {
+ return
;
+ }
+
+ if (isError) {
+ notifications.show({
+ title: 'Error while authenticating, redirecting to login page',
+ message: error.message,
+ });
+ navigate('/login');
+ }
+
+ return null;
+};
diff --git a/ui/src/components/OAuthCallback/index.ts b/ui/src/components/OAuthCallback/index.ts
new file mode 100644
index 00000000..2b614843
--- /dev/null
+++ b/ui/src/components/OAuthCallback/index.ts
@@ -0,0 +1 @@
+export { OAuthCallback } from './OAuthCallback';
diff --git a/ui/src/components/Header/UserMenu/UserMenu.css b/ui/src/components/UserMenu/UserMenu.css
similarity index 100%
rename from ui/src/components/Header/UserMenu/UserMenu.css
rename to ui/src/components/UserMenu/UserMenu.css
diff --git a/ui/src/components/Header/UserMenu/UserMenu.test.tsx b/ui/src/components/UserMenu/UserMenu.test.tsx
similarity index 89%
rename from ui/src/components/Header/UserMenu/UserMenu.test.tsx
rename to ui/src/components/UserMenu/UserMenu.test.tsx
index 0bdb1480..8f66d86e 100644
--- a/ui/src/components/Header/UserMenu/UserMenu.test.tsx
+++ b/ui/src/components/UserMenu/UserMenu.test.tsx
@@ -1,14 +1,9 @@
//
-import { describe, it, expect, vi } from 'vitest';
+import { describe, it, expect } from 'vitest';
import { render, screen } from '@test-utils';
import { UserMenu } from './UserMenu';
import { useUser } from '@/api';
-// Mock the API hook and store
-vi.mock('@/api', () => ({
- useUser: vi.fn(),
-}));
-
describe('UserMenu', () => {
it('renders loading state correctly', () => {
// @ts-ignore
diff --git a/ui/src/components/Header/UserMenu/UserMenu.tsx b/ui/src/components/UserMenu/UserMenu.tsx
similarity index 93%
rename from ui/src/components/Header/UserMenu/UserMenu.tsx
rename to ui/src/components/UserMenu/UserMenu.tsx
index e4e0056b..c1471cb3 100644
--- a/ui/src/components/Header/UserMenu/UserMenu.tsx
+++ b/ui/src/components/UserMenu/UserMenu.tsx
@@ -3,14 +3,10 @@ import { IconAlertCircle, IconChevronDown, IconSettings } from '@tabler/icons-re
import './UserMenu.css';
import { useState } from 'react';
import { useUser } from '@/api';
-import { useAuthStore } from '@/store';
export const UserMenu = () => {
const { data, error, isLoading } = useUser();
const [userMenuOpened, setUserMenuOpened] = useState(false);
- const { openModal } = useAuthStore((state) => ({
- openModal: state.openModal,
- }));
if (isLoading) {
return
;
@@ -44,7 +40,6 @@ export const UserMenu = () => {
Settings
}
- onClick={openModal}
>
Change Token
diff --git a/ui/src/components/Header/UserMenu/index.ts b/ui/src/components/UserMenu/index.ts
similarity index 100%
rename from ui/src/components/Header/UserMenu/index.ts
rename to ui/src/components/UserMenu/index.ts
diff --git a/ui/src/components/index.ts b/ui/src/components/index.ts
index 89c715e9..f4a1ee49 100644
--- a/ui/src/components/index.ts
+++ b/ui/src/components/index.ts
@@ -1,2 +1,4 @@
-export { Header } from './Header';
+export { Header } from '../layouts/Header';
export { Loading } from './Loading';
+export { OAuthCallback } from './OAuthCallback';
+export { UserMenu } from './UserMenu';
diff --git a/ui/src/layouts/AppLayout.tsx b/ui/src/layouts/AppLayout.tsx
index a89b0dc2..9d0aef93 100644
--- a/ui/src/layouts/AppLayout.tsx
+++ b/ui/src/layouts/AppLayout.tsx
@@ -1,25 +1,15 @@
import { AppShell } from '@mantine/core';
import React from 'react';
-import { Header } from '@/components';
-import { NavBar } from '@/components/NavBar';
+import { NavBar } from './NavBar';
+import { Header } from './Header';
export const AppLayout = () => (
- {/*TODO: data-testid is not retained after the component is mounted. Need to investigate this*/}
-
- {/**/}
- {/* Navbar*/}
- {/* {Array(15)*/}
- {/* .fill(0)*/}
- {/* .map((_, index) => (*/}
- {/* */}
- {/* ))}*/}
- {/**/}
Main
);
diff --git a/ui/src/components/Header/Header.css b/ui/src/layouts/Header/Header.css
similarity index 100%
rename from ui/src/components/Header/Header.css
rename to ui/src/layouts/Header/Header.css
diff --git a/ui/src/components/Header/Header.test.tsx b/ui/src/layouts/Header/Header.test.tsx
similarity index 100%
rename from ui/src/components/Header/Header.test.tsx
rename to ui/src/layouts/Header/Header.test.tsx
diff --git a/ui/src/components/Header/Header.tsx b/ui/src/layouts/Header/Header.tsx
similarity index 89%
rename from ui/src/components/Header/Header.tsx
rename to ui/src/layouts/Header/Header.tsx
index 8e259dbb..e5b24201 100644
--- a/ui/src/components/Header/Header.tsx
+++ b/ui/src/layouts/Header/Header.tsx
@@ -1,7 +1,7 @@
import React, { FC } from 'react';
import { Container, Group } from '@mantine/core';
-import { UserMenu } from './UserMenu';
+import { UserMenu } from '@/components';
import './Header.css';
export const Header: FC = () => (
diff --git a/ui/src/components/Header/index.ts b/ui/src/layouts/Header/index.ts
similarity index 100%
rename from ui/src/components/Header/index.ts
rename to ui/src/layouts/Header/index.ts
diff --git a/ui/src/components/NavBar/NavBar.css b/ui/src/layouts/NavBar/NavBar.css
similarity index 100%
rename from ui/src/components/NavBar/NavBar.css
rename to ui/src/layouts/NavBar/NavBar.css
diff --git a/ui/src/layouts/NavBar/NavBar.tsx b/ui/src/layouts/NavBar/NavBar.tsx
new file mode 100644
index 00000000..a7a977a8
--- /dev/null
+++ b/ui/src/layouts/NavBar/NavBar.tsx
@@ -0,0 +1,3 @@
+import './NavBar.css';
+
+export const NavBar = () =>
;
diff --git a/ui/src/components/NavBar/index.ts b/ui/src/layouts/NavBar/index.ts
similarity index 100%
rename from ui/src/components/NavBar/index.ts
rename to ui/src/layouts/NavBar/index.ts
diff --git a/ui/src/pages/Login/Login.css b/ui/src/pages/Login/Login.css
new file mode 100644
index 00000000..4b162552
--- /dev/null
+++ b/ui/src/pages/Login/Login.css
@@ -0,0 +1,11 @@
+.login-page {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ height: 100vh;
+}
+
+.login-title {
+ margin-bottom: 20px;
+}
diff --git a/ui/src/pages/Login/Login.test.tsx b/ui/src/pages/Login/Login.test.tsx
new file mode 100644
index 00000000..e09dcb31
--- /dev/null
+++ b/ui/src/pages/Login/Login.test.tsx
@@ -0,0 +1,42 @@
+import React from 'react';
+import { render, screen, waitFor, userEvent } from '@test-utils';
+import { vi } from 'vitest';
+import { Login } from '@/pages';
+import { githubLogin } from '@/api';
+
+describe('Login Component', () => {
+ beforeEach(() => {
+ // Clear all mocks before each test
+ vi.clearAllMocks();
+ });
+
+ it('renders correctly', () => {
+ render(
);
+ expect(screen.getByText('Spark Expectations')).toBeInTheDocument();
+ expect(
+ screen.getByText('Please login using one of the following providers:')
+ ).toBeInTheDocument();
+ expect(screen.getByRole('button', { name: 'Login with GitHub' })).toBeInTheDocument();
+ });
+
+ it('calls githubLogin on button click', async () => {
+ render(
);
+ const loginButton = screen.getByRole('button', { name: 'Login with GitHub' });
+ userEvent.click(loginButton);
+ await waitFor(() => expect(githubLogin).toHaveBeenCalled());
+ });
+
+ it('handles the login process correctly', async () => {
+ // Assume githubLogin is a promise that resolves to an access token
+ (githubLogin as jest.Mock).mockResolvedValue({ access_token: 'fake-token' });
+
+ render(
);
+ const loginButton = screen.getByRole('button', { name: 'Login with GitHub' });
+ userEvent.click(loginButton);
+
+ await waitFor(() => {
+ // You can add additional assertions here to check for changes in the UI or redirects
+ expect(githubLogin).toHaveBeenCalled();
+ });
+ });
+});
diff --git a/ui/src/pages/Login/Login.tsx b/ui/src/pages/Login/Login.tsx
new file mode 100644
index 00000000..92cfaee6
--- /dev/null
+++ b/ui/src/pages/Login/Login.tsx
@@ -0,0 +1,26 @@
+import { FC } from 'react';
+import { Button, Container, Paper, Text, Title } from '@mantine/core';
+
+import { githubLogin } from '@/api';
+
+export const Login: FC = () => {
+ const loginWithGithub = () => {
+ githubLogin();
+ };
+ return (
+ <>
+
+ Spark Expectations
+
+ Please login using one of the following providers:
+
+
+
+
+
+
+ >
+ );
+};
diff --git a/ui/src/pages/Login/index.ts b/ui/src/pages/Login/index.ts
new file mode 100644
index 00000000..a10c3a83
--- /dev/null
+++ b/ui/src/pages/Login/index.ts
@@ -0,0 +1 @@
+export * from './Login';
diff --git a/ui/src/pages/index.ts b/ui/src/pages/index.ts
new file mode 100644
index 00000000..a10c3a83
--- /dev/null
+++ b/ui/src/pages/index.ts
@@ -0,0 +1 @@
+export * from './Login';
diff --git a/ui/src/providers/app-provider.tsx b/ui/src/providers/app-provider.tsx
index 1610ae6b..82e5975f 100644
--- a/ui/src/providers/app-provider.tsx
+++ b/ui/src/providers/app-provider.tsx
@@ -1,12 +1,14 @@
import React from 'react';
+import { Notifications } from '@mantine/notifications';
import { ReactQueryProvider } from './react-query-provider';
import { CustomMantineProvider } from './mantine-provider';
-import { AuthProvider } from './auth-provider';
+import { RouterProvider } from './router-provider';
-export const AppProvider = ({ children }: { children: React.ReactNode }) => (
+export const AppProvider = () => (
- {children}
+
+
);
diff --git a/ui/src/providers/auth-provider/auth-provider.test.tsx b/ui/src/providers/auth-provider/auth-provider.test.tsx
deleted file mode 100644
index 2475d675..00000000
--- a/ui/src/providers/auth-provider/auth-provider.test.tsx
+++ /dev/null
@@ -1,26 +0,0 @@
-import { render, screen } from '@testing-library/react';
-import { AppProvider } from '@/providers';
-import { useAuthStore } from '@/store';
-
-const TestConsumer = () => {
- const { token, setToken } = useAuthStore();
- return (
-
- Token: {token}
-
-
- );
-};
-
-describe('AuthProvider', () => {
- it('provides the auth context correctly', () => {
- render(
-
-
-
- );
- expect(screen.getByText('Token')).toBeInTheDocument();
- });
-});
diff --git a/ui/src/providers/auth-provider/auth-provider.tsx b/ui/src/providers/auth-provider/auth-provider.tsx
deleted file mode 100644
index 94c8abe5..00000000
--- a/ui/src/providers/auth-provider/auth-provider.tsx
+++ /dev/null
@@ -1,70 +0,0 @@
-import React, { ReactNode, useEffect } from 'react';
-import { Modal, TextInput, Button, Group } from '@mantine/core';
-import { useForm } from '@mantine/form';
-import { useAuthStore } from '@/store';
-import { getUserFn } from '@/api';
-import { Loading } from '@/components/Loading/Loading';
-
-interface AuthProviderProps {
- children: ReactNode;
-}
-
-export const AuthProvider: React.FC
= ({ children }) => {
- const { token, username, isModalOpen, setUserName, setToken, openModal, closeModal } =
- useAuthStore();
-
- const form = useForm({
- initialValues: {
- token,
- },
- validate: {
- token: (value: string | null) => value && value.length === 0 && 'Token is required',
- },
- });
-
- useEffect(() => {
- if (!token) {
- openModal();
- } else {
- closeModal();
- }
- }, [token, openModal, closeModal]);
-
- const handleLogin = (values: { token: null | string }) => {
- setToken(values?.token);
-
- getUserFn().then((res) => {
- setUserName(res.login);
- });
-
- closeModal();
- };
-
- const handleCancel = () => {
- if (token) closeModal();
- };
-
- return (
- <>
-
-
-
- {/*TODO: Handle Error State*/}
- {token && username ? children : }
- >
- );
-};
diff --git a/ui/src/providers/auth-provider/index.ts b/ui/src/providers/auth-provider/index.ts
deleted file mode 100644
index 83ad66de..00000000
--- a/ui/src/providers/auth-provider/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export { AuthProvider } from './auth-provider';
diff --git a/ui/src/providers/index.ts b/ui/src/providers/index.ts
index f01e5ba2..37ac7157 100644
--- a/ui/src/providers/index.ts
+++ b/ui/src/providers/index.ts
@@ -1 +1,4 @@
export { AppProvider } from './app-provider';
+export { CustomMantineProvider } from './mantine-provider';
+export { RouterProvider } from './router-provider';
+export { ReactQueryProvider } from './react-query-provider';
diff --git a/ui/src/providers/mantine-provider.tsx b/ui/src/providers/mantine-provider.tsx
index 98aceae6..1ea3fcbb 100644
--- a/ui/src/providers/mantine-provider.tsx
+++ b/ui/src/providers/mantine-provider.tsx
@@ -1,7 +1,7 @@
-import { createTheme, MantineProvider } from '@mantine/core';
+import { MantineProvider } from '@mantine/core';
import React from 'react';
import { theme } from '@/theme';
export const CustomMantineProvider = ({ children }: { children: React.ReactNode }) => (
- {children}
+ {children}
);
diff --git a/ui/src/providers/react-query-provider.tsx b/ui/src/providers/react-query-provider.tsx
index 35272306..8bcd8aca 100644
--- a/ui/src/providers/react-query-provider.tsx
+++ b/ui/src/providers/react-query-provider.tsx
@@ -1,8 +1,23 @@
import { PropsWithChildren } from 'react';
-import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
+import { QueryCache, QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
+import { notifications } from '@mantine/notifications';
-const queryClient = new QueryClient();
+const queryClient = new QueryClient({
+ defaultOptions: {
+ queries: {
+ retry: 1,
+ },
+ },
+ queryCache: new QueryCache({
+ onError: (error, query) =>
+ notifications.show({
+ title: `${error.name} for ${query.queryKey}`,
+ message: error.message,
+ color: 'red',
+ }),
+ }),
+});
export const ReactQueryProvider = ({ children }: PropsWithChildren) => (
diff --git a/ui/src/providers/router-provider.tsx b/ui/src/providers/router-provider.tsx
new file mode 100644
index 00000000..b0bfaf51
--- /dev/null
+++ b/ui/src/providers/router-provider.tsx
@@ -0,0 +1,4 @@
+import { RouterProvider as ReactRouterProvider } from 'react-router-dom';
+import { router } from '@/routes';
+
+export const RouterProvider = () => ;
diff --git a/ui/src/routes/index.ts b/ui/src/routes/index.ts
new file mode 100644
index 00000000..164ab508
--- /dev/null
+++ b/ui/src/routes/index.ts
@@ -0,0 +1 @@
+export * from './router';
diff --git a/ui/src/routes/protected.tsx b/ui/src/routes/protected.tsx
new file mode 100644
index 00000000..4778e5cd
--- /dev/null
+++ b/ui/src/routes/protected.tsx
@@ -0,0 +1,12 @@
+import { Navigate, Outlet } from 'react-router-dom';
+import { useAuthStore } from '@/store';
+
+export const Protected = () => {
+ const { token } = useAuthStore.getState();
+
+ if (!token) {
+ return ;
+ }
+
+ return ;
+};
diff --git a/ui/src/routes/router.tsx b/ui/src/routes/router.tsx
new file mode 100644
index 00000000..75069b85
--- /dev/null
+++ b/ui/src/routes/router.tsx
@@ -0,0 +1,19 @@
+import { createBrowserRouter, createRoutesFromElements, Route } from 'react-router-dom';
+import { Protected } from '@/routes/protected';
+import { AppLayout } from '@/layouts';
+import { Login } from '@/pages';
+import { OAuthCallback } from '@/components';
+
+export const router = createBrowserRouter(
+ createRoutesFromElements(
+
+ }>
+ } />
+
+ {/*} />*/}
+ } />
+ } />
+ Not Found} />
+
+ )
+);
diff --git a/ui/src/store/auth-store/auth-store.test.ts b/ui/src/store/auth-store/auth-store.test.ts
index 196aeda3..24c53c4b 100644
--- a/ui/src/store/auth-store/auth-store.test.ts
+++ b/ui/src/store/auth-store/auth-store.test.ts
@@ -1,24 +1,9 @@
// tests for src/store/auth-store.ts
-import { act } from 'react-dom/test-utils';
+import { act } from 'react';
import { renderHook } from '@testing-library/react';
import { useAuthStore } from '@/store/auth-store';
describe('useAuthStore', () => {
- it('should toggle modal open and close', () => {
- const { result } = renderHook(() => useAuthStore());
- expect(result.current.isModalOpen).toBe(false);
-
- act(() => {
- result.current.openModal();
- });
- expect(result.current.isModalOpen).toBe(true);
-
- act(() => {
- result.current.closeModal();
- });
- expect(result.current.isModalOpen).toBe(false);
- });
-
it('should set and update token', () => {
const { result } = renderHook(() => useAuthStore());
expect(result.current.token).toBeNull();
diff --git a/ui/src/store/auth-store/auth-store.ts b/ui/src/store/auth-store/auth-store.ts
index 0229fd9e..fbee72a8 100644
--- a/ui/src/store/auth-store/auth-store.ts
+++ b/ui/src/store/auth-store/auth-store.ts
@@ -1,47 +1,32 @@
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
+import { customStorage } from '../custom-store';
interface AuthState {
token: string | null;
- isModalOpen: boolean;
username: string | null;
setToken: (token: string | null) => void;
setUserName: (username: string) => void;
- openModal: () => void;
- closeModal: () => void;
}
-/*
- * Coupling modals with state management.
- * Not sure if it's a good idea to do this.
- *
- * Keep an eye on this approach
- * Potential TODO
- * */
-
export const useAuthStore = create(
persist(
(set: any) => ({
token: null,
- isModalOpen: false,
username: null,
setToken: (token: string | null) => set(() => ({ token })),
setUserName: (username: string) => set(() => ({ username })),
- openModal: () => set(() => ({ isModalOpen: true })),
- closeModal: () => set(() => ({ isModalOpen: false })),
}),
{
name: 'auth',
+ storage: customStorage,
}
)
);
-
+// //
// export const useAuthStore = create((set) => ({
// token: null,
-// isModalOpen: false,
// username: null,
-// setToken: (token: string) => set(() => ({ token })),
+// setToken: (token: string | null) => set(() => ({ token })),
// setUserName: (username: string) => set(() => ({ username })),
-// openModal: () => set(() => ({ isModalOpen: true })),
-// closeModal: () => set(() => ({ isModalOpen: false })),
// }));
diff --git a/ui/src/store/custom-store.ts b/ui/src/store/custom-store.ts
new file mode 100644
index 00000000..deee999c
--- /dev/null
+++ b/ui/src/store/custom-store.ts
@@ -0,0 +1,29 @@
+import { PersistStorage } from 'zustand/middleware';
+
+export const customStorage: PersistStorage = {
+ getItem: (name) => {
+ const item = localStorage.getItem(name);
+ if (!item) return null;
+
+ const parsedItem = JSON.parse(item);
+ const now = new Date().getTime();
+
+ if (parsedItem.expiry && now > parsedItem.expiry) {
+ localStorage.removeItem(name);
+ return null;
+ }
+
+ return parsedItem.value;
+ },
+ setItem: (name, value) => {
+ const now = new Date().getTime();
+ const item = {
+ value,
+ expiry: now + 6 * 60 * 60 * 1000, // 6 hours from now
+ };
+ localStorage.setItem(name, JSON.stringify(item));
+ },
+ removeItem: (name) => {
+ localStorage.removeItem(name);
+ },
+};
diff --git a/ui/src/theme.ts b/ui/src/theme.ts
index 9868cfb8..fbe6de71 100644
--- a/ui/src/theme.ts
+++ b/ui/src/theme.ts
@@ -1,6 +1,43 @@
-import { createTheme } from '@mantine/core';
+import { createTheme, rem } from '@mantine/core';
-// TODO: LOOK INTO LIGHT DARK THEME
export const theme = createTheme({
- /** Put your mantine theme override here */
+ colors: {
+ deepBlue: [
+ '#eef3ff',
+ '#dce4f5',
+ '#b9c7e2',
+ '#94a8d0',
+ '#748dc1',
+ '#5f7cb8',
+ '#5474b4',
+ '#44639f',
+ '#39588f',
+ '#2d4b81',
+ ],
+
+ blue: [
+ '#eef3ff',
+ '#dee2f2',
+ '#bdc2de',
+ '#98a0ca',
+ '#7a84ba',
+ '#6672b0',
+ '#5c68ac',
+ '#4c5897',
+ '#424e88',
+ '#364379',
+ ],
+ },
+
+ shadows: {
+ md: '1px 1px 3px rgba(0, 0, 0, .25)',
+ xl: '5px 5px 3px rgba(0, 0, 0, .25)',
+ },
+
+ headings: {
+ fontFamily: 'Roboto, sans-serif',
+ sizes: {
+ h1: { fontSize: rem(36) },
+ },
+ },
});
diff --git a/ui/test-utils/__mocks__/index.ts b/ui/test-utils/__mocks__/index.ts
new file mode 100644
index 00000000..41db9682
--- /dev/null
+++ b/ui/test-utils/__mocks__/index.ts
@@ -0,0 +1,3 @@
+export * from './user.mock';
+export * from './repo.mock';
+export * from './repos.mock';
diff --git a/ui/test-utils/__mocks__/repo.mock.ts b/ui/test-utils/__mocks__/repo.mock.ts
new file mode 100644
index 00000000..dc4ff8e1
--- /dev/null
+++ b/ui/test-utils/__mocks__/repo.mock.ts
@@ -0,0 +1,56 @@
+import { faker } from '@faker-js/faker';
+
+const createSecurityStatusMock = (): SecurityStatus => ({
+ status: faker.helpers.arrayElement(['enabled', 'disabled']),
+});
+
+const createSecurityAndAnalysisMock = (): SecurityAndAnalysis => ({
+ advanced_security: createSecurityStatusMock(),
+ secret_scanning: createSecurityStatusMock(),
+ secret_scanning_push_protection: createSecurityStatusMock(),
+});
+
+const createPermissionsMock = (): Permissions =>
+ {
+ admin: faker.datatype.boolean(),
+ push: faker.datatype.boolean(),
+ pull: faker.datatype.boolean(),
+ };
+
+const createOwnerMock = (): Owner => ({
+ login: faker.internet.userName(),
+ id: faker.number.int(),
+ node_id: faker.string.uuid(),
+ avatar_url: faker.image.avatar(),
+ gravatar_id: '',
+ url: faker.internet.url(),
+ html_url: faker.internet.url(),
+ followers_url: faker.internet.url(),
+ following_url: faker.internet.url(),
+ gists_url: faker.internet.url(),
+ starred_url: faker.internet.url(),
+ subscriptions_url: faker.internet.url(),
+ organizations_url: faker.internet.url(),
+ repos_url: faker.internet.url(),
+ events_url: faker.internet.url(),
+ received_events_url: faker.internet.url(),
+ type: 'User',
+ site_admin: faker.datatype.boolean(),
+});
+
+export const createRepoMock = (): Repos =>
+ {
+ id: faker.number.int(),
+ node_id: faker.string.uuid(),
+ name: faker.company.name(),
+ full_name: `${faker.company.name()}/${faker.internet.userName()}`,
+ owner: createOwnerMock(),
+ private: faker.datatype.boolean(),
+ html_url: faker.internet.url(),
+ description: faker.lorem.sentence(),
+ fork: faker.datatype.boolean(),
+ url: faker.internet.url(),
+ permissions: createPermissionsMock(),
+ security_and_analysis: createSecurityAndAnalysisMock(),
+ // Populate other URLs and properties as needed...
+ };
diff --git a/ui/test-utils/__mocks__/repos.mock.ts b/ui/test-utils/__mocks__/repos.mock.ts
new file mode 100644
index 00000000..036f229f
--- /dev/null
+++ b/ui/test-utils/__mocks__/repos.mock.ts
@@ -0,0 +1,7 @@
+import { createRepoMock } from './repo.mock';
+
+export const useReposMock = () => ({
+ data: Array.from({ length: 10 }, createRepoMock),
+ isLoading: false,
+ isError: false,
+});
diff --git a/ui/test-utils/__mocks__/user.mock.ts b/ui/test-utils/__mocks__/user.mock.ts
new file mode 100644
index 00000000..8b905c55
--- /dev/null
+++ b/ui/test-utils/__mocks__/user.mock.ts
@@ -0,0 +1,66 @@
+import { faker } from '@faker-js/faker';
+
+export const createPlanMock = (): Plan => ({
+ name: faker.company.name(),
+ space: faker.number.int({ min: 1000, max: 10000 }),
+ private_repos: faker.number.int({ max: 100 }),
+ collaborators: faker.number.int({ max: 10 }),
+});
+
+export const createUserMock = (): User => ({
+ login: faker.internet.userName(),
+ id: faker.number.int(),
+ avatar_url: faker.image.avatar(),
+ subscriptions_url: faker.internet.url(),
+ organizations_url: faker.internet.url(),
+ name: faker.person.fullName(),
+});
+
+//
+// export const createUserMock = (): User => ({
+// login: faker.internet.userName(),
+// id: faker.number.int(),
+// node_id: faker.string.uuid(),
+// avatar_url: faker.image.avatar(),
+// gravatar_id: '',
+// url: faker.internet.url(),
+// html_url: faker.internet.url(),
+// followers_url: faker.internet.url(),
+// following_url: faker.internet.url(),
+// gists_url: faker.internet.url(),
+// starred_url: faker.internet.url(),
+// subscriptions_url: faker.internet.url(),
+// organizations_url: faker.internet.url(),
+// repos_url: faker.internet.url(),
+// events_url: faker.internet.url(),
+// received_events_url: faker.internet.url(),
+// type: 'User',
+// site_admin: faker.datatype.boolean(),
+// name: faker.person.fullName(),
+// company: faker.company.name(),
+// blog: faker.internet.url(),
+// location: faker.location.secondaryAddress(),
+// email: faker.internet.email(),
+// hireable: faker.datatype.boolean(),
+// bio: faker.lorem.sentence(),
+// twitter_username: faker.internet.userName(),
+// public_repos: faker.number.int({ max: 100 }),
+// public_gists: faker.number.int({ max: 100 }),
+// followers: faker.number.int({ max: 1000 }),
+// following: faker.number.int({ max: 1000 }),
+// created_at: faker.date.past().toISOString(),
+// updated_at: faker.date.recent().toISOString(),
+// private_gists: faker.number.int({ max: 100 }),
+// total_private_repos: faker.number.int({ max: 100 }),
+// owned_private_repos: faker.number.int({ max: 100 }),
+// disk_usage: faker.number.int({ max: 10000 }),
+// collaborators: faker.number.int({ max: 10 }),
+// two_factor_authentication: faker.datatype.boolean(),
+// plan: createPlanMock(),
+// });
+
+export const useUserMock = () => ({
+ data: createUserMock(),
+ isLoading: false,
+ isError: false,
+});
diff --git a/ui/test-utils/index.ts b/ui/test-utils/index.ts
index 15d3a0e4..c1da34b0 100644
--- a/ui/test-utils/index.ts
+++ b/ui/test-utils/index.ts
@@ -1,5 +1,5 @@
import userEvent from '@testing-library/user-event';
+import { screen, waitFor } from '@testing-library/react';
-export * from '@testing-library/react';
-export { render } from './render';
-export { userEvent };
+export { render, renderWithOutMocks } from './render';
+export { userEvent, screen, waitFor };
diff --git a/ui/test-utils/render.tsx b/ui/test-utils/render.tsx
index 87f49c3e..b5d7f46e 100644
--- a/ui/test-utils/render.tsx
+++ b/ui/test-utils/render.tsx
@@ -1,27 +1,88 @@
import { render as testingLibraryRender } from '@testing-library/react';
import React from 'react';
import { vi } from 'vitest';
-import { AppProvider } from '@/providers';
+import { MemoryRouter } from 'react-router-dom';
+import { createUserMock, useReposMock, useUserMock } from './__mocks__';
+import { CustomMantineProvider, ReactQueryProvider } from '@/providers';
export function render(ui: React.ReactNode) {
- vi.mock('@/store', () => ({
- useAuthStore: vi.fn(() => ({
- token: 'mock-token',
- username: 'mock-username',
- openModal: vi.fn(),
- closeModal: vi.fn(),
- })),
- }));
-
- vi.mock('@/api/github-client', () => ({
- gitHubClient: vi.fn(() => ({
- get: vi.fn(() => Promise.resolve({ data: 'mocked data' })),
- post: vi.fn(() => Promise.resolve({ data: 'mocked response' })),
- // Add other methods as needed
- })),
- }));
+ /*
+ * Any updates to the store should be replicated here.
+ * */
+ vi.mock('@/store', () => {
+ const useAuthStore = vi.fn(() => ({
+ token: null,
+ username: null,
+ setToken: vi.fn(),
+ setUserName: vi.fn(),
+ }));
+ return { useAuthStore };
+ });
+
+ /* If additional methods are added to api client, this wrapper needs to be updated
+ * As api-client is an abstraction of the underlying GitHub client, extending the app to other git managers
+ * will be easy. And this wrapper doesn't have to be updated.
+ * */
+
+ vi.mock('@/api', () => {
+ const getUserFn = vi.fn(() => Promise.resolve(createUserMock()));
+ const useUser = vi.fn(() => useUserMock());
+ // const getReposFn = vi.fn(() => Promise.resolve(Array.from({ length: 10 }, createRepoMock)));
+ const useRepos = vi.fn(() => useReposMock());
+ const useOAuth = vi.fn(() => ({
+ data: { access_token: 'test' },
+ isLoading: false,
+ isError: false,
+ }));
+
+ const apiClient = {
+ get: vi.fn(() => Promise.resolve({ data: 'mocked get' })),
+ post: vi.fn(() => Promise.resolve({ data: 'mocked post' })),
+ put: vi.fn(() => Promise.resolve({ data: 'mocked put' })),
+ delete: vi.fn(() => Promise.resolve({ data: 'mocked delete' })),
+ patch: vi.fn(() => Promise.resolve({ data: 'mocked patch' })),
+ head: vi.fn(() => Promise.resolve({ data: 'mocked head' })),
+ options: vi.fn(() => Promise.resolve({ data: 'mocked options' })),
+ request: vi.fn(() => Promise.resolve({ data: 'mocked request' })),
+ interceptors: {
+ request: { use: vi.fn(), eject: vi.fn() },
+ response: { use: vi.fn(), eject: vi.fn() },
+ },
+ defaults: { headers: { common: {} } },
+ };
+
+ return { getUserFn, useUser, useRepos, apiClient, useOAuth, githubLogin: vi.fn() };
+ });
+
+ vi.mock('react-router-dom', async (importOriginal) => {
+ const actual = await importOriginal(); // Import the actual module
+ return {
+ // @ts-ignore
+ ...actual, // Spread all original exports
+ useNavigate: vi.fn(), // Override specific exports you want to mock
+ };
+ });
+
+ return testingLibraryRender(<>{ui}>, {
+ wrapper: ({ children }: { children: React.ReactNode }) => (
+
+
+ {children}
+
+
+ ),
+ });
+}
+
+export function renderWithOutMocks(ui: React.ReactNode) {
return testingLibraryRender(<>{ui}>, {
- wrapper: ({ children }: { children: React.ReactNode }) => {children},
+ wrapper: ({ children }: { children: React.ReactNode }) => (
+
+
+ {children}
+
+
+ ),
});
}
diff --git a/ui/vite.config.mjs b/ui/vite.config.mjs
index d225fb8d..e800cd15 100644
--- a/ui/vite.config.mjs
+++ b/ui/vite.config.mjs
@@ -1,12 +1,43 @@
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import tsconfigPaths from 'vite-tsconfig-paths';
+import istanbul from 'vite-plugin-istanbul';
export default defineConfig({
- plugins: [react(), tsconfigPaths()],
+ plugins: [react(), tsconfigPaths(), istanbul({
+ include: [
+ 'src/**/*.ts',
+ 'src/**/*.tsx',
+ ],
+ exclude: [
+ 'node_modules/**',
+ 'tests/**',
+ '**/__*',
+ '**/*.mock.ts',
+ 'test-utils/__mocks__/**',
+ 'postcss.config.cjs',
+ ],
+ extension: [ '.ts', '.tsx' ],
+ cypress: false,
+ requireEnv: false
+ })],
test: {
globals: true,
environment: 'jsdom',
setupFiles: './vitest.setup.mjs',
},
-});
\ No newline at end of file
+ coverage: {
+ provider: 'v8',
+ reporter: ['text', 'html'],
+ },
+ server: {
+ proxy: {
+ '/login/oauth/access_token': {
+ target: 'https://github.com',
+ changeOrigin: true,
+ rewrite: path => path.replace(/^\/login\/oauth\/access_token/, '/login/oauth/access_token')
+
+ }
+ }
+ }
+});
diff --git a/ui/yarn.lock b/ui/yarn.lock
index ac11e8cc..d5520c45 100644
--- a/ui/yarn.lock
+++ b/ui/yarn.lock
@@ -7,7 +7,7 @@
resolved "https://registry.yarnpkg.com/@adobe/css-tools/-/css-tools-4.3.3.tgz#90749bde8b89cd41764224f5aac29cd4138f75ff"
integrity sha512-rE0Pygv0sEZ4vBWHlAgJLGDU7Pm8xoO6p3wsEceb7GYAjScrOHpEo8KK/eVkAcnSM+slAEtXjA2JpdjLp4fJQQ==
-"@ampproject/remapping@^2.2.0":
+"@ampproject/remapping@^2.2.0", "@ampproject/remapping@^2.2.1":
version "2.3.0"
resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.3.0.tgz#ed441b6fa600072520ce18b43d2c8cc8caecc7f4"
integrity sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==
@@ -23,11 +23,24 @@
"@babel/highlight" "^7.24.2"
picocolors "^1.0.0"
+"@babel/code-frame@^7.24.6":
+ version "7.24.6"
+ resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.24.6.tgz#ab88da19344445c3d8889af2216606d3329f3ef2"
+ integrity sha512-ZJhac6FkEd1yhG2AHOmfcXG4ceoLltoCVJjN5XsWN9BifBQr+cHJbWi0h68HZuSORq+3WtJ2z0hwF2NG1b5kcA==
+ dependencies:
+ "@babel/highlight" "^7.24.6"
+ picocolors "^1.0.0"
+
"@babel/compat-data@^7.23.5":
version "7.24.4"
resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.24.4.tgz#6f102372e9094f25d908ca0d34fc74c74606059a"
integrity sha512-vg8Gih2MLK+kOkHJp4gBEIkyaIi00jgWot2D9QOmmfLC8jINSOzmCLta6Bvz/JSBCqnegV0L80jhxkol5GWNfQ==
+"@babel/compat-data@^7.24.6":
+ version "7.24.6"
+ resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.24.6.tgz#b3600217688cabb26e25f8e467019e66d71b7ae2"
+ integrity sha512-aC2DGhBq5eEdyXWqrDInSqQjO0k8xtPRf5YylULqx8MCd6jBtzqfta/3ETMRpuKIc5hyswfO80ObyA1MvkCcUQ==
+
"@babel/core@^7.23.5":
version "7.24.5"
resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.24.5.tgz#15ab5b98e101972d171aeef92ac70d8d6718f06a"
@@ -49,6 +62,27 @@
json5 "^2.2.3"
semver "^6.3.1"
+"@babel/core@^7.23.9":
+ version "7.24.6"
+ resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.24.6.tgz#8650e0e4b03589ebe886c4e4a60398db0a7ec787"
+ integrity sha512-qAHSfAdVyFmIvl0VHELib8xar7ONuSHrE2hLnsaWkYNTI68dmi1x8GYDhJjMI/e7XWal9QBlZkwbOnkcw7Z8gQ==
+ dependencies:
+ "@ampproject/remapping" "^2.2.0"
+ "@babel/code-frame" "^7.24.6"
+ "@babel/generator" "^7.24.6"
+ "@babel/helper-compilation-targets" "^7.24.6"
+ "@babel/helper-module-transforms" "^7.24.6"
+ "@babel/helpers" "^7.24.6"
+ "@babel/parser" "^7.24.6"
+ "@babel/template" "^7.24.6"
+ "@babel/traverse" "^7.24.6"
+ "@babel/types" "^7.24.6"
+ convert-source-map "^2.0.0"
+ debug "^4.1.0"
+ gensync "^1.0.0-beta.2"
+ json5 "^2.2.3"
+ semver "^6.3.1"
+
"@babel/generator@^7.24.5":
version "7.24.5"
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.24.5.tgz#e5afc068f932f05616b66713e28d0f04e99daeb3"
@@ -59,6 +93,16 @@
"@jridgewell/trace-mapping" "^0.3.25"
jsesc "^2.5.1"
+"@babel/generator@^7.24.6":
+ version "7.24.6"
+ resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.24.6.tgz#dfac82a228582a9d30c959fe50ad28951d4737a7"
+ integrity sha512-S7m4eNa6YAPJRHmKsLHIDJhNAGNKoWNiWefz1MBbpnt8g9lvMDl1hir4P9bo/57bQEmuwEhnRU/AMWsD0G/Fbg==
+ dependencies:
+ "@babel/types" "^7.24.6"
+ "@jridgewell/gen-mapping" "^0.3.5"
+ "@jridgewell/trace-mapping" "^0.3.25"
+ jsesc "^2.5.1"
+
"@babel/helper-compilation-targets@^7.23.6":
version "7.23.6"
resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz#4d79069b16cbcf1461289eccfbbd81501ae39991"
@@ -70,11 +114,27 @@
lru-cache "^5.1.1"
semver "^6.3.1"
+"@babel/helper-compilation-targets@^7.24.6":
+ version "7.24.6"
+ resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.24.6.tgz#4a51d681f7680043d38e212715e2a7b1ad29cb51"
+ integrity sha512-VZQ57UsDGlX/5fFA7GkVPplZhHsVc+vuErWgdOiysI9Ksnw0Pbbd6pnPiR/mmJyKHgyIW0c7KT32gmhiF+cirg==
+ dependencies:
+ "@babel/compat-data" "^7.24.6"
+ "@babel/helper-validator-option" "^7.24.6"
+ browserslist "^4.22.2"
+ lru-cache "^5.1.1"
+ semver "^6.3.1"
+
"@babel/helper-environment-visitor@^7.22.20":
version "7.22.20"
resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz#96159db61d34a29dba454c959f5ae4a649ba9167"
integrity sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==
+"@babel/helper-environment-visitor@^7.24.6":
+ version "7.24.6"
+ resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.24.6.tgz#ac7ad5517821641550f6698dd5468f8cef78620d"
+ integrity sha512-Y50Cg3k0LKLMjxdPjIl40SdJgMB85iXn27Vk/qbHZCFx/o5XO3PSnpi675h1KEmmDb6OFArfd5SCQEQ5Q4H88g==
+
"@babel/helper-function-name@^7.23.0":
version "7.23.0"
resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz#1f9a3cdbd5b2698a670c30d2735f9af95ed52759"
@@ -83,6 +143,14 @@
"@babel/template" "^7.22.15"
"@babel/types" "^7.23.0"
+"@babel/helper-function-name@^7.24.6":
+ version "7.24.6"
+ resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.24.6.tgz#cebdd063386fdb95d511d84b117e51fc68fec0c8"
+ integrity sha512-xpeLqeeRkbxhnYimfr2PC+iA0Q7ljX/d1eZ9/inYbmfG2jpl8Lu3DyXvpOAnrS5kxkfOWJjioIMQsaMBXFI05w==
+ dependencies:
+ "@babel/template" "^7.24.6"
+ "@babel/types" "^7.24.6"
+
"@babel/helper-hoist-variables@^7.22.5":
version "7.22.5"
resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz#c01a007dac05c085914e8fb652b339db50d823bb"
@@ -90,6 +158,13 @@
dependencies:
"@babel/types" "^7.22.5"
+"@babel/helper-hoist-variables@^7.24.6":
+ version "7.24.6"
+ resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.24.6.tgz#8a7ece8c26756826b6ffcdd0e3cf65de275af7f9"
+ integrity sha512-SF/EMrC3OD7dSta1bLJIlrsVxwtd0UpjRJqLno6125epQMJ/kyFmpTT4pbvPbdQHzCHg+biQ7Syo8lnDtbR+uA==
+ dependencies:
+ "@babel/types" "^7.24.6"
+
"@babel/helper-module-imports@^7.24.3":
version "7.24.3"
resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.24.3.tgz#6ac476e6d168c7c23ff3ba3cf4f7841d46ac8128"
@@ -97,6 +172,13 @@
dependencies:
"@babel/types" "^7.24.0"
+"@babel/helper-module-imports@^7.24.6":
+ version "7.24.6"
+ resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.24.6.tgz#65e54ffceed6a268dc4ce11f0433b82cfff57852"
+ integrity sha512-a26dmxFJBF62rRO9mmpgrfTLsAuyHk4e1hKTUkD/fcMfynt8gvEKwQPQDVxWhca8dHoDck+55DFt42zV0QMw5g==
+ dependencies:
+ "@babel/types" "^7.24.6"
+
"@babel/helper-module-transforms@^7.24.5":
version "7.24.5"
resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.24.5.tgz#ea6c5e33f7b262a0ae762fd5986355c45f54a545"
@@ -108,6 +190,17 @@
"@babel/helper-split-export-declaration" "^7.24.5"
"@babel/helper-validator-identifier" "^7.24.5"
+"@babel/helper-module-transforms@^7.24.6":
+ version "7.24.6"
+ resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.24.6.tgz#22346ed9df44ce84dee850d7433c5b73fab1fe4e"
+ integrity sha512-Y/YMPm83mV2HJTbX1Qh2sjgjqcacvOlhbzdCCsSlblOKjSYmQqEbO6rUniWQyRo9ncyfjT8hnUjlG06RXDEmcA==
+ dependencies:
+ "@babel/helper-environment-visitor" "^7.24.6"
+ "@babel/helper-module-imports" "^7.24.6"
+ "@babel/helper-simple-access" "^7.24.6"
+ "@babel/helper-split-export-declaration" "^7.24.6"
+ "@babel/helper-validator-identifier" "^7.24.6"
+
"@babel/helper-plugin-utils@^7.24.0", "@babel/helper-plugin-utils@^7.24.5":
version "7.24.5"
resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.5.tgz#a924607dd254a65695e5bd209b98b902b3b2f11a"
@@ -120,6 +213,13 @@
dependencies:
"@babel/types" "^7.24.5"
+"@babel/helper-simple-access@^7.24.6":
+ version "7.24.6"
+ resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.24.6.tgz#1d6e04d468bba4fc963b4906f6dac6286cfedff1"
+ integrity sha512-nZzcMMD4ZhmB35MOOzQuiGO5RzL6tJbsT37Zx8M5L/i9KSrukGXWTjLe1knIbb/RmxoJE9GON9soq0c0VEMM5g==
+ dependencies:
+ "@babel/types" "^7.24.6"
+
"@babel/helper-split-export-declaration@^7.24.5":
version "7.24.5"
resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.5.tgz#b9a67f06a46b0b339323617c8c6213b9055a78b6"
@@ -127,21 +227,43 @@
dependencies:
"@babel/types" "^7.24.5"
+"@babel/helper-split-export-declaration@^7.24.6":
+ version "7.24.6"
+ resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.6.tgz#e830068f7ba8861c53b7421c284da30ae656d7a3"
+ integrity sha512-CvLSkwXGWnYlF9+J3iZUvwgAxKiYzK3BWuo+mLzD/MDGOZDj7Gq8+hqaOkMxmJwmlv0iu86uH5fdADd9Hxkymw==
+ dependencies:
+ "@babel/types" "^7.24.6"
+
"@babel/helper-string-parser@^7.24.1":
version "7.24.1"
resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.24.1.tgz#f99c36d3593db9540705d0739a1f10b5e20c696e"
integrity sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ==
+"@babel/helper-string-parser@^7.24.6":
+ version "7.24.6"
+ resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.24.6.tgz#28583c28b15f2a3339cfafafeaad42f9a0e828df"
+ integrity sha512-WdJjwMEkmBicq5T9fm/cHND3+UlFa2Yj8ALLgmoSQAJZysYbBjw+azChSGPN4DSPLXOcooGRvDwZWMcF/mLO2Q==
+
"@babel/helper-validator-identifier@^7.24.5":
version "7.24.5"
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.5.tgz#918b1a7fa23056603506370089bd990d8720db62"
integrity sha512-3q93SSKX2TWCG30M2G2kwaKeTYgEUp5Snjuj8qm729SObL6nbtUldAi37qbxkD5gg3xnBio+f9nqpSepGZMvxA==
+"@babel/helper-validator-identifier@^7.24.6":
+ version "7.24.6"
+ resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.6.tgz#08bb6612b11bdec78f3feed3db196da682454a5e"
+ integrity sha512-4yA7s865JHaqUdRbnaxarZREuPTHrjpDT+pXoAZ1yhyo6uFnIEpS8VMu16siFOHDpZNKYv5BObhsB//ycbICyw==
+
"@babel/helper-validator-option@^7.23.5":
version "7.23.5"
resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz#907a3fbd4523426285365d1206c423c4c5520307"
integrity sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==
+"@babel/helper-validator-option@^7.24.6":
+ version "7.24.6"
+ resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.24.6.tgz#59d8e81c40b7d9109ab7e74457393442177f460a"
+ integrity sha512-Jktc8KkF3zIkePb48QO+IapbXlSapOW9S+ogZZkcO6bABgYAxtZcjZ/O005111YLf+j4M84uEgwYoidDkXbCkQ==
+
"@babel/helpers@^7.24.5":
version "7.24.5"
resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.24.5.tgz#fedeb87eeafa62b621160402181ad8585a22a40a"
@@ -151,6 +273,14 @@
"@babel/traverse" "^7.24.5"
"@babel/types" "^7.24.5"
+"@babel/helpers@^7.24.6":
+ version "7.24.6"
+ resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.24.6.tgz#cd124245299e494bd4e00edda0e4ea3545c2c176"
+ integrity sha512-V2PI+NqnyFu1i0GyTd/O/cTpxzQCYioSkUIRmgo7gFEHKKCg5w46+r/A6WeUR1+P3TeQ49dspGPNd/E3n9AnnA==
+ dependencies:
+ "@babel/template" "^7.24.6"
+ "@babel/types" "^7.24.6"
+
"@babel/highlight@^7.24.2":
version "7.24.5"
resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.24.5.tgz#bc0613f98e1dd0720e99b2a9ee3760194a704b6e"
@@ -161,11 +291,26 @@
js-tokens "^4.0.0"
picocolors "^1.0.0"
-"@babel/parser@^7.1.0", "@babel/parser@^7.20.7", "@babel/parser@^7.24.0", "@babel/parser@^7.24.5":
+"@babel/highlight@^7.24.6":
+ version "7.24.6"
+ resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.24.6.tgz#6d610c1ebd2c6e061cade0153bf69b0590b7b3df"
+ integrity sha512-2YnuOp4HAk2BsBrJJvYCbItHx0zWscI1C3zgWkz+wDyD9I7GIVrfnLyrR4Y1VR+7p+chAEcrgRQYZAGIKMV7vQ==
+ dependencies:
+ "@babel/helper-validator-identifier" "^7.24.6"
+ chalk "^2.4.2"
+ js-tokens "^4.0.0"
+ picocolors "^1.0.0"
+
+"@babel/parser@^7.1.0", "@babel/parser@^7.20.7", "@babel/parser@^7.24.0", "@babel/parser@^7.24.4", "@babel/parser@^7.24.5":
version "7.24.5"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.24.5.tgz#4a4d5ab4315579e5398a82dcf636ca80c3392790"
integrity sha512-EOv5IK8arwh3LI47dz1b0tKUb/1uhHAnHJOrjgtQMIpu1uXd9mlFrJg9IUgGUgZ41Ch0K8REPTYpO7B76b4vJg==
+"@babel/parser@^7.23.9", "@babel/parser@^7.24.6":
+ version "7.24.6"
+ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.24.6.tgz#5e030f440c3c6c78d195528c3b688b101a365328"
+ integrity sha512-eNZXdfU35nJC2h24RznROuOpO94h6x8sg9ju0tT9biNtLZ2vuP8SduLqqV+/8+cebSLV9SJEAN5Z3zQbJG/M+Q==
+
"@babel/plugin-transform-react-jsx-self@^7.23.3":
version "7.24.5"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.24.5.tgz#22cc7572947895c8e4cd034462e65d8ecf857756"
@@ -180,7 +325,7 @@
dependencies:
"@babel/helper-plugin-utils" "^7.24.0"
-"@babel/runtime@^7.12.5", "@babel/runtime@^7.20.13", "@babel/runtime@^7.23.2", "@babel/runtime@^7.9.2":
+"@babel/runtime@^7.12.5", "@babel/runtime@^7.20.13", "@babel/runtime@^7.23.2", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2":
version "7.24.5"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.24.5.tgz#230946857c053a36ccc66e1dd03b17dd0c4ed02c"
integrity sha512-Nms86NXrsaeU9vbBJKni6gXiEXZ4CVpYVzEjDH9Sb8vmZ3UljyA1GSOJl/6LGPO8EHLuSF9H+IxNXHPX8QHJ4g==
@@ -196,6 +341,15 @@
"@babel/parser" "^7.24.0"
"@babel/types" "^7.24.0"
+"@babel/template@^7.24.6":
+ version "7.24.6"
+ resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.24.6.tgz#048c347b2787a6072b24c723664c8d02b67a44f9"
+ integrity sha512-3vgazJlLwNXi9jhrR1ef8qiB65L1RK90+lEQwv4OxveHnqC3BfmnHdgySwRLzf6akhlOYenT+b7AfWq+a//AHw==
+ dependencies:
+ "@babel/code-frame" "^7.24.6"
+ "@babel/parser" "^7.24.6"
+ "@babel/types" "^7.24.6"
+
"@babel/traverse@^7.24.5":
version "7.24.5"
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.24.5.tgz#972aa0bc45f16983bf64aa1f877b2dd0eea7e6f8"
@@ -212,6 +366,22 @@
debug "^4.3.1"
globals "^11.1.0"
+"@babel/traverse@^7.24.6":
+ version "7.24.6"
+ resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.24.6.tgz#0941ec50cdeaeacad0911eb67ae227a4f8424edc"
+ integrity sha512-OsNjaJwT9Zn8ozxcfoBc+RaHdj3gFmCmYoQLUII1o6ZrUwku0BMg80FoOTPx+Gi6XhcQxAYE4xyjPTo4SxEQqw==
+ dependencies:
+ "@babel/code-frame" "^7.24.6"
+ "@babel/generator" "^7.24.6"
+ "@babel/helper-environment-visitor" "^7.24.6"
+ "@babel/helper-function-name" "^7.24.6"
+ "@babel/helper-hoist-variables" "^7.24.6"
+ "@babel/helper-split-export-declaration" "^7.24.6"
+ "@babel/parser" "^7.24.6"
+ "@babel/types" "^7.24.6"
+ debug "^4.3.1"
+ globals "^11.1.0"
+
"@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.22.5", "@babel/types@^7.23.0", "@babel/types@^7.24.0", "@babel/types@^7.24.5":
version "7.24.5"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.24.5.tgz#7661930afc638a5383eb0c4aee59b74f38db84d7"
@@ -221,6 +391,20 @@
"@babel/helper-validator-identifier" "^7.24.5"
to-fast-properties "^2.0.0"
+"@babel/types@^7.24.6":
+ version "7.24.6"
+ resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.24.6.tgz#ba4e1f59870c10dc2fa95a274ac4feec23b21912"
+ integrity sha512-WaMsgi6Q8zMgMth93GvWPXkhAIEobfsIkLTacoVZoK1J0CevIPGYY2Vo5YvJGqyHqXM6P4ppOYGsIRU8MM9pFQ==
+ dependencies:
+ "@babel/helper-string-parser" "^7.24.6"
+ "@babel/helper-validator-identifier" "^7.24.6"
+ to-fast-properties "^2.0.0"
+
+"@bcoe/v8-coverage@^0.2.3":
+ version "0.2.3"
+ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
+ integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
+
"@csstools/css-parser-algorithms@^2.6.1":
version "2.6.3"
resolved "https://registry.yarnpkg.com/@csstools/css-parser-algorithms/-/css-parser-algorithms-2.6.3.tgz#b5e7eb2bd2a42e968ef61484f1490a8a4148a8eb"
@@ -410,6 +594,11 @@
resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.57.0.tgz#a5417ae8427873f1dd08b70b3574b453e67b5f7f"
integrity sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==
+"@faker-js/faker@^8.4.1":
+ version "8.4.1"
+ resolved "https://registry.yarnpkg.com/@faker-js/faker/-/faker-8.4.1.tgz#5d5e8aee8fce48f5e189bf730ebd1f758f491451"
+ integrity sha512-XQ3cU+Q8Uqmrbf2e0cIC/QN43sTBSC8KF12u29Mb47tWrt2hAgBXSgpZMj4Ao8Uk0iJcU99QsOCaIL8934obCg==
+
"@floating-ui/core@^1.0.0":
version "1.6.1"
resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-1.6.1.tgz#a4e6fef1b069cda533cbc7a4998c083a37f37573"
@@ -465,6 +654,22 @@
resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz#4a2868d75d6d6963e423bcf90b7fd1be343409d3"
integrity sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==
+"@istanbuljs/load-nyc-config@^1.1.0":
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced"
+ integrity sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==
+ dependencies:
+ camelcase "^5.3.1"
+ find-up "^4.1.0"
+ get-package-type "^0.1.0"
+ js-yaml "^3.13.1"
+ resolve-from "^5.0.0"
+
+"@istanbuljs/schema@^0.1.2", "@istanbuljs/schema@^0.1.3":
+ version "0.1.3"
+ resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98"
+ integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==
+
"@jest/expect-utils@^29.7.0":
version "29.7.0"
resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.7.0.tgz#023efe5d26a8a70f21677d0a1afc0f0a44e3a1c6"
@@ -515,7 +720,7 @@
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32"
integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==
-"@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25":
+"@jridgewell/trace-mapping@^0.3.23", "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25":
version "0.3.25"
resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0"
integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==
@@ -548,6 +753,19 @@
resolved "https://registry.yarnpkg.com/@mantine/hooks/-/hooks-7.9.0.tgz#41ecc95d55da398c8c0718743f8cac13bf66b74d"
integrity sha512-LKgyrlaIK0S/gcn/VDbhqLBZOYjvhXfVcH7rMs4MIBVD+wuRo2LvvAYe3cUfQbBCfmlpRjqvewwvsIYYsjSofQ==
+"@mantine/notifications@^7.9.2":
+ version "7.9.2"
+ resolved "https://registry.yarnpkg.com/@mantine/notifications/-/notifications-7.9.2.tgz#dd1aa2f5d635b7c305d9c9738eb161bfaae12895"
+ integrity sha512-ERgmPLkiVPOqPjCVfSSK2QcRb/2W9wJVPpIlkSyMNYUWosceAH9uPhZCtnWxyRqH/PLhYtOOflxq2i4hiArEJQ==
+ dependencies:
+ "@mantine/store" "7.9.2"
+ react-transition-group "4.4.5"
+
+"@mantine/store@7.9.2":
+ version "7.9.2"
+ resolved "https://registry.yarnpkg.com/@mantine/store/-/store-7.9.2.tgz#1b20ba4acff3c085660e28e2daa11705ae1ddcba"
+ integrity sha512-oqCjse3cAp0DQI1fT5AWLW+Me6Mu4b2DVPpoRRwm7Ptw8gzUEmxb/9Brx2rkhaAym+S9sGe8IdEpNVLXaZyGXw==
+
"@nodelib/fs.scandir@2.1.5":
version "2.1.5"
resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5"
@@ -1034,6 +1252,25 @@
"@types/babel__core" "^7.20.5"
react-refresh "^0.14.0"
+"@vitest/coverage-v8@^1.6.0":
+ version "1.6.0"
+ resolved "https://registry.yarnpkg.com/@vitest/coverage-v8/-/coverage-v8-1.6.0.tgz#2f54ccf4c2d9f23a71294aba7f95b3d2e27d14e7"
+ integrity sha512-KvapcbMY/8GYIG0rlwwOKCVNRc0OL20rrhFkg/CHNzncV03TE2XWvO5w9uZYoxNiMEBacAJt3unSOiZ7svePew==
+ dependencies:
+ "@ampproject/remapping" "^2.2.1"
+ "@bcoe/v8-coverage" "^0.2.3"
+ debug "^4.3.4"
+ istanbul-lib-coverage "^3.2.2"
+ istanbul-lib-report "^3.0.1"
+ istanbul-lib-source-maps "^5.0.4"
+ istanbul-reports "^3.1.6"
+ magic-string "^0.30.5"
+ magicast "^0.3.3"
+ picocolors "^1.0.0"
+ std-env "^3.5.0"
+ strip-literal "^2.0.0"
+ test-exclude "^6.0.0"
+
"@vitest/expect@1.6.0":
version "1.6.0"
resolved "https://registry.yarnpkg.com/@vitest/expect/-/expect-1.6.0.tgz#0b3ba0914f738508464983f4d811bc122b51fb30"
@@ -1149,6 +1386,13 @@ ansi-styles@^5.0.0:
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b"
integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==
+argparse@^1.0.7:
+ version "1.0.10"
+ resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911"
+ integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==
+ dependencies:
+ sprintf-js "~1.0.2"
+
argparse@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38"
@@ -1388,6 +1632,11 @@ camelcase-css@^2.0.1:
resolved "https://registry.yarnpkg.com/camelcase-css/-/camelcase-css-2.0.1.tgz#ee978f6947914cc30c6b44741b6ed1df7f043fd5"
integrity sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==
+camelcase@^5.3.1:
+ version "5.3.1"
+ resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320"
+ integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==
+
camelize@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/camelize/-/camelize-1.0.1.tgz#89b7e16884056331a35d6b5ad064332c91daa6c3"
@@ -1746,6 +1995,14 @@ dom-accessibility-api@^0.5.6, dom-accessibility-api@^0.5.9:
resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz#5a7429e6066eb3664d911e33fb0e45de8eb08453"
integrity sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==
+dom-helpers@^5.0.1:
+ version "5.2.1"
+ resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-5.2.1.tgz#d9400536b2bf8225ad98fe052e029451ac40e902"
+ integrity sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==
+ dependencies:
+ "@babel/runtime" "^7.8.7"
+ csstype "^3.0.2"
+
electron-to-chromium@^1.4.668:
version "1.4.757"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.757.tgz#45f7c9341b538f8c4b9ca8af9692e0ed1a776a44"
@@ -2092,6 +2349,11 @@ eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800"
integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==
+eslint-visitor-keys@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz#e3adc021aa038a2a8e0b2f8b0ce8f66b9483b1fb"
+ integrity sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==
+
eslint@^8.57.0:
version "8.57.0"
resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.57.0.tgz#c786a6fd0e0b68941aaf624596fb987089195668"
@@ -2136,6 +2398,15 @@ eslint@^8.57.0:
strip-ansi "^6.0.1"
text-table "^0.2.0"
+espree@^10.0.1:
+ version "10.0.1"
+ resolved "https://registry.yarnpkg.com/espree/-/espree-10.0.1.tgz#600e60404157412751ba4a6f3a2ee1a42433139f"
+ integrity sha512-MWkrWZbJsL2UwnjxTX3gG8FneachS/Mwg7tdGXce011sJd5b0JG54vat5KHnfSBODZ3Wvzd2WnjxyzsRoVv+ww==
+ dependencies:
+ acorn "^8.11.3"
+ acorn-jsx "^5.3.2"
+ eslint-visitor-keys "^4.0.0"
+
espree@^9.6.0, espree@^9.6.1:
version "9.6.1"
resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f"
@@ -2145,6 +2416,11 @@ espree@^9.6.0, espree@^9.6.1:
acorn-jsx "^5.3.2"
eslint-visitor-keys "^3.4.1"
+esprima@^4.0.0:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71"
+ integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==
+
esquery@^1.4.2:
version "1.5.0"
resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.5.0.tgz#6ce17738de8577694edd7361c57182ac8cb0db0b"
@@ -2261,6 +2537,14 @@ fill-range@^7.0.1:
dependencies:
to-regex-range "^5.0.1"
+find-up@^4.1.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19"
+ integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==
+ dependencies:
+ locate-path "^5.0.0"
+ path-exists "^4.0.0"
+
find-up@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc"
@@ -2368,6 +2652,11 @@ get-nonce@^1.0.0:
resolved "https://registry.yarnpkg.com/get-nonce/-/get-nonce-1.0.1.tgz#fdf3f0278073820d2ce9426c18f07481b1e0cdf3"
integrity sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==
+get-package-type@^0.1.0:
+ version "0.1.0"
+ resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a"
+ integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==
+
get-stream@^8.0.1:
version "8.0.1"
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-8.0.1.tgz#def9dfd71742cd7754a7761ed43749a27d02eca2"
@@ -2396,7 +2685,7 @@ glob-parent@^6.0.2:
dependencies:
is-glob "^4.0.3"
-glob@^7.1.3:
+glob@^7.1.3, glob@^7.1.4:
version "7.2.3"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b"
integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==
@@ -2548,6 +2837,11 @@ html-encoding-sniffer@^4.0.0:
dependencies:
whatwg-encoding "^3.1.1"
+html-escaper@^2.0.0:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453"
+ integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==
+
html-tags@^3.3.1:
version "3.3.1"
resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-3.3.1.tgz#a04026a18c882e4bba8a01a3d39cfe465d40b5ce"
@@ -2858,6 +3152,48 @@ isexe@^2.0.0:
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==
+istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0, istanbul-lib-coverage@^3.2.2:
+ version "3.2.2"
+ resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz#2d166c4b0644d43a39f04bf6c2edd1e585f31756"
+ integrity sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==
+
+istanbul-lib-instrument@^6.0.2:
+ version "6.0.2"
+ resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.2.tgz#91655936cf7380e4e473383081e38478b69993b1"
+ integrity sha512-1WUsZ9R1lA0HtBSohTkm39WTPlNKSJ5iFk7UwqXkBLoHQT+hfqPsfsTDVuZdKGaBwn7din9bS7SsnoAr943hvw==
+ dependencies:
+ "@babel/core" "^7.23.9"
+ "@babel/parser" "^7.23.9"
+ "@istanbuljs/schema" "^0.1.3"
+ istanbul-lib-coverage "^3.2.0"
+ semver "^7.5.4"
+
+istanbul-lib-report@^3.0.0, istanbul-lib-report@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz#908305bac9a5bd175ac6a74489eafd0fc2445a7d"
+ integrity sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==
+ dependencies:
+ istanbul-lib-coverage "^3.0.0"
+ make-dir "^4.0.0"
+ supports-color "^7.1.0"
+
+istanbul-lib-source-maps@^5.0.4:
+ version "5.0.4"
+ resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.4.tgz#1947003c72a91b6310efeb92d2a91be8804d92c2"
+ integrity sha512-wHOoEsNJTVltaJp8eVkm8w+GVkVNHT2YDYo53YdzQEL2gWm1hBX5cGFR9hQJtuGLebidVX7et3+dmDZrmclduw==
+ dependencies:
+ "@jridgewell/trace-mapping" "^0.3.23"
+ debug "^4.1.1"
+ istanbul-lib-coverage "^3.0.0"
+
+istanbul-reports@^3.1.6:
+ version "3.1.7"
+ resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.1.7.tgz#daed12b9e1dca518e15c056e1e537e741280fa0b"
+ integrity sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==
+ dependencies:
+ html-escaper "^2.0.0"
+ istanbul-lib-report "^3.0.0"
+
iterator.prototype@^1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/iterator.prototype/-/iterator.prototype-1.1.2.tgz#5e29c8924f01916cb9335f1ff80619dcff22b0c0"
@@ -2956,6 +3292,14 @@ js-tokens@^9.0.0:
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-9.0.0.tgz#0f893996d6f3ed46df7f0a3b12a03f5fd84223c1"
integrity sha512-WriZw1luRMlmV3LGJaR6QOJjWwgLUTf89OwT2lUOyjX2dJGBwgmIkbcz+7WFZjrZM635JOIR517++e/67CP9dQ==
+js-yaml@^3.13.1:
+ version "3.14.1"
+ resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537"
+ integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==
+ dependencies:
+ argparse "^1.0.7"
+ esprima "^4.0.0"
+
js-yaml@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602"
@@ -3097,6 +3441,13 @@ local-pkg@^0.5.0:
mlly "^1.4.2"
pkg-types "^1.0.3"
+locate-path@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0"
+ integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==
+ dependencies:
+ p-locate "^4.1.0"
+
locate-path@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286"
@@ -3159,6 +3510,22 @@ magic-string@^0.30.5:
dependencies:
"@jridgewell/sourcemap-codec" "^1.4.15"
+magicast@^0.3.3:
+ version "0.3.4"
+ resolved "https://registry.yarnpkg.com/magicast/-/magicast-0.3.4.tgz#bbda1791d03190a24b00ff3dd18151e7fd381d19"
+ integrity sha512-TyDF/Pn36bBji9rWKHlZe+PZb6Mx5V8IHCSxk7X4aljM4e/vyDvZZYwHewdVaqiA0nb3ghfHU/6AUpDxWoER2Q==
+ dependencies:
+ "@babel/parser" "^7.24.4"
+ "@babel/types" "^7.24.0"
+ source-map-js "^1.2.0"
+
+make-dir@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-4.0.0.tgz#c3c2307a771277cd9638305f915c29ae741b614e"
+ integrity sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==
+ dependencies:
+ semver "^7.5.3"
+
mathml-tag-names@^2.1.3:
version "2.1.3"
resolved "https://registry.yarnpkg.com/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz#4ddadd67308e780cf16a47685878ee27b736a0a3"
@@ -3214,7 +3581,7 @@ min-indent@^1.0.0:
resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869"
integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==
-minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2:
+minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2:
version "3.1.2"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
@@ -3390,6 +3757,13 @@ optionator@^0.9.3:
type-check "^0.4.0"
word-wrap "^1.2.5"
+p-limit@^2.2.0:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1"
+ integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==
+ dependencies:
+ p-try "^2.0.0"
+
p-limit@^3.0.2:
version "3.1.0"
resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b"
@@ -3404,6 +3778,13 @@ p-limit@^5.0.0:
dependencies:
yocto-queue "^1.0.0"
+p-locate@^4.1.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07"
+ integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==
+ dependencies:
+ p-limit "^2.2.0"
+
p-locate@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834"
@@ -3411,6 +3792,11 @@ p-locate@^5.0.0:
dependencies:
p-limit "^3.0.2"
+p-try@^2.0.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6"
+ integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==
+
parent-module@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2"
@@ -3615,7 +4001,7 @@ pretty-format@^29.0.0, pretty-format@^29.7.0:
ansi-styles "^5.0.0"
react-is "^18.0.0"
-prop-types@^15.7.2, prop-types@^15.8.1:
+prop-types@^15.6.2, prop-types@^15.7.2, prop-types@^15.8.1:
version "15.8.1"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5"
integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==
@@ -3736,6 +4122,16 @@ react-textarea-autosize@8.5.3:
use-composed-ref "^1.3.0"
use-latest "^1.2.1"
+react-transition-group@4.4.5:
+ version "4.4.5"
+ resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.5.tgz#e53d4e3f3344da8521489fbef8f2581d42becdd1"
+ integrity sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==
+ dependencies:
+ "@babel/runtime" "^7.5.5"
+ dom-helpers "^5.0.1"
+ loose-envify "^1.4.0"
+ prop-types "^15.6.2"
+
react@^18.3.1:
version "18.3.1"
resolved "https://registry.yarnpkg.com/react/-/react-18.3.1.tgz#49ab892009c53933625bd16b2533fc754cab2891"
@@ -3909,6 +4305,11 @@ semver@^6.3.0, semver@^6.3.1:
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4"
integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
+semver@^7.5.3, semver@^7.5.4:
+ version "7.6.2"
+ resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.2.tgz#1e3b34759f896e8f14d6134732ce798aeb0c6e13"
+ integrity sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==
+
semver@^7.6.0:
version "7.6.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.0.tgz#1a46a4db4bffcccd97b743b5005c8325f23d4e2d"
@@ -3994,6 +4395,16 @@ source-map-js@^1.0.1, source-map-js@^1.0.2, source-map-js@^1.2.0:
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.0.tgz#16b809c162517b5b8c3e7dcd315a2a5c2612b2af"
integrity sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==
+source-map@^0.7.4:
+ version "0.7.4"
+ resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.4.tgz#a9bbe705c9d8846f4e08ff6765acf0f1b0898656"
+ integrity sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==
+
+sprintf-js@~1.0.2:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
+ integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==
+
stack-utils@^2.0.3:
version "2.0.6"
resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.6.tgz#aaf0748169c02fc33c8232abccf933f54a1cc34f"
@@ -4279,6 +4690,15 @@ table@^6.8.2:
string-width "^4.2.3"
strip-ansi "^6.0.1"
+test-exclude@^6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e"
+ integrity sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==
+ dependencies:
+ "@istanbuljs/schema" "^0.1.2"
+ glob "^7.1.4"
+ minimatch "^3.0.4"
+
text-table@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
@@ -4530,6 +4950,18 @@ vite-node@1.6.0:
picocolors "^1.0.0"
vite "^5.0.0"
+vite-plugin-istanbul@^6.0.2:
+ version "6.0.2"
+ resolved "https://registry.yarnpkg.com/vite-plugin-istanbul/-/vite-plugin-istanbul-6.0.2.tgz#242a57b20d540adc49631f9e6c174a1c3559ece7"
+ integrity sha512-0/sKwjEEIwbEyl43xX7onX3dIbMJAsigNsKyyVPalG1oRFo5jn3qkJbS2PUfp9wrr3piy1eT6qRoeeum2p4B2A==
+ dependencies:
+ "@istanbuljs/load-nyc-config" "^1.1.0"
+ espree "^10.0.1"
+ istanbul-lib-instrument "^6.0.2"
+ picocolors "^1.0.0"
+ source-map "^0.7.4"
+ test-exclude "^6.0.0"
+
vite-tsconfig-paths@^4.3.1:
version "4.3.2"
resolved "https://registry.yarnpkg.com/vite-tsconfig-paths/-/vite-tsconfig-paths-4.3.2.tgz#321f02e4b736a90ff62f9086467faf4e2da857a9"