diff --git a/package.json b/package.json index 20a3c87..3f7562b 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,11 @@ "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", + "@types/jest": "^29.2.5", + "@types/node": "^18.11.18", + "@types/react": "^18.0.26", + "@types/react-dom": "^18.0.10", + "@types/styled-components": "^5.1.26", "dotenv": "^16.0.3", "firebase": "^9.14.0", "react": "^18.2.0", @@ -22,6 +27,7 @@ "sass": "^1.56.1", "stripe": "^11.5.0", "styled-components": "^5.3.6", + "typescript": "^4.9.4", "web-vitals": "^2.1.4" }, "scripts": { diff --git a/src/App.js b/src/App.js index 4cfbbf5..5e26daf 100644 --- a/src/App.js +++ b/src/App.js @@ -14,9 +14,14 @@ const App = ()=> { useEffect(() => { const unsubscribe = onAuthStateChangeListener((user)=>{ + const createUser = async (user) => { + const userSnapshot = await createUserDocumentFromAuth(user); + return userSnapshot; + }; if (user) { - const userSnapshot = createUserDocumentFromAuth(user); - dispatch(setUser(user)); // TODO: should be userSnapshot! + const userSnapshot = createUser(user); + console.log(userSnapshot); + dispatch(setUser(user)); // TODO: test and use userSnapshot! } }); diff --git a/src/components/button/button.component.jsx b/src/components/button/button.component.jsx deleted file mode 100644 index a2dca5e..0000000 --- a/src/components/button/button.component.jsx +++ /dev/null @@ -1,14 +0,0 @@ -import { BaseButton, GoogleSignInButton, InvertedButton, SpinnerButton } from './button.sytles' - -const getButton = (buttonType = 'base') => ({ - base: BaseButton, - google: GoogleSignInButton, - inverted: InvertedButton, -}[buttonType]) - -const Button = ({ children, buttonType, isLoading, ...otherProps }) => { - const CustomButton = getButton(buttonType); - return {isLoading ? : children}; -}; - -export default Button; \ No newline at end of file diff --git a/src/components/button/button.component.tsx b/src/components/button/button.component.tsx new file mode 100644 index 0000000..e5f024e --- /dev/null +++ b/src/components/button/button.component.tsx @@ -0,0 +1,26 @@ +import { ButtonHTMLAttributes, FC } from 'react'; +import { BaseButton, GoogleSignInButton, InvertedButton, SpinnerButton } from './button.sytles' + +export enum BUTTON_TYPES { + base = 'base', + google = 'google', + inverted = 'inverted', +}; + +const getButton = (buttonType = BUTTON_TYPES.base): typeof BaseButton => ({ + [BUTTON_TYPES.base]: BaseButton, + [BUTTON_TYPES.google]: GoogleSignInButton, + [BUTTON_TYPES.inverted]: InvertedButton, +}[buttonType]) + +export type ButtonProps = { + buttonType?: BUTTON_TYPES, + isLoading?: boolean, +} & ButtonHTMLAttributes; + +const Button: FC = ({ children, buttonType, isLoading, ...otherProps }) => { + const CustomButton = getButton(buttonType); + return {isLoading ? : children}; +}; + +export default Button; \ No newline at end of file diff --git a/src/components/button/button.sytles.jsx b/src/components/button/button.sytles.tsx similarity index 100% rename from src/components/button/button.sytles.jsx rename to src/components/button/button.sytles.tsx diff --git a/src/components/payment-form/payment-form.component.jsx b/src/components/payment-form/payment-form.component.jsx index 073307d..80bb956 100644 --- a/src/components/payment-form/payment-form.component.jsx +++ b/src/components/payment-form/payment-form.component.jsx @@ -1,7 +1,6 @@ import { useState } from 'react'; import { CardElement, useStripe, useElements } from '@stripe/react-stripe-js'; -import Button from '../button/button.component'; -import { PaymentFormContainer, FormContainer } from './payment-form.styles'; +import { PaymentFormContainer, FormContainer, PaymentButton } from './payment-form.styles'; import { useSelector } from 'react-redux'; const PaymentForm = () => { @@ -52,13 +51,13 @@ const PaymentForm = () => {

Credit Card Payment:

- +
) diff --git a/src/components/payment-form/payment-form.styles.jsx b/src/components/payment-form/payment-form.styles.jsx index ed63cff..c9ad222 100644 --- a/src/components/payment-form/payment-form.styles.jsx +++ b/src/components/payment-form/payment-form.styles.jsx @@ -1,4 +1,5 @@ import styled from "styled-components"; +import Button from "../button/button.component"; export const PaymentFormContainer = styled.div` height: 300px; @@ -12,3 +13,8 @@ export const FormContainer = styled.div` height: 100px; min-width: 500px; `; + +export const PaymentButton = styled(Button)` + margin-left: auto; + margin-top: 30px; +` diff --git a/src/components/sign-in-form/sign-in-form.component.jsx b/src/components/sign-in-form/sign-in-form.component.jsx index 62a51b2..1a8d6b9 100644 --- a/src/components/sign-in-form/sign-in-form.component.jsx +++ b/src/components/sign-in-form/sign-in-form.component.jsx @@ -2,7 +2,7 @@ import { useState } from 'react'; import { signInWithGooglePopup } from '../../utils/firebase/firebase.utils'; import { signInAuthUserWithEmailAndPassword } from '../../utils/firebase/firebase.utils'; import FormInput from '../../components/form-input/form-input.component'; -import Button from '../../components/button/button.component'; +import Button from '../button/button.component'; import { SignInContainer, ButtonContainer } from './sign-in-form.styles' const defaultFormFields = { @@ -66,8 +66,8 @@ const SignInForm = ()=> { value={password} /> - - + diff --git a/src/components/sign-up-form/sign-up-form.component.jsx b/src/components/sign-up-form/sign-up-form.component.jsx index f010503..0dfb9f7 100644 --- a/src/components/sign-up-form/sign-up-form.component.jsx +++ b/src/components/sign-up-form/sign-up-form.component.jsx @@ -1,7 +1,7 @@ import { useState } from 'react'; import { createAuthUserWithEmailAndPassword } from '../../utils/firebase/firebase.utils'; import FormInput from '../../components/form-input/form-input.component'; -import Button from '../../components/button/button.component'; +import Button from '../button/button.component'; import { SignUpContainer } from './sign-up-form.styles'; const defaultFormFields = { @@ -73,7 +73,7 @@ const SignUpForm = ()=> { onChange={handleChange} value={confirmPassword} /> - + ) diff --git a/src/store/categories/categories.slice.js b/src/store/categories/categories.slice.ts similarity index 61% rename from src/store/categories/categories.slice.js rename to src/store/categories/categories.slice.ts index 2ac074c..61a71a1 100644 --- a/src/store/categories/categories.slice.js +++ b/src/store/categories/categories.slice.ts @@ -1,6 +1,14 @@ import { createSlice } from "@reduxjs/toolkit"; +import type { PayloadAction } from '@reduxjs/toolkit' +import { RootState } from "../store"; -const InitCategoriesSate = { +interface CategoriesState { + categoriesMap: Object | null, + isLoading: boolean, + error: Error | null, +} + +const InitCategoriesSate: CategoriesState = { categoriesMap: null, isLoading: false, error: null @@ -15,12 +23,12 @@ export const categoriesSlice = createSlice({ state.error = null; }, // getCategoriesSucceeded - setCategories: (state, action) => { + setCategories: (state, action: PayloadAction) => { state.categoriesMap = action.payload; state.isLoading = false; state.error = null; }, - getCategoriesFailed: (state, action) => { + getCategoriesFailed: (state, action: PayloadAction) => { state.isLoading = false; state.error = action.payload; }, @@ -28,4 +36,5 @@ export const categoriesSlice = createSlice({ }); export const { getCategoriesStart, setCategories, getCategoriesFailed } = categoriesSlice.actions; +export const selectCategoriesMap = (state: RootState) => state.categories.categoriesMap; export default categoriesSlice.reducer; \ No newline at end of file diff --git a/src/store/store.js b/src/store/store.ts similarity index 85% rename from src/store/store.js rename to src/store/store.ts index e3a8c66..a69f348 100644 --- a/src/store/store.js +++ b/src/store/store.ts @@ -47,6 +47,11 @@ export const store = configureStore({ devTools: process.env.NODE_ENV !== 'production', // The Redux DevTools Extension is disabled for production }); +// Infer the `RootState` and `AppDispatch` types from the store itself +export type RootState = ReturnType; +// Inferred type: {posts: PostsState, comments: CommentsState, users: UsersState} +export type AppDispatch = typeof store.dispatch; + // then run the saga sagaMiddleware.run(rootSaga) diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..ed63278 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "target": "es5", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "noFallthroughCasesInSwitch": true, + "module": "esnext", + "moduleResolution": "node", + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx" + }, + "include": ["src"] +} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index a956e98..d96890d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2522,7 +2522,7 @@ dependencies: "@types/node" "*" -"@types/hoist-non-react-statics@^3.3.1": +"@types/hoist-non-react-statics@*", "@types/hoist-non-react-statics@^3.3.1": version "3.3.1" resolved "https://registry.npmmirror.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz#1124aafe5118cb591977aeb1ceaaed1070eb039f" integrity sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA== @@ -2569,6 +2569,14 @@ expect "^29.0.0" pretty-format "^29.0.0" +"@types/jest@^29.2.5": + version "29.2.5" + resolved "https://registry.npmmirror.com/@types/jest/-/jest-29.2.5.tgz#c27f41a9d6253f288d1910d3c5f09484a56b73c0" + integrity sha512-H2cSxkKgVmqNHXP7TC2L/WUorrZu8ZigyRywfVzv6EyBlxj39n4C00hjXYQWsbwqgElaj/CiAeSRmk5GoaKTgw== + dependencies: + expect "^29.0.0" + pretty-format "^29.0.0" + "@types/json-schema@*", "@types/json-schema@^7.0.4", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": version "7.0.11" resolved "https://registry.npmmirror.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3" @@ -2599,7 +2607,7 @@ resolved "https://registry.npmmirror.com/@types/node/-/node-18.11.10.tgz#4c64759f3c2343b7e6c4b9caf761c7a3a05cee34" integrity sha512-juG3RWMBOqcOuXC643OAdSA525V44cVgGV6dUDuiFtss+8Fk5x1hI93Rsld43VeJVIeqlP9I7Fn9/qaVqoEAuQ== -"@types/node@>=8.1.0": +"@types/node@>=8.1.0", "@types/node@^18.11.18": version "18.11.18" resolved "https://registry.npmmirror.com/@types/node/-/node-18.11.18.tgz#8dfb97f0da23c2293e554c5a50d61ef134d7697f" integrity sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA== @@ -2641,6 +2649,13 @@ dependencies: "@types/react" "*" +"@types/react-dom@^18.0.10": + version "18.0.10" + resolved "https://registry.npmmirror.com/@types/react-dom/-/react-dom-18.0.10.tgz#3b66dec56aa0f16a6cc26da9e9ca96c35c0b4352" + integrity sha512-E42GW/JA4Qv15wQdqJq8DL4JhNpB3prJgjgapN3qJT9K2zO5IIAQh4VXvCEDupoqAwnz0cY4RlXeC/ajX5SFHg== + dependencies: + "@types/react" "*" + "@types/react@*": version "18.0.25" resolved "https://registry.npmmirror.com/@types/react/-/react-18.0.25.tgz#8b1dcd7e56fe7315535a4af25435e0bb55c8ae44" @@ -2650,6 +2665,15 @@ "@types/scheduler" "*" csstype "^3.0.2" +"@types/react@^18.0.26": + version "18.0.26" + resolved "https://registry.npmmirror.com/@types/react/-/react-18.0.26.tgz#8ad59fc01fef8eaf5c74f4ea392621749f0b7917" + integrity sha512-hCR3PJQsAIXyxhTNSiDFY//LhnMZWpNNr5etoCqx/iUfGc5gXWtQR2Phl908jVR6uPXacojQWTg4qRpkxTuGug== + dependencies: + "@types/prop-types" "*" + "@types/scheduler" "*" + csstype "^3.0.2" + "@types/resolve@1.17.1": version "1.17.1" resolved "https://registry.npmmirror.com/@types/resolve/-/resolve-1.17.1.tgz#3afd6ad8967c77e4376c598a82ddd58f46ec45d6" @@ -2699,6 +2723,15 @@ resolved "https://registry.npmmirror.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c" integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw== +"@types/styled-components@^5.1.26": + version "5.1.26" + resolved "https://registry.npmmirror.com/@types/styled-components/-/styled-components-5.1.26.tgz#5627e6812ee96d755028a98dae61d28e57c233af" + integrity sha512-KuKJ9Z6xb93uJiIyxo/+ksS7yLjS1KzG6iv5i78dhVg/X3u5t1H7juRWqVmodIdz6wGVaIApo1u01kmFRdJHVw== + dependencies: + "@types/hoist-non-react-statics" "*" + "@types/react" "*" + csstype "^3.0.2" + "@types/testing-library__jest-dom@^5.9.1": version "5.14.5" resolved "https://registry.npmmirror.com/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.5.tgz#d113709c90b3c75fdb127ec338dad7d5f86c974f" @@ -9470,6 +9503,11 @@ typescript-tuple@^2.2.1: dependencies: typescript-compare "^0.0.2" +typescript@^4.9.4: + version "4.9.4" + resolved "https://registry.npmmirror.com/typescript/-/typescript-4.9.4.tgz#a2a3d2756c079abda241d75f149df9d561091e78" + integrity sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg== + unbox-primitive@^1.0.2: version "1.0.2" resolved "https://registry.npmmirror.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e"