diff --git a/.eslintrc.js b/.eslintrc.js
index cfae62de7..aeb79e7e2 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -1,19 +1,12 @@
module.exports = {
- extends: ['@mate-academy/eslint-config-react', 'plugin:cypress/recommended'],
+ extends: ['@mate-academy/eslint-config-react-typescript', 'plugin:cypress/recommended'],
rules: {
- 'import/no-extraneous-dependencies': ['error', {
- devDependencies: true,
- optionalDependencies: false,
- peerDependencies: false,
- }],
- 'react/prop-types': 0,
'max-len': ['error', {
ignoreTemplateLiterals: true,
ignoreComments: true,
}],
- 'jsx-a11y/label-has-associated-control': ['error', {
- assert: 'either',
+ 'jsx-a11y/label-has-associated-control': ["error", {
+ assert: "either",
}],
- 'jsx-a11y/control-has-associated-label': 'off',
},
};
diff --git a/node_modules/.cache/.eslintcache b/node_modules/.cache/.eslintcache
index 4ac4a9631..b665127b5 100644
--- a/node_modules/.cache/.eslintcache
+++ b/node_modules/.cache/.eslintcache
@@ -1 +1 @@
-[{"/Users/alexandraprotyanova/Desktop/node/fe_aug23_product-categories-practice/src/index.jsx":"1","/Users/alexandraprotyanova/Desktop/node/fe_aug23_product-categories-practice/src/App.jsx":"2","/Users/alexandraprotyanova/Desktop/node/fe_aug23_product-categories-practice/src/api/products.js":"3","/Users/alexandraprotyanova/Desktop/node/fe_aug23_product-categories-practice/src/api/users.js":"4","/Users/alexandraprotyanova/Desktop/node/fe_aug23_product-categories-practice/src/api/categories.js":"5"},{"size":241,"mtime":1697387370538,"results":"6","hashOfConfig":"7"},{"size":9528,"mtime":1697447666398,"results":"8","hashOfConfig":"7"},{"size":542,"mtime":1697387370538,"results":"9","hashOfConfig":"7"},{"size":167,"mtime":1697387370538,"results":"10","hashOfConfig":"7"},{"size":402,"mtime":1697387370538,"results":"11","hashOfConfig":"7"},{"filePath":"12","messages":"13","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},"5bxbhg",{"filePath":"14","messages":"15","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"16","messages":"17","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"18"},{"filePath":"19","messages":"20","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"18"},{"filePath":"21","messages":"22","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"18"},"/Users/alexandraprotyanova/Desktop/node/fe_aug23_product-categories-practice/src/index.jsx",[],"/Users/alexandraprotyanova/Desktop/node/fe_aug23_product-categories-practice/src/App.jsx",[],"/Users/alexandraprotyanova/Desktop/node/fe_aug23_product-categories-practice/src/api/products.js",[],["23","24","25","26","27","28","29","30","31","32"],"/Users/alexandraprotyanova/Desktop/node/fe_aug23_product-categories-practice/src/api/users.js",[],"/Users/alexandraprotyanova/Desktop/node/fe_aug23_product-categories-practice/src/api/categories.js",[],{"ruleId":"33","replacedBy":"34"},{"ruleId":"35","replacedBy":"36"},{"ruleId":"37","replacedBy":"38"},{"ruleId":"39","replacedBy":"40"},{"ruleId":"41","replacedBy":"42"},{"ruleId":"43","replacedBy":"44"},{"ruleId":"45","replacedBy":"46"},{"ruleId":"47","replacedBy":"48"},{"ruleId":"49","replacedBy":"50"},{"ruleId":"51","replacedBy":"52"},"jsx-a11y/label-has-for",[],"handle-callback-err",[],"no-negated-in-lhs",["53"],"no-new-require",[],"no-path-concat",[],"jsx-a11y/accessible-emoji",[],"lines-around-directive",["54"],"no-spaced-func",["55"],"global-require",[],"no-buffer-constructor",[],"no-unsafe-negation","padding-line-between-statements","func-call-spacing"]
\ No newline at end of file
+[{"/Users/alexandraprotyanova/Desktop/node/fe_aug23_product-categories-practice/src/index.tsx":"1","/Users/alexandraprotyanova/Desktop/node/fe_aug23_product-categories-practice/src/App.tsx":"2","/Users/alexandraprotyanova/Desktop/node/fe_aug23_product-categories-practice/src/api/products.ts":"3","/Users/alexandraprotyanova/Desktop/node/fe_aug23_product-categories-practice/src/api/categories.ts":"4","/Users/alexandraprotyanova/Desktop/node/fe_aug23_product-categories-practice/src/api/users.ts":"5","/Users/alexandraprotyanova/Desktop/node/fe_aug23_product-categories-practice/src/components/PeopleTable.tsx":"6","/Users/alexandraprotyanova/Desktop/node/fe_aug23_product-categories-practice/src/types/ColumnNames.ts":"7","/Users/alexandraprotyanova/Desktop/node/fe_aug23_product-categories-practice/src/components/Filters.tsx":"8"},{"size":214,"mtime":1697699454625,"results":"9","hashOfConfig":"10"},{"size":3816,"mtime":1697704011624,"results":"11","hashOfConfig":"10"},{"size":561,"mtime":1697700584558,"results":"12","hashOfConfig":"10"},{"size":472,"mtime":1697700440357,"results":"13","hashOfConfig":"10"},{"size":220,"mtime":1697700325393,"results":"14","hashOfConfig":"10"},{"size":2767,"mtime":1697704214187,"results":"15","hashOfConfig":"10"},{"size":107,"mtime":1697702919848,"results":"16","hashOfConfig":"10"},{"size":3986,"mtime":1697703873198,"results":"17","hashOfConfig":"10"},{"filePath":"18","messages":"19","errorCount":1,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":1,"fixableWarningCount":0,"source":"20","usedDeprecatedRules":"21"},"1ayjumf",{"filePath":"22","messages":"23","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"24","messages":"25","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"21"},{"filePath":"26","messages":"27","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"21"},{"filePath":"28","messages":"29","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"21"},{"filePath":"30","messages":"31","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"32","messages":"33","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"21"},{"filePath":"34","messages":"35","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"21"},"/Users/alexandraprotyanova/Desktop/node/fe_aug23_product-categories-practice/src/index.tsx",["36"],"import ReactDOM from 'react-dom';\n\nimport 'bulma/css/bulma.css';\nimport '@fortawesome/fontawesome-free/css/all.css';\n\nimport { App } from './App';\n\nReactDOM.render(\n ,\n document.getElementById('root'),\n);\n",["37","38","39","40","41","42","43"],"/Users/alexandraprotyanova/Desktop/node/fe_aug23_product-categories-practice/src/App.tsx",[],"/Users/alexandraprotyanova/Desktop/node/fe_aug23_product-categories-practice/src/api/products.ts",[],"/Users/alexandraprotyanova/Desktop/node/fe_aug23_product-categories-practice/src/api/categories.ts",[],"/Users/alexandraprotyanova/Desktop/node/fe_aug23_product-categories-practice/src/api/users.ts",[],"/Users/alexandraprotyanova/Desktop/node/fe_aug23_product-categories-practice/src/components/PeopleTable.tsx",[],"/Users/alexandraprotyanova/Desktop/node/fe_aug23_product-categories-practice/src/types/ColumnNames.ts",[],"/Users/alexandraprotyanova/Desktop/node/fe_aug23_product-categories-practice/src/components/Filters.tsx",[],{"ruleId":"44","severity":2,"message":"45","line":1,"column":1,"nodeType":"46","messageId":"47","endLine":2,"endColumn":1,"fix":"48"},{"ruleId":"49","replacedBy":"50"},{"ruleId":"51","replacedBy":"52"},{"ruleId":"53","replacedBy":"54"},{"ruleId":"55","replacedBy":"56"},{"ruleId":"57","replacedBy":"58"},{"ruleId":"59","replacedBy":"60"},{"ruleId":"61","replacedBy":"62"},"no-multiple-empty-lines","Too many blank lines at the beginning of file. Max of 0 allowed.","Program","blankBeginningOfFile",{"range":"63","text":"64"},"jsx-a11y/label-has-for",[],"lines-around-directive",["65"],"no-spaced-func",["66"],"global-require",[],"no-buffer-constructor",[],"no-new-require",[],"no-path-concat",[],[0,1],"","padding-line-between-statements","func-call-spacing"]
\ No newline at end of file
diff --git a/package.json b/package.json
index 54cbfb148..58f4f8769 100644
--- a/package.json
+++ b/package.json
@@ -62,7 +62,7 @@
},
"mateAcademy": {
"_comment": "Replace 'reactTypescript' with 'react' if you want use React without Typescript",
- "projectType": "react",
+ "projectType": "reactTypescript",
"tests": {
"_comment": "Add `cypressComponents: true` to enable component tests",
"cypress": true
diff --git a/src/App.jsx b/src/App.jsx
deleted file mode 100644
index 99d0c9ad1..000000000
--- a/src/App.jsx
+++ /dev/null
@@ -1,341 +0,0 @@
-/* eslint-disable jsx-a11y/accessible-emoji */
-import React, { useState } from 'react';
-import './App.scss';
-
-import cn from 'classnames';
-import usersFromServer from './api/users';
-import categoriesFromServer from './api/categories';
-import productsFromServer from './api/products';
-
-const products = productsFromServer.map((product) => {
- const category = categoriesFromServer.find(c => c.id === product.categoryId)
- || null;
- const user = usersFromServer.find(u => u.id === category.ownerId)
- || null;
-
- return {
- ...product,
- category,
- user,
- };
-});
-
-const columns = [
- 'ID',
- 'Product',
- 'Category',
- 'User',
-];
-
-const getVisibleProducts = ({
- goods,
- selectedUserId,
- query,
- selectedCategoriesIds,
- sortBy,
- isReversed,
-}) => {
- let visibleProducts = [...goods];
-
- if (selectedUserId) {
- visibleProducts = visibleProducts.filter(product => (
- product.user.id === selectedUserId
- ));
- }
-
- if (query) {
- const normalizedQuery = query.trim().toLowerCase();
-
- visibleProducts = visibleProducts.filter(product => (
- product.name.toLowerCase().includes(normalizedQuery)
- ));
- }
-
- if (selectedCategoriesIds.length > 0) {
- visibleProducts = visibleProducts.filter(product => (
- selectedCategoriesIds.includes(product.categoryId)
- ));
- }
-
- if (sortBy) {
- visibleProducts.sort((a, b) => {
- switch (sortBy) {
- case 'ID':
- return a.id - b.id;
-
- case 'Product':
- return a.name.localeCompare(b.name);
-
- case 'Category':
- return a.category.title.localeCompare(b.category.title);
-
- case 'User':
- return a.user.name.localeCompare(b.user.name);
-
- default:
- return 0;
- }
- });
-
- if (isReversed) {
- visibleProducts.reverse();
- }
- }
-
- return visibleProducts;
-};
-
-export const App = () => {
- const [selectedUserId, setSelectedUserId] = useState(0);
- const [query, setQuery] = useState('');
- const [selectedCategoriesIds, setSelectedCategoriesIds] = useState([]);
- const [sortBy, setSortBy] = useState('');
- const [isReversed, setIsReversed] = useState(false);
-
- function toggleCategories(categoryId) {
- if (selectedCategoriesIds.includes(categoryId)) {
- setSelectedCategoriesIds(selectedCategoriesIds.filter(id => (
- id !== categoryId
- )));
- } else {
- setSelectedCategoriesIds([...selectedCategoriesIds, categoryId]);
- }
- }
-
- function resetAll() {
- if (selectedUserId) {
- setSelectedUserId(0);
- }
-
- if (query) {
- setQuery('');
- }
-
- if (selectedCategoriesIds.length) {
- setSelectedCategoriesIds([]);
- }
- }
-
- function toggleSortBy(newColumnName) {
- const firstClick = sortBy !== newColumnName;
- const secondClick = sortBy === newColumnName && isReversed === false;
- const thirdClick = sortBy === newColumnName && isReversed === true;
-
- if (firstClick) {
- setSortBy(newColumnName);
- setIsReversed(false);
- }
-
- if (secondClick) {
- setIsReversed(true);
- }
-
- if (thirdClick) {
- setSortBy('');
- setIsReversed(false);
- }
- }
-
- const visibleProducts = getVisibleProducts({
- goods: products,
- selectedUserId,
- query,
- selectedCategoriesIds,
- sortBy,
- isReversed,
- });
-
- return (
-
-
-
Product Categories
-
-
-
-
- {visibleProducts.length > 0 ? (
-
-
-
- {columns.map(columnName => (
-
-
- {columnName}
-
- {
- toggleSortBy(columnName);
- }}
- >
-
-
-
-
-
- |
- ))}
-
-
-
-
- {visibleProducts.map(({ user, category, ...product }) => (
-
-
- {product.id}
- |
-
- {product.name} |
-
- {`${category.icon} - ${category.title}`}
- |
-
-
- {user.name}
- |
-
- ))}
-
-
- ) : (
-
- No products matching selected criteria
-
- )}
-
-
-
- );
-};
diff --git a/src/App.tsx b/src/App.tsx
new file mode 100644
index 000000000..f6c1e816e
--- /dev/null
+++ b/src/App.tsx
@@ -0,0 +1,153 @@
+/* eslint-disable jsx-a11y/accessible-emoji */
+import React, { useState } from 'react';
+import './App.scss';
+
+import { users } from './api/users';
+import { categories } from './api/categories';
+import { productsFromServer } from './api/products';
+import { Product } from './types/Product';
+import { ColumnNames } from './types/ColumnNames';
+import { PeopleTable } from './components/PeopleTable';
+import { Filters } from './components/Filters';
+
+const products: Product[] = productsFromServer.map((product) => {
+ const category = categories.find(c => c.id === product.categoryId)
+ || null;
+ const user = users.find(u => u.id === category?.ownerId)
+ || null;
+
+ return {
+ ...product,
+ category,
+ user,
+ };
+});
+
+interface ProductsFilterOptions {
+ goods: Product[],
+ selectedUserId: number,
+ query: string,
+ selectedCategoriesIds: number[],
+ sortBy?: ColumnNames,
+ isReversed: boolean,
+}
+
+const getVisibleProducts = ({
+ goods,
+ selectedUserId,
+ query,
+ selectedCategoriesIds,
+ sortBy,
+ isReversed,
+}: ProductsFilterOptions): Product[] => {
+ let visibleProducts = [...goods];
+
+ if (selectedUserId) {
+ visibleProducts = visibleProducts.filter(product => (
+ product.user?.id === selectedUserId
+ ));
+ }
+
+ if (query) {
+ const normalizedQuery = query.trim().toLowerCase();
+
+ visibleProducts = visibleProducts.filter(product => (
+ product.name.toLowerCase().includes(normalizedQuery)
+ ));
+ }
+
+ if (selectedCategoriesIds.length > 0) {
+ visibleProducts = visibleProducts.filter(product => (
+ selectedCategoriesIds.includes(product.categoryId)
+ ));
+ }
+
+ if (sortBy) {
+ visibleProducts.sort((a, b) => {
+ switch (sortBy) {
+ case ColumnNames.ID:
+ return a.id - b.id;
+
+ case ColumnNames.Products:
+ return a.name.localeCompare(b.name);
+
+ case ColumnNames.Category:
+ if (!a.category || !b.category) {
+ return 0;
+ }
+
+ return a.category.title.localeCompare(b.category.title);
+
+ case ColumnNames.User:
+ if (!a.user || !b.user) {
+ return 0;
+ }
+
+ return a.user.name.localeCompare(b.user.name);
+
+ default:
+ return 0;
+ }
+ });
+
+ if (isReversed) {
+ visibleProducts.reverse();
+ }
+ }
+
+ return visibleProducts;
+};
+
+export const App: React.FC = () => {
+ const [selectedUserId, setSelectedUserId] = useState(0);
+ const [query, setQuery] = useState('');
+ const [selectedCategoriesIds, setSelectedCategoriesIds] = useState<
+ number[]
+ >([]);
+ const [sortBy, setSortBy] = useState();
+ const [isReversed, setIsReversed] = useState(false);
+
+ const visibleProducts = getVisibleProducts({
+ goods: products,
+ selectedUserId,
+ query,
+ selectedCategoriesIds,
+ sortBy,
+ isReversed,
+ });
+
+ return (
+
+
+
Product Categories
+
+
+
+
+
+
+ {visibleProducts.length > 0 ? (
+
+ ) : (
+
+ No products matching selected criteria
+
+ )}
+
+
+
+ );
+};
diff --git a/src/api/categories.js b/src/api/categories.ts
similarity index 81%
rename from src/api/categories.js
rename to src/api/categories.ts
index 7ab6dd096..31d8abf64 100644
--- a/src/api/categories.js
+++ b/src/api/categories.ts
@@ -1,4 +1,6 @@
-export default [
+import { Category } from '../types/Category';
+
+export const categories: Category[] = [
{
id: 1,
title: 'Grocery',
diff --git a/src/api/products.js b/src/api/products.ts
similarity index 93%
rename from src/api/products.js
rename to src/api/products.ts
index 95036fe6b..6cfa4b1f0 100644
--- a/src/api/products.js
+++ b/src/api/products.ts
@@ -1,4 +1,4 @@
-export default [
+export const productsFromServer = [
{
id: 1,
name: 'Milk',
diff --git a/src/api/users.js b/src/api/users.ts
similarity index 68%
rename from src/api/users.js
rename to src/api/users.ts
index 018ef3442..8dfb3bda3 100644
--- a/src/api/users.js
+++ b/src/api/users.ts
@@ -1,4 +1,6 @@
-export default [
+import { User } from '../types/User';
+
+export const users: User[] = [
{ id: 1, name: 'Roma', sex: 'm' },
{ id: 2, name: 'Anna', sex: 'f' },
{ id: 3, name: 'Max', sex: 'm' },
diff --git a/src/components/Filters.tsx b/src/components/Filters.tsx
new file mode 100644
index 000000000..e7132efbc
--- /dev/null
+++ b/src/components/Filters.tsx
@@ -0,0 +1,162 @@
+import React from 'react';
+import cn from 'classnames';
+import { categories } from '../api/categories';
+import { users } from '../api/users';
+
+type Props = {
+ selectedUserId: number;
+ setSelectedUserId: (value: number) => void;
+ query: string;
+ setQuery: (value: string) => void;
+ selectedCategoriesIds: number[];
+ setSelectedCategoriesIds: (value: number[]) => void;
+};
+
+export const Filters: React.FC = ({
+ selectedUserId,
+ setSelectedUserId,
+ query,
+ setQuery,
+ selectedCategoriesIds,
+ setSelectedCategoriesIds,
+}) => {
+ function toggleCategories(categoryId: number) {
+ if (selectedCategoriesIds.includes(categoryId)) {
+ setSelectedCategoriesIds(selectedCategoriesIds.filter(id => (
+ id !== categoryId
+ )));
+ } else {
+ setSelectedCategoriesIds([...selectedCategoriesIds, categoryId]);
+ }
+ }
+
+ function resetAll() {
+ if (selectedUserId) {
+ setSelectedUserId(0);
+ }
+
+ if (query) {
+ setQuery('');
+ }
+
+ if (selectedCategoriesIds.length) {
+ setSelectedCategoriesIds([]);
+ }
+ }
+
+ return (
+
+ );
+};
diff --git a/src/components/PeopleTable.tsx b/src/components/PeopleTable.tsx
new file mode 100644
index 000000000..e5014697b
--- /dev/null
+++ b/src/components/PeopleTable.tsx
@@ -0,0 +1,104 @@
+import React from 'react';
+import cn from 'classnames';
+import { ColumnNames } from '../types/ColumnNames';
+import { Product } from '../types/Product';
+
+type Props = {
+ products: Product[];
+ sortBy?: ColumnNames;
+ isReversed: boolean;
+ setSortBy: (value?: ColumnNames) => void;
+ setIsReversed: (value: boolean) => void;
+};
+
+export const PeopleTable: React.FC = ({
+ products,
+ sortBy,
+ isReversed,
+ setSortBy,
+ setIsReversed,
+}) => {
+ function toggleSortBy(newColumnName: ColumnNames) {
+ const firstClick = sortBy !== newColumnName;
+ const secondClick = sortBy === newColumnName && isReversed === false;
+ const thirdClick = sortBy === newColumnName && isReversed === true;
+
+ if (firstClick) {
+ setSortBy(newColumnName);
+ setIsReversed(false);
+ }
+
+ if (secondClick) {
+ setIsReversed(true);
+ }
+
+ if (thirdClick) {
+ setSortBy(undefined);
+ setIsReversed(false);
+ }
+ }
+
+ return (
+
+
+
+ {Object.values(ColumnNames).map(columnName => (
+
+
+ {columnName}
+
+ {
+ toggleSortBy(columnName);
+ }}
+ >
+
+
+
+
+
+ |
+ ))}
+
+
+
+
+ {products.map(({ user, category, ...product }) => (
+
+
+ {product.id}
+ |
+
+ {product.name} |
+
+ {`${category?.icon} - ${category?.title}`}
+ |
+
+
+ {user?.name}
+ |
+
+ ))}
+
+
+ );
+};
diff --git a/src/index.jsx b/src/index.tsx
similarity index 88%
rename from src/index.jsx
rename to src/index.tsx
index 7fd85bb41..029503464 100644
--- a/src/index.jsx
+++ b/src/index.tsx
@@ -1,4 +1,3 @@
-import React from 'react';
import ReactDOM from 'react-dom';
import 'bulma/css/bulma.css';
diff --git a/src/react-app-env.d.ts b/src/react-app-env.d.ts
new file mode 100644
index 000000000..ebd6da4cc
--- /dev/null
+++ b/src/react-app-env.d.ts
@@ -0,0 +1,3 @@
+///
+
+
diff --git a/src/types/Category.ts b/src/types/Category.ts
new file mode 100644
index 000000000..728117b67
--- /dev/null
+++ b/src/types/Category.ts
@@ -0,0 +1,6 @@
+export interface Category {
+ id: number,
+ title: string,
+ icon: string,
+ ownerId: number,
+}
diff --git a/src/types/ColumnNames.ts b/src/types/ColumnNames.ts
new file mode 100644
index 000000000..ca13817c4
--- /dev/null
+++ b/src/types/ColumnNames.ts
@@ -0,0 +1,6 @@
+export enum ColumnNames {
+ ID = 'ID',
+ Products = 'Product',
+ Category = 'Category',
+ User = 'User',
+}
diff --git a/src/types/Product.ts b/src/types/Product.ts
new file mode 100644
index 000000000..c5dea8cdb
--- /dev/null
+++ b/src/types/Product.ts
@@ -0,0 +1,10 @@
+import { Category } from './Category';
+import { User } from './User';
+
+export interface Product {
+ id: number,
+ name: string,
+ categoryId: number,
+ category: Category | null,
+ user: User | null,
+}
diff --git a/src/types/User.ts b/src/types/User.ts
new file mode 100644
index 000000000..2ef95ad66
--- /dev/null
+++ b/src/types/User.ts
@@ -0,0 +1,5 @@
+export interface User {
+ id: number,
+ name: string,
+ sex: 'm' | 'f',
+}
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 000000000..95c932e51
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,6 @@
+{
+ "extends": "@mate-academy/students-ts-config",
+ "include": [
+ "src"
+ ]
+}