Skip to content

Commit

Permalink
Merge pull request #12 from vadymstebakov/main
Browse files Browse the repository at this point in the history
New updates
  • Loading branch information
m-kolomoyets authored Feb 3, 2025
2 parents 82763b2 + 3e1048a commit dea4402
Show file tree
Hide file tree
Showing 35 changed files with 1,467 additions and 705 deletions.
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ This project was bootstrapped with [Vite.js](https://vitejs.dev).
- [Typescript](https://www.typescriptlang.org) - Static type checker
- [Vite](https://vitejs.dev/) - Bundler
- [Tanstack Router](https://tanstack.com/router/latest/docs/framework/react/overview) - Routing (file-based)
- [@svg-use](https://github.com/fpapado/svg-use) - icon management tool
- [Tanstack Query](https://tanstack.com/query/latest/docs/framework/react/overview) - Asynchronous state management
- [@svg-use](https://github.com/fpapado/svg-use) - Icon management tool
- [Eslint](https://eslint.org/) - Code linter
- [Prettier](https://prettier.io/) - Code formatter
- [Husky](https://typicode.github.io/husky/) - commands execution handler on git events
- [CLSX](https://github.com/lukeed/clsx) - classNames management tool
- [Husky](https://typicode.github.io/husky/) - Commands execution handler on git events

</details>

Expand Down Expand Up @@ -227,7 +227,7 @@ Services should:
- Have separate root `src/services` folder
- Each service scope should have its own folder
- Each service scope should have its own file

##### API Services

API services are services that contain API requests functions to be used in API hooks.
Expand Down
10 changes: 8 additions & 2 deletions eslint.config.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import eslint from '@eslint/js';
import pluginTanstackQuery from '@tanstack/eslint-plugin-query';
import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended';
import react from 'eslint-plugin-react';
import reactHooksPlugin from 'eslint-plugin-react-hooks';
Expand All @@ -8,8 +9,13 @@ import tseslint from 'typescript-eslint';
export default tseslint.config({
ignores: ['**/dist/**', '**/tmp/**', '/src/routeTree.gen.ts'],
files: ['**/*.{js,ts,tsx,cjs,mjs}'],
extends: [eslint.configs.recommended, tseslint.configs.recommended, eslintPluginPrettierRecommended],
plugins: { react, 'react-hooks': reactHooksPlugin },
extends: [
eslint.configs.recommended,
tseslint.configs.recommended,
eslintPluginPrettierRecommended,
pluginTanstackQuery.configs['flat/recommended'],
],
plugins: { react, 'react-hooks': reactHooksPlugin, '@tanstack/query': pluginTanstackQuery },
languageOptions: {
parserOptions: {
project: ['./tsconfig.app.json', './tsconfig.node.json', './tsconfig.json'],
Expand Down
3 changes: 3 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + React + TS</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Syne:[email protected]&display=swap" rel="stylesheet" />
</head>

<body>
Expand Down
1,505 changes: 1,005 additions & 500 deletions package-lock.json

Large diffs are not rendered by default.

43 changes: 24 additions & 19 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,39 +23,44 @@
},
"dependencies": {
"@svg-use/react": "^0.4.3",
"@tanstack/react-router": "^1.95.3",
"@tanstack/react-query": "^5.66.0",
"@tanstack/react-router": "^1.99.0",
"axios": "^1.7.9",
"clsx": "^2.1.1",
"react": "^19.0.0",
"react-dom": "^19.0.0"
"react-dom": "^19.0.0",
"zod": "^3.24.1"
},
"devDependencies": {
"@commitlint/cli": "^19.6.1",
"@commitlint/config-conventional": "^19.6.0",
"@eslint/js": "^9.18.0",
"@ianvs/prettier-plugin-sort-imports": "^4.4.0",
"@commitlint/cli": "^19.7.1",
"@commitlint/config-conventional": "^19.7.1",
"@eslint/js": "^9.19.0",
"@ianvs/prettier-plugin-sort-imports": "^4.4.1",
"@svg-use/vite": "^0.1.3",
"@tanstack/router-devtools": "^1.95.3",
"@tanstack/router-vite-plugin": "^1.95.3",
"@types/react": "^19.0.4",
"@types/react-dom": "^19.0.2",
"@tanstack/eslint-plugin-query": "^5.66.0",
"@tanstack/react-query-devtools": "^5.66.0",
"@tanstack/router-devtools": "^1.99.0",
"@tanstack/router-vite-plugin": "^1.99.0",
"@types/react": "^19.0.8",
"@types/react-dom": "^19.0.3",
"@vitejs/plugin-react": "^4.3.4",
"autoprefixer": "^10.4.20",
"eslint": "^9.17.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.2.1",
"eslint-plugin-react": "^7.37.3",
"eslint": "^9.19.0",
"eslint-config-prettier": "^10.0.1",
"eslint-plugin-prettier": "^5.2.3",
"eslint-plugin-react": "^7.37.4",
"eslint-plugin-react-hooks": "^5.1.0",
"globals": "^15.14.0",
"husky": "^9.1.7",
"npm-run-all": "^4.1.5",
"postcss": "^8.4.49",
"postcss": "^8.5.1",
"prettier": "^3.4.2",
"rollup-plugin-visualizer": "^5.14.0",
"stylelint": "^16.12.0",
"stylelint-config-standard": "^36.0.1",
"stylelint": "^16.14.1",
"stylelint-config-standard": "^37.0.0",
"typescript": "^5.7.3",
"typescript-eslint": "^8.19.1",
"vite": "^6.0.7",
"typescript-eslint": "^8.22.0",
"vite": "^6.0.11",
"vite-plugin-eslint2": "^5.0.3"
}
}
28 changes: 28 additions & 0 deletions src/api/@axios.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import axios from 'axios';
import { getAccessToken } from '@/utils/auth/tokens';

export const http = axios.create({
baseURL: import.meta.env.VITE_API_URL,
adapter: 'fetch',
});

export const httpPrivate = axios.create({
baseURL: import.meta.env.VITE_API_URL,
adapter: 'fetch',
});

httpPrivate.interceptors.request.use(
(config) => {
const token = getAccessToken();

if (token) {
config.headers.Authorization = `Bearer ${token}`;
config.withCredentials = true;
}

return config;
},
(error) => {
return Promise.reject(error);
}
);
17 changes: 17 additions & 0 deletions src/api/auth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import type { AxiosRequestConfig } from 'axios';
import { httpPrivate } from '@/api/@axios';

export type RefreshTokenData = {
refreshToken: string;
};

export type RefreshTokenResponseData = {
accessToken: string;
refreshToken: string;
};

export const refreshToken = async (data: RefreshTokenData, config?: AxiosRequestConfig) => {
const response = await httpPrivate.post<RefreshTokenResponseData>('/api/refresh-token', data, config);

return response.data;
};
14 changes: 14 additions & 0 deletions src/api/usersExample.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import type { AxiosRequestConfig } from 'axios';
import { httpPrivate } from '@/api/@axios';

export const getUsers = async (config?: AxiosRequestConfig) => {
const response = await httpPrivate.get<string[]>('/api/users', config);

return response.data;
};

export const createUser = async (data: string, config?: AxiosRequestConfig) => {
const response = await httpPrivate.post<string>('/api/users', data, config);

return response.data;
};
59 changes: 59 additions & 0 deletions src/hooks/useOptimisticMutation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import type { DataTag, MutationFunction, QueryKey } from '@tanstack/react-query';

type Updater<TQueryFnData, TVariables> = (
_oldData: TQueryFnData | undefined,
_variables: TVariables
) => TQueryFnData | undefined;

type OptimisticProps<
TData = unknown,
TVariables = unknown,
TQueryKey extends QueryKey = QueryKey,
TQueryFnData = unknown,
> = {
mutationFn: MutationFunction<TData, TVariables>;
queryKey: TQueryKey;
updater: Updater<TQueryFnData, TVariables>;
invalidate: () => Promise<void>;
};

export const useOptimisticMutation = <
TData = unknown,
TVariables = unknown,
TQueryFnData = unknown,
TQueryKey extends QueryKey = QueryKey,
TInferredQueryFnData = TQueryKey extends DataTag<unknown, infer TaggedValue> ? TaggedValue : TQueryFnData,
>({
mutationFn,
queryKey,
updater,
invalidate,
}: OptimisticProps<TData, TVariables, TQueryKey, TInferredQueryFnData>) => {
const queryClient = useQueryClient();

return useMutation({
mutationFn,
async onMutate(variables) {
await queryClient.cancelQueries({
queryKey,
});

const snapshot = queryClient.getQueryData(queryKey);

queryClient.setQueryData<TInferredQueryFnData>(queryKey, (old) => {
return updater(old, variables);
});

return () => {
queryClient.setQueryData(queryKey, snapshot);
};
},
onError(_error, _variables, rollback) {
rollback?.();
},
onSettled() {
return invalidate();
},
});
};
12 changes: 0 additions & 12 deletions src/hooks/useSetGlobalCustomProperty.ts

This file was deleted.

11 changes: 11 additions & 0 deletions src/hooks/useSetRootCustomProperty.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import React from 'react';

export const useSetRootCustomProperty = (name: string, value?: string) => {
React.useEffect(() => {
if (value) {
document.documentElement.style.setProperty(name, value);
} else {
document.documentElement.style.removeProperty(name);
}
}, [name, value]);
};
6 changes: 5 additions & 1 deletion src/main.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import React from 'react';
import { queryClient } from '@/tanstackQuery/@queryClient';
import { configContext as SvgUseConfigContext } from '@svg-use/react';
import { QueryClientProvider } from '@tanstack/react-query';
import { createRouter, RouterProvider } from '@tanstack/react-router';
import ReactDOM from 'react-dom/client';
import type { Config } from '@svg-use/react';
Expand All @@ -25,7 +27,9 @@ const config: Config = {
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
<React.StrictMode>
<SvgUseConfigContext.Provider value={config}>
<RouterProvider router={router} />
<QueryClientProvider client={queryClient}>
<RouterProvider router={router} />
</QueryClientProvider>
</SvgUseConfigContext.Provider>
</React.StrictMode>
);
2 changes: 0 additions & 2 deletions src/modules/Home/components/Counter/index.ts

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import { memo, useCallback, useState } from 'react';
import React from 'react';
import clsx from 'clsx';
import s from './Counter.module.css';
import s from './style.module.css';

const Counter: React.FC = () => {
const [count, setCount] = useState(0);
const [count, setCount] = React.useState(0);

const increment = useCallback(() => {
const increment = React.useCallback(() => {
setCount((prev) => {
return prev + 1;
});
}, []);

const decrement = useCallback(() => {
const decrement = React.useCallback(() => {
setCount((prev) => {
return prev - 1;
});
Expand All @@ -34,4 +34,4 @@ const Counter: React.FC = () => {
);
};

export default memo(Counter);
export default React.memo(Counter);
2 changes: 1 addition & 1 deletion src/modules/Home/constants.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export const CURRENT_YEAR = new Date().getFullYear();
export const EXAMPLE = 'Example';
1 change: 1 addition & 0 deletions src/modules/Home/hooks/useExampleHook.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const useExampleHook = () => {};
2 changes: 0 additions & 2 deletions src/modules/Home/index.ts

This file was deleted.

11 changes: 5 additions & 6 deletions src/modules/Home/Home.tsx → src/modules/Home/index.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import { memo } from 'react';
import clsx from 'clsx';
import { CURRENT_YEAR } from './constants';
import type { HomeExampleProps } from './types';
import Counter from './components/Counter';
import { Component as PhenomenonMarkIcon } from '@/icons/phenomenon-mark.svg?svgUse';
import s from './Home.module.css';
import s from './style.module.css';

const Home: React.FC = () => {
const Home: React.FC<HomeExampleProps> = () => {
return (
<main className={clsx(s.wrap, 'full-height')}>
<div className={s.inner}>
Expand All @@ -24,9 +23,9 @@ const Home: React.FC = () => {
</p>
</section>
</div>
<footer className={s.footer}>&copy;&nbsp;{CURRENT_YEAR}, Phenomenon.studio</footer>
<footer className={s.footer}>&copy;&nbsp;{new Date().getFullYear()}, Phenomenon.studio</footer>
</main>
);
};

export default memo(Home);
export default Home;
8 changes: 8 additions & 0 deletions src/modules/Home/schemas/exampleSchema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { z } from 'zod';

export type ExampleSchema = z.infer<typeof exampleSchema>;

export const exampleSchema = z.object({
name: z.string(),
type: z.string(),
});
File renamed without changes.
4 changes: 4 additions & 0 deletions src/modules/Home/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export type HomeExampleProps = {
id?: number;
name?: string;
};
1 change: 1 addition & 0 deletions src/modules/Home/utils/exampleUtil.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const exampleUtil = () => {};
Loading

0 comments on commit dea4402

Please sign in to comment.