@autobusal/providers 1.0.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,56 @@
1
+ import { Toaster } from 'react-hot-toast';
2
+ import useSystemTheme from '../Styles/hooks/useSystemTheme';
3
+ import { GetSettings } from '../services';
4
+
5
+ interface Props {
6
+ children: JSX.Element
7
+ }
8
+
9
+ export const Provider = ({ children }: Props): JSX.Element => {
10
+ const { data } = GetSettings();
11
+
12
+ const mode = useSystemTheme();
13
+
14
+ const ourTheme: any = data.colors[mode as keyof typeof data.colors];
15
+
16
+ return (
17
+ <>
18
+ { children }
19
+
20
+ <Toaster
21
+ position="top-right"
22
+ gutter={ 12 }
23
+ toastOptions={{
24
+ style: {
25
+ padding: '6px 10px',
26
+ fontSize: data.theme.size.xs,
27
+ color: ourTheme.font.normal,
28
+ fontWeight: '700',
29
+ background: ourTheme.background.normal,
30
+ borderRadius: ourTheme.borderRadius,
31
+ boxShadow: ourTheme.boxShadow
32
+ },
33
+
34
+ success: {
35
+ style: {
36
+ background: ourTheme.font.success,
37
+ color: '#FFFFFF'
38
+ }
39
+ },
40
+
41
+ error: {
42
+ style: {
43
+ background: ourTheme.font.error,
44
+ color: '#FFFFFF'
45
+ }
46
+ }
47
+ }}
48
+ containerStyle={{
49
+ top: '70px'
50
+ }}
51
+ />
52
+ </>
53
+ );
54
+ };
55
+
56
+ export default Provider;
package/Providers.tsx ADDED
@@ -0,0 +1,27 @@
1
+ import { Suspense } from 'react';
2
+ import Queries from './Queries/Queries';
3
+ import Styles from './Styles/Styles';
4
+ import Notifications from './Notifications/Notifications';
5
+ import Setup from './Setup/Setup';
6
+
7
+ interface Props {
8
+ type: 'normal' | 'whitelabel'
9
+ loading: JSX.Element
10
+ children: JSX.Element
11
+ }
12
+
13
+ const Providers = ({ type, loading, children }: Props): JSX.Element => (
14
+ <Queries>
15
+ <Styles>
16
+ <Notifications>
17
+ <Setup type={ type }>
18
+ <Suspense fallback={ loading }>
19
+ { children }
20
+ </Suspense>
21
+ </Setup>
22
+ </Notifications>
23
+ </Styles>
24
+ </Queries>
25
+ );
26
+
27
+ export default Providers;
@@ -0,0 +1,22 @@
1
+ import { QueryClientProvider, QueryClient } from '@tanstack/react-query';
2
+
3
+ const queryClient = new QueryClient({
4
+ defaultOptions: {
5
+ queries: {
6
+ refetchOnWindowFocus: false,
7
+ retry: false
8
+ }
9
+ }
10
+ });
11
+
12
+ interface Props {
13
+ children: JSX.Element
14
+ }
15
+
16
+ const Queries = ({ children }: Props): JSX.Element => (
17
+ <QueryClientProvider client={ queryClient }>
18
+ { children }
19
+ </QueryClientProvider>
20
+ );
21
+
22
+ export default Queries;
@@ -0,0 +1,49 @@
1
+ import axios from 'axios';
2
+ // import i18next from 'i18next';
3
+ // import { error } from '@utilities/notify';
4
+
5
+ const apiClient = axios.create({
6
+ baseURL: import.meta.env.VITE_API_OBT,
7
+ withCredentials: true
8
+ });
9
+
10
+ apiClient.interceptors.request.use(
11
+ config => {
12
+ // // const token = localStorage.getItem('token');
13
+
14
+ // // if (token !== null) {
15
+ // // config.headers.Authorization = token;
16
+ // // }
17
+
18
+ return config;
19
+ }
20
+ );
21
+
22
+ apiClient.interceptors.response.use(response => (
23
+ response
24
+ ), err => {
25
+ console.log(err.response);
26
+
27
+ // if (err.response) {
28
+ // // if we have a 401, we go to the logout page
29
+ // // if (err.response.status === 401) {
30
+ // // alert(401);
31
+ // // }
32
+
33
+ // // @todo: how to handle 404?
34
+
35
+ // // if we have a 422 error, we display the message
36
+ // if (err.response.status === 422) {
37
+ // error(err.response.data.message);
38
+ // }
39
+ // } else if (err.request) {
40
+ // // if we have a 500 error, we display an error message
41
+ // error(i18next.t('server_error', { ns: 'general' }))
42
+ // } else {
43
+ // console.log('ERROR', err.message);
44
+ // }
45
+
46
+ return err;
47
+ });
48
+
49
+ export default apiClient;
@@ -0,0 +1,39 @@
1
+ import { Helmet } from 'react-helmet';
2
+ import { SettingsData } from '../types';
3
+
4
+ interface Props {
5
+ data: SettingsData
6
+ children: JSX.Element
7
+ }
8
+
9
+ // @todo: De adaugat din tema Favicons
10
+ const Preload = ({ data, children }: Props): JSX.Element => {
11
+ // we arrange the font family names before inserting them
12
+ const fontTitle = data.theme.fontFamily.title.replace(/\ /g, '+');
13
+ const fontContent = data.theme.fontFamily.content.replace(/\ /g, '+');
14
+
15
+ return (
16
+ <>
17
+ <Helmet>
18
+ <title>{ data.preferences.company }</title>
19
+
20
+ {/* <!-- Favicons --> */}
21
+ {/* <link rel="apple-touch-icon" sizes="180x180" href="/favicons/apple-touch-icon.png" /> */}
22
+ {/* <link rel="icon" type="image/png" sizes="32x32" href="/favicons/favicon-32x32.png" /> */}
23
+ {/* <link rel="icon" type="image/png" sizes="16x16" href="/favicons/favicon-16x16.png" /> */}
24
+ {/* <link rel="manifest" href="/favicons/site.webmanifest" /> */}
25
+ {/* <link rel="mask-icon" href="/favicons/safari-pinned-tab.svg" color="#5bbad5" /> */}
26
+ {/* <link rel="shortcut icon" href="/favicons/favicon.ico" /> */}
27
+ {/* <meta name="msapplication-TileColor" content="#ffffff" /> */}
28
+ {/* <meta name="msapplication-config" content="/favicons/browserconfig.xml" /> */}
29
+ {/* <meta name="theme-color" content="#ffffff" /> */}
30
+
31
+ <link href={ `https://fonts.googleapis.com/css2?family=${ fontTitle }:wght@700&family=${ fontContent }:wght@300;400;700&display=swap` } rel="stylesheet" />
32
+ </Helmet>
33
+
34
+ { children }
35
+ </>
36
+ );
37
+ };
38
+
39
+ export default Preload;
@@ -0,0 +1,25 @@
1
+ import Preload from './Preload';
2
+ import languages from './utilities/languages';
3
+ import analytics from './utilities/analytics';
4
+ import { GetSettings } from '../services';
5
+
6
+ interface Props {
7
+ type: 'normal' | 'whitelabel'
8
+ children: JSX.Element
9
+ }
10
+
11
+ const Setup = ({ type, children }: Props): JSX.Element => {
12
+ const { data } = GetSettings();
13
+
14
+ languages(type, data.preferences.languages.default);
15
+
16
+ analytics(data.preferences.googleAnalyticsID);
17
+
18
+ return (
19
+ <Preload data={ data }>
20
+ { children }
21
+ </Preload>
22
+ );
23
+ };
24
+
25
+ export default Setup;
@@ -0,0 +1,9 @@
1
+ import ReactGA from 'react-ga4';
2
+
3
+ const analytics = (id: string | null): void => {
4
+ if (id !== null) {
5
+ ReactGA.initialize(id);
6
+ }
7
+ };
8
+
9
+ export default analytics;
@@ -0,0 +1,25 @@
1
+ import i18next from 'i18next';
2
+ import Backend from 'i18next-http-backend';
3
+ import { initReactI18next } from 'react-i18next';
4
+
5
+ const languages = (type: string, defaultLanguage: string): void => {
6
+ const currentLanguage = localStorage.getItem('language');
7
+
8
+ i18next
9
+ .use(Backend)
10
+ .use(initReactI18next)
11
+ .init({
12
+ interpolation: {
13
+ escapeValue: false
14
+ },
15
+ lng: currentLanguage ?? defaultLanguage,
16
+ fallbackLng: defaultLanguage,
17
+ debug: true,
18
+ backend: {
19
+ loadPath: `${ import.meta.env.VITE_API_OBT }/languages/${ type }/{{lng}}/{{ns}}.json`
20
+ },
21
+ ns: 'general'
22
+ });
23
+ };
24
+
25
+ export default languages;
@@ -0,0 +1,112 @@
1
+ import { createGlobalStyle } from 'styled-components';
2
+
3
+ const GlobalStyles = createGlobalStyle`
4
+ * {
5
+ outline: none;
6
+ box-sizing: border-box;
7
+ }
8
+
9
+ html {
10
+ height: 100%;
11
+ font-family: sans-serif;
12
+ -webkit-text-size-adjust: 100%;
13
+ -ms-text-size-adjust: 100%;
14
+ -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
15
+ }
16
+
17
+ body {
18
+ height: 100%;
19
+ margin: 0;
20
+ padding: 0;
21
+ font-family: '${props => props.theme.fontFamily.content}', sans-serif;
22
+ color: ${ props => props.theme.font.normal };
23
+ font-size: ${ props => props.theme.size.m };
24
+ line-height: ${ props => props.theme.lineHeight.content };
25
+ background: ${ props => props.theme.background.normal };
26
+ }
27
+
28
+ #root {
29
+ width: 100%;
30
+ height: 100%;
31
+ }
32
+
33
+ a {
34
+ color: ${ props => props.theme.font.normal };
35
+ text-decoration: none;
36
+
37
+ &:hover {
38
+ text-decoration: underline;
39
+ }
40
+ }
41
+
42
+ button {
43
+ all: unset;
44
+ box-sizing: border-box;
45
+ cursor: pointer;
46
+ }
47
+
48
+ h1, h2, h3, h4, h5, h6 {
49
+ display: block;
50
+ margin: 0 0 20px 0;
51
+ padding: 0;
52
+ font-weight: 700;
53
+ line-height: ${ props => props.theme.lineHeight.title };
54
+ font-family: ${ props => props.theme.fontFamily.title };
55
+ }
56
+
57
+ h1 {
58
+ font-size: ${ props => props.theme.size.xxl };
59
+ }
60
+ h2, h3 {
61
+ font-size: ${ props => props.theme.size.xl };
62
+ }
63
+ h4, h5, h6 {
64
+ font-size: ${ props => props.theme.size.l };
65
+ }
66
+
67
+ input[type='text'], input[type='number'], input[type='email'], input[type='password'], textarea, select {
68
+ display: block;
69
+ width: 100%;
70
+ height: 38px;
71
+ padding: 0 10px;
72
+ font-size: ${ props => props.theme.size.m };
73
+ font-weight: 700;
74
+ color: ${ props => props.theme.font.normal };
75
+ background: ${ props => props.theme.background.normal };
76
+ border: 1px solid ${ props => props.theme.font.faded };
77
+ border-radius: ${ props => props.theme.borderRadius };
78
+ -webkit-appearance: none;
79
+ }
80
+
81
+ input[type='checkbox'] {
82
+ -webkit-appearance: checkbox;
83
+ }
84
+
85
+ input[type='radio'] {
86
+ display: inline-block;
87
+ margin-right: 5px;
88
+ -webkit-appearance: radio;
89
+ }
90
+
91
+ select {
92
+ padding: 0 10px;
93
+ font-size: ${ props => props.theme.size.m };
94
+ }
95
+
96
+ textarea {
97
+ min-height: 120px;
98
+ resize: vertical;
99
+ padding: 10px;
100
+ font-family: '${props => props.theme.fontFamily.content}', sans-serif;
101
+ font-size: ${ props => props.theme.size.s };
102
+ line-height: ${ props => props.theme.lineHeight.title };
103
+ font-weight: 700;
104
+ }
105
+
106
+ p {
107
+ padding: 0;
108
+ margin: 0;
109
+ }
110
+ `;
111
+
112
+ export default GlobalStyles;
@@ -0,0 +1,24 @@
1
+ import { ThemeProvider } from 'styled-components';
2
+ import useSystemTheme from './hooks/useSystemTheme';
3
+ import GlobalStyles from './GlobalStyles';
4
+ import { GetSettings } from '../services';
5
+
6
+ interface Props {
7
+ children: JSX.Element
8
+ }
9
+
10
+ const Styles = ({ children }: Props): JSX.Element => {
11
+ const { data } = GetSettings();
12
+
13
+ const mode = useSystemTheme();
14
+
15
+ return (
16
+ <ThemeProvider theme={ { ...data.colors[mode as keyof typeof data.colors], ...data.theme } }>
17
+ <GlobalStyles />
18
+
19
+ { children }
20
+ </ThemeProvider>
21
+ );
22
+ };
23
+
24
+ export default Styles;
@@ -0,0 +1,18 @@
1
+ import { useState, useEffect } from 'react';
2
+
3
+ const useSystemTheme = (): string => {
4
+ const queryDarkMode = window.matchMedia("(prefers-color-scheme: dark)");
5
+
6
+ const [ theme, setTheme ] = useState<string>(queryDarkMode.matches ? 'dark' : 'light');
7
+
8
+ // setup a listener to respond to future changes of the system theme
9
+ useEffect(() => {
10
+ queryDarkMode.addEventListener('change', (event: MediaQueryListEvent) => {
11
+ setTheme(event.matches ? 'dark' : 'light');
12
+ })
13
+ });
14
+
15
+ return theme;
16
+ };
17
+
18
+ export default useSystemTheme;
package/index.ts CHANGED
@@ -1 +1,8 @@
1
- alert('Hello World');
1
+ import Providers from './Providers';
2
+ import apiClient from './Queries/apiClient';
3
+
4
+ export {
5
+ apiClient
6
+ };
7
+
8
+ export default Providers;
package/package.json CHANGED
@@ -1,5 +1,17 @@
1
1
  {
2
2
  "name": "@autobusal/providers",
3
- "version": "1.0.0",
4
- "type": "module"
5
- }
3
+ "version": "1.0.2",
4
+ "type": "module",
5
+ "main": "index.ts",
6
+ "dependencies": {
7
+ "@tanstack/react-query": "^5.0.5",
8
+ "axios": "^1.5.1",
9
+ "i18next": "^23.6.0",
10
+ "i18next-http-backend": "^2.2.2",
11
+ "react-ga4": "^2.1.0",
12
+ "react-helmet": "^6.1.0",
13
+ "react-hot-toast": "^2.4.1",
14
+ "react-i18next": "^13.3.1",
15
+ "styled-components": "^6.1.0"
16
+ }
17
+ }
package/services.ts ADDED
@@ -0,0 +1,18 @@
1
+ import { useSuspenseQuery, UseSuspenseQueryResult } from '@tanstack/react-query';
2
+ import apiClient from './Queries/apiClient';
3
+ import { SettingsData } from './types';
4
+
5
+ export const GetSettings = (): UseSuspenseQueryResult<SettingsData> => (
6
+ useSuspenseQuery({
7
+ queryKey: ['get-settings'],
8
+ queryFn: async () => (
9
+ await apiClient
10
+ .get('/api/settings/get')
11
+ .then(response => (
12
+ response.data
13
+ ))
14
+ .catch(() => {})
15
+ ),
16
+ staleTime: Infinity
17
+ })
18
+ );
package/types.ts ADDED
@@ -0,0 +1,25 @@
1
+ export interface SettingsData {
2
+ preferences: {
3
+ company: string
4
+ logo: string
5
+
6
+ googleAnalyticsID: string | null
7
+
8
+ languages: {
9
+ available: string[]
10
+ default: string
11
+ }
12
+ }
13
+
14
+ theme: {
15
+ fontFamily: any
16
+ lineHeight: any
17
+ size: any
18
+ borderRadius: string
19
+ }
20
+
21
+ colors: {
22
+ light: any
23
+ dark: any
24
+ }
25
+ }