diff --git a/.eslintrc.cjs b/.eslintrc.cjs index ecbf2c6..3ceb2e5 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -11,5 +11,6 @@ module.exports = { plugins: ['react-refresh'], rules: { 'react-refresh/only-export-components': 'warn', + 'react/prop-types': 'off' }, } diff --git a/index.html b/index.html index 79c4701..51dedc8 100644 --- a/index.html +++ b/index.html @@ -4,10 +4,13 @@ + + + Vite + React
- + diff --git a/package.json b/package.json index 8e3cf39..1adf2b6 100644 --- a/package.json +++ b/package.json @@ -1,17 +1,21 @@ { - "name": "temp", + "name": "react-posts-app", "private": true, "version": "0.0.0", "type": "module", "scripts": { "dev": "vite", - "build": "vite build", - "lint": "eslint src --ext js,jsx --report-unused-disable-directives --max-warnings 0", + "build": "tsc && vite build", + "lint": "eslint src --ext ts,tsx --report-unused-disable-directives --max-warnings 0", "preview": "vite preview" }, "dependencies": { + "axios": "^1.4.0", + "axios-hooks": "^4.0.0", "react": "^18.2.0", - "react-dom": "^18.2.0" + "react-dom": "^18.2.0", + "react-router-dom": "^6.14.1", + "swr": "^2.2.0" }, "devDependencies": { "@types/react": "^18.0.37", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index fcc6e4c..0b3af5b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5,12 +5,24 @@ settings: excludeLinksFromLockfile: false dependencies: + axios: + specifier: ^1.4.0 + version: 1.4.0 + axios-hooks: + specifier: ^4.0.0 + version: 4.0.0(axios@1.4.0)(react@18.2.0) react: specifier: ^18.2.0 version: 18.2.0 react-dom: specifier: ^18.2.0 version: 18.2.0(react@18.2.0) + react-router-dom: + specifier: ^6.14.1 + version: 6.14.1(react-dom@18.2.0)(react@18.2.0) + swr: + specifier: ^2.2.0 + version: 2.2.0(react@18.2.0) devDependencies: '@types/react': @@ -45,6 +57,13 @@ packages: engines: {node: '>=0.10.0'} dev: true + /@babel/runtime@7.19.0: + resolution: {integrity: sha512-eR8Lo9hnDS7tqkO7NsV+mKvCmv5boaXFSZ70DnfhcgiEne8hv9oCEd36Klw74EtizEqLsy4YnW8UWwpBVolHZA==} + engines: {node: '>=6.9.0'} + dependencies: + regenerator-runtime: 0.13.11 + dev: false + /@esbuild/android-arm64@0.17.19: resolution: {integrity: sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA==} engines: {node: '>=12'} @@ -321,6 +340,11 @@ packages: fastq: 1.15.0 dev: true + /@remix-run/router@1.7.1: + resolution: {integrity: sha512-bgVQM4ZJ2u2CM8k1ey70o1ePFXsEzYVZoWghh6WjM8p59jQ7HxzbHW4SbnWFG7V9ig9chLawQxDTZ3xzOF8MkQ==} + engines: {node: '>=14'} + dev: false + /@swc/core-darwin-arm64@1.3.67: resolution: {integrity: sha512-zCT2mCkOBVNf5uJDcQ3A9KDoO1OEaGdfjsRTZTo7sejDd9AXLfJg+xgyCBBrK2jNS/uWcT21IvSv3LqKp4K8pA==} engines: {node: '>=10'} @@ -553,11 +577,38 @@ packages: get-intrinsic: 1.2.1 dev: true + /asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + dev: false + /available-typed-arrays@1.0.5: resolution: {integrity: sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==} engines: {node: '>= 0.4'} dev: true + /axios-hooks@4.0.0(axios@1.4.0)(react@18.2.0): + resolution: {integrity: sha512-IYpVCh/3I99KA826+Qtms581EegvlUVzDkhljglo21Ugc6P9lQQVG6q22seZy0ieYuepjS3kvCtzUG7jAX7BTA==} + peerDependencies: + axios: '>=0.24.0' + react: ^16.8.0-0 || ^17.0.0 || ^18.0.0 + dependencies: + '@babel/runtime': 7.19.0 + axios: 1.4.0 + dequal: 2.0.3 + lru-cache: 7.18.3 + react: 18.2.0 + dev: false + + /axios@1.4.0: + resolution: {integrity: sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==} + dependencies: + follow-redirects: 1.15.2 + form-data: 4.0.0 + proxy-from-env: 1.1.0 + transitivePeerDependencies: + - debug + dev: false + /balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} dev: true @@ -600,6 +651,13 @@ packages: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} dev: true + /combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + dependencies: + delayed-stream: 1.0.0 + dev: false + /concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} dev: true @@ -641,6 +699,16 @@ packages: object-keys: 1.1.1 dev: true + /delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + dev: false + + /dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + dev: false + /doctrine@2.1.0: resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} engines: {node: '>=0.10.0'} @@ -935,12 +1003,31 @@ packages: resolution: {integrity: sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==} dev: true + /follow-redirects@1.15.2: + resolution: {integrity: sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + dev: false + /for-each@0.3.3: resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} dependencies: is-callable: 1.2.7 dev: true + /form-data@4.0.0: + resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==} + engines: {node: '>= 6'} + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + mime-types: 2.1.35 + dev: false + /fs.realpath@1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} dev: true @@ -1282,6 +1369,23 @@ packages: dependencies: js-tokens: 4.0.0 + /lru-cache@7.18.3: + resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} + engines: {node: '>=12'} + dev: false + + /mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + dev: false + + /mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + dependencies: + mime-db: 1.52.0 + dev: false + /minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} dependencies: @@ -1444,6 +1548,10 @@ packages: react-is: 16.13.1 dev: true + /proxy-from-env@1.1.0: + resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + dev: false + /punycode@2.3.0: resolution: {integrity: sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==} engines: {node: '>=6'} @@ -1467,6 +1575,29 @@ packages: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} dev: true + /react-router-dom@6.14.1(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-ssF6M5UkQjHK70fgukCJyjlda0Dgono2QGwqGvuk7D+EDGHdacEN3Yke2LTMjkrpHuFwBfDFsEjGVXBDmL+bWw==} + engines: {node: '>=14'} + peerDependencies: + react: '>=16.8' + react-dom: '>=16.8' + dependencies: + '@remix-run/router': 1.7.1 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + react-router: 6.14.1(react@18.2.0) + dev: false + + /react-router@6.14.1(react@18.2.0): + resolution: {integrity: sha512-U4PfgvG55LdvbQjg5Y9QRWyVxIdO1LlpYT7x+tMAxd9/vmiPuJhIwdxZuIQLN/9e3O4KFDHYfR9gzGeYMasW8g==} + engines: {node: '>=14'} + peerDependencies: + react: '>=16.8' + dependencies: + '@remix-run/router': 1.7.1 + react: 18.2.0 + dev: false + /react@18.2.0: resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==} engines: {node: '>=0.10.0'} @@ -1474,6 +1605,10 @@ packages: loose-envify: 1.4.0 dev: false + /regenerator-runtime@0.13.11: + resolution: {integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==} + dev: false + /regexp.prototype.flags@1.5.0: resolution: {integrity: sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==} engines: {node: '>= 0.4'} @@ -1629,6 +1764,15 @@ packages: engines: {node: '>= 0.4'} dev: true + /swr@2.2.0(react@18.2.0): + resolution: {integrity: sha512-AjqHOv2lAhkuUdIiBu9xbuettzAzWXmCEcLONNKJRba87WAefz8Ca9d6ds/SzrPc235n1IxWYdhJ2zF3MNUaoQ==} + peerDependencies: + react: ^16.11.0 || ^17.0.0 || ^18.0.0 + dependencies: + react: 18.2.0 + use-sync-external-store: 1.2.0(react@18.2.0) + dev: false + /text-table@0.2.0: resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} dev: true @@ -1668,6 +1812,14 @@ packages: punycode: 2.3.0 dev: true + /use-sync-external-store@1.2.0(react@18.2.0): + resolution: {integrity: sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + react: 18.2.0 + dev: false + /vite@4.3.9: resolution: {integrity: sha512-qsTNZjO9NoJNW7KnOrgYwczm0WctJ8m/yqYAMAK9Lxt4SoySUfS5S8ia9K7JHpa3KEeMfyF8LoJ3c5NeBJy6pg==} engines: {node: ^14.18.0 || >=16.0.0} diff --git a/public/vite.svg b/public/vite.svg deleted file mode 100644 index e7b8dfb..0000000 --- a/public/vite.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/App.jsx b/src/App.jsx deleted file mode 100644 index 6702051..0000000 --- a/src/App.jsx +++ /dev/null @@ -1,9 +0,0 @@ -function App() { - return ( - <> -

Hello, World!

- - ) -} - -export default App diff --git a/src/App.tsx b/src/App.tsx new file mode 100644 index 0000000..9820d69 --- /dev/null +++ b/src/App.tsx @@ -0,0 +1,57 @@ +import React, { useContext, useEffect, useRef } from "react"; +import { + RouterProvider, + createBrowserRouter, + createRoutesFromElements, + Route, + Outlet, +} from "react-router-dom"; + +import Posts, { CreatePost, SinglePost } from "./components/posts"; +import Nav from "./components/navbar"; +import ThemeProvider, { ThemeContext } from "./ThemeContext"; + +const App: React.FC = () => { + const router = createBrowserRouter( + createRoutesFromElements( + }> + } /> + + } /> + } /> + } /> + + + ) + ); + + return ( + + + + ); +}; + +const Root: React.FC = () => { + const theme = useContext(ThemeContext); + const containerDiv = useRef(null); + + useEffect(() => { + if (theme === "dark") { + containerDiv.current?.classList.add("dark-theme"); + containerDiv.current?.classList.remove("light-theme"); + } else { + containerDiv.current?.classList.add("light-theme"); + containerDiv.current?.classList.remove("dark-theme"); + } + }, [theme]); + + return ( +
+
+ ); +}; + +export default App; diff --git a/src/ThemeContext.tsx b/src/ThemeContext.tsx new file mode 100644 index 0000000..c864820 --- /dev/null +++ b/src/ThemeContext.tsx @@ -0,0 +1,32 @@ +import React, { useState, useContext } from "react"; + +export const ThemeContext = React.createContext("dark"); +export const ThemeToggleContext = + React.createContext(null!); + +interface Props { + children: React.ReactNode; +} + +const ThemeProvider: React.FC = ({ children }) => { + const defaultTheme = useContext(ThemeContext); + const [theme, setTheme] = useState(defaultTheme); + + const handleOnClick = () => { + if (theme === "dark") { + setTheme("light"); + } else { + setTheme("dark"); + } + }; + + return ( + + + {children} + + + ); +}; + +export default ThemeProvider; diff --git a/src/components/navbar/Nav.tsx b/src/components/navbar/Nav.tsx new file mode 100644 index 0000000..a079400 --- /dev/null +++ b/src/components/navbar/Nav.tsx @@ -0,0 +1,16 @@ +import React, { useContext, useEffect, useRef } from "react"; + +import NavTitle from "./NavTitle"; +import NavLinks from "./NavLinks"; +import "./styles/nav.css"; + +const Nav: React.FC = () => { + return ( + + ); +}; + +export default Nav; diff --git a/src/components/navbar/NavLink.tsx b/src/components/navbar/NavLink.tsx new file mode 100644 index 0000000..eb3e6f7 --- /dev/null +++ b/src/components/navbar/NavLink.tsx @@ -0,0 +1,16 @@ +import React from "react"; +import { Link } from "react-router-dom"; + + +interface Props { + title: string, + link: string +} + +const NavLink: React.FC = ({ title, link }) => { + return ( + {title} + ) +} + +export default NavLink; \ No newline at end of file diff --git a/src/components/navbar/NavLinks.tsx b/src/components/navbar/NavLinks.tsx new file mode 100644 index 0000000..88450b3 --- /dev/null +++ b/src/components/navbar/NavLinks.tsx @@ -0,0 +1,19 @@ +import React, { useContext, useRef } from "react"; +import NavLink from "./NavLink"; +import { ThemeToggleContext } from "../../ThemeContext"; + +const NavLinks: React.FC = () => { + const themeBtn = useRef(null); + const toggleTheme = useContext(ThemeToggleContext); + + return ( +
+
toggleTheme!(e)}> + THEME +
+ +
+ ); +}; + +export default NavLinks; diff --git a/src/components/navbar/NavTitle.tsx b/src/components/navbar/NavTitle.tsx new file mode 100644 index 0000000..d7f0254 --- /dev/null +++ b/src/components/navbar/NavTitle.tsx @@ -0,0 +1,16 @@ +import React from "react"; +import { Link } from "react-router-dom"; + +interface Props { + title: string +} + +const NavTitle: React.FC = ({ title }) => { + return ( +
+

{title}

+
+ ) +} + +export default NavTitle; \ No newline at end of file diff --git a/src/components/navbar/index.ts b/src/components/navbar/index.ts new file mode 100644 index 0000000..53d0843 --- /dev/null +++ b/src/components/navbar/index.ts @@ -0,0 +1,3 @@ +import Nav from "./Nav"; + +export default Nav; \ No newline at end of file diff --git a/src/components/navbar/styles/nav.css b/src/components/navbar/styles/nav.css new file mode 100644 index 0000000..e34914e --- /dev/null +++ b/src/components/navbar/styles/nav.css @@ -0,0 +1,37 @@ +.nav { + display: flex; + color: white; + background-color: var(--primary-background-color); + padding: 0 15px; +} + +.nav-title { + font-size: 22px; + padding: 10px; + text-decoration: none; +} + +.nav-links-container { + flex: 1; + display: flex; + justify-content: end; + align-items: center; +} + +.nav-link { + padding: 10px; + font-size: 14px; + font-weight: 500; + border-radius: 5px; + color: var(--primary-color); + border: 1px solid white; + margin: 5px; + text-transform: uppercase; + text-decoration: none; +} + +.nav-link:hover { + color: var(--primary-background-color); + background-color: var(--primary-color); + cursor: pointer; +} diff --git a/src/components/posts/CreatePost.tsx b/src/components/posts/CreatePost.tsx new file mode 100644 index 0000000..7449169 --- /dev/null +++ b/src/components/posts/CreatePost.tsx @@ -0,0 +1,8 @@ +import React from "react"; +import PostForm from "./PostForm"; + +const CreatePost: React.FC = () => { + return ; +}; + +export default CreatePost; diff --git a/src/components/posts/Post.tsx b/src/components/posts/Post.tsx new file mode 100644 index 0000000..de5b3c9 --- /dev/null +++ b/src/components/posts/Post.tsx @@ -0,0 +1,21 @@ +import React from "react"; + +interface Props { + title: string, + body: string +} + +const Post: React.FC = ({ title, body }) => { + return ( +
+
+ {title} +
+
+ {body} +
+
+ ) +} + +export default Post; \ No newline at end of file diff --git a/src/components/posts/PostForm.tsx b/src/components/posts/PostForm.tsx new file mode 100644 index 0000000..364f1cf --- /dev/null +++ b/src/components/posts/PostForm.tsx @@ -0,0 +1,59 @@ +import React, { useState } from "react"; + +import { useCreatePost } from "./utils"; +import "./styles/form.css"; + +const PostForm: React.FC = () => { + const [title, setTitle] = useState(""); + const [body, setBody] = useState(""); + + const executePost = useCreatePost(); + + const handleSubmit = async (e: React.ChangeEvent) => { + e.preventDefault(); + + const newPostData = await executePost({ + data: { + title, + body, + }, + }); + + if (newPostData.status === 201) { + e.target.submit(); + } + }; + + return ( +
+
+
+ + setTitle(e.target.value)} + className="input title-input" + /> +
+
+ + setBody(e.target.value)} + className="input body-input" + /> +
+ + +
+
+ ); +}; + +export default PostForm; diff --git a/src/components/posts/Posts.tsx b/src/components/posts/Posts.tsx new file mode 100644 index 0000000..38f7264 --- /dev/null +++ b/src/components/posts/Posts.tsx @@ -0,0 +1,21 @@ +import React from "react"; +import Post from "./Post"; +import usePosts, { IPosts } from "./utils"; + + +const Posts: React.FC = () => { + const { posts, error, loading } = usePosts(); + const totalPosts = 10; + + if (loading) return
...Loading
+ if (error) return
{error.toString()}
+ return ( +
+ {posts.slice(0, totalPosts).map((post: IPosts) => + + )} +
+ ) +} + +export default Posts; \ No newline at end of file diff --git a/src/components/posts/SinglePost.tsx b/src/components/posts/SinglePost.tsx new file mode 100644 index 0000000..ecaed7a --- /dev/null +++ b/src/components/posts/SinglePost.tsx @@ -0,0 +1,22 @@ +import React from "react" +import { useParams } from "react-router-dom"; + +import Post from "./Post" +import { usePost } from "./utils"; + + + +const SinglePost: React.FC = () => { + const { id } = useParams(); + const { post, error, loading } = usePost(id); + + + if (loading) return
...Loading
+ if (error) return
{error.toString()}
+ return ( + + ) +} + + +export default SinglePost; \ No newline at end of file diff --git a/src/components/posts/index.ts b/src/components/posts/index.ts new file mode 100644 index 0000000..f203da4 --- /dev/null +++ b/src/components/posts/index.ts @@ -0,0 +1,9 @@ +import Posts from "./Posts"; +import Post from "./Post"; +import SinglePost from "./SinglePost"; +import CreatePost from "./CreatePost"; + +import "./styles/posts.css"; + +export default Posts; +export { CreatePost, Post, SinglePost }; \ No newline at end of file diff --git a/src/components/posts/styles/form.css b/src/components/posts/styles/form.css new file mode 100644 index 0000000..78bd4aa --- /dev/null +++ b/src/components/posts/styles/form.css @@ -0,0 +1,43 @@ +.form-container { + display: flex; + height: 80vh; + justify-content: center; + align-items: center; +} + +form { + display: flex; + flex-direction: column; + align-items: center; + width: 500px; +} + +.field-container { + display: flex; + flex-direction: column; + width: 300px; + margin-top: 20px; +} + +label { + font-size: 20px; +} + +.input { + font-size: 16px; + padding: 2px; + margin-top: 3px; + border: 1px solid grey; + border-radius: 3px; +} + +.submit-btn { + width: 300px; + margin-top: 20px; + border: none; + background-color: var(--primary-background-color); + color: white; + font-size: 16px; + text-transform: uppercase; + padding: 7px 0; +} diff --git a/src/components/posts/styles/posts.css b/src/components/posts/styles/posts.css new file mode 100644 index 0000000..183e4b7 --- /dev/null +++ b/src/components/posts/styles/posts.css @@ -0,0 +1,26 @@ + + +.posts { + padding: 20px; + display: flex; + justify-content: center; + flex-wrap: wrap; +} + +.post { + width: 450px; + border-radius: 5px; + padding: 20px; + margin: 20px; + text-align: justify; + box-shadow: rgba(50, 50, 93, 0.25) 0px 2px 5px -1px, rgba(0, 0, 0, 0.3) 0px 1px 3px -1px; +} + +.post-title { + font-size: 20px; + margin-bottom: 20px; +} + +.post-body { + line-height: 1.2; +} \ No newline at end of file diff --git a/src/components/posts/utils.ts b/src/components/posts/utils.ts new file mode 100644 index 0000000..025f0d6 --- /dev/null +++ b/src/components/posts/utils.ts @@ -0,0 +1,45 @@ +import useAxios from "axios-hooks"; + +export interface IPosts { + id?: number; + title: string; + body: string; +} + +export default function usePosts() { + const [{ data, error, loading }] = useAxios( + "https://jsonplaceholder.typicode.com/posts" + ); + + return { + posts: data, + error, + loading, + }; +} + +export function usePost(id: string | undefined) { + const [{ data, error, loading }] = useAxios( + `https://jsonplaceholder.typicode.com/posts/${id}` + ); + + return { + post: data, + error, + loading, + }; +} + +export function useCreatePost() { + const [{ data, error, loading }, executePost] = useAxios( + { + url: "https://jsonplaceholder.typicode.com/posts", + method: "POST", + }, + { + manual: true, + } + ); + + return executePost; +} diff --git a/src/main.css b/src/main.css new file mode 100644 index 0000000..cda8cf7 --- /dev/null +++ b/src/main.css @@ -0,0 +1,21 @@ +* { + box-sizing: border-box; +} + +body { + margin: 0; + padding: 0; + font-family: "Roboto", sans-serif; +} + +:root { + --primary-color: white; +} + +.dark-theme { + --primary-background-color: #1a1e26; +} + +.light-theme { + --primary-background-color: #0078fb; +} diff --git a/src/main.jsx b/src/main.tsx similarity index 61% rename from src/main.jsx rename to src/main.tsx index 51a8c58..76cb65e 100644 --- a/src/main.jsx +++ b/src/main.tsx @@ -1,8 +1,10 @@ import React from 'react' import ReactDOM from 'react-dom/client' import App from './App.jsx' +import "./main.css"; -ReactDOM.createRoot(document.getElementById('root')).render( + +ReactDOM.createRoot(document.getElementById('root') as Element).render( , diff --git a/src/utils/fetcher.ts b/src/utils/fetcher.ts new file mode 100644 index 0000000..f6d41e8 --- /dev/null +++ b/src/utils/fetcher.ts @@ -0,0 +1,3 @@ +import axios from "axios"; + +export default async (url) => await axios.get(url); \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..846f377 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "target": "ESNext", + "useDefineForClassFields": true, + "lib": ["DOM", "DOM.Iterable", "ESNext"], + "allowJs": false, + "skipLibCheck": true, + "esModuleInterop": false, + "allowSyntheticDefaultImports": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "module": "ESNext", + "moduleResolution": "Node", + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx" + }, + "include": ["src"], + "references": [{ "path": "./tsconfig.node.json" }] +} \ No newline at end of file diff --git a/tsconfig.node.json b/tsconfig.node.json new file mode 100644 index 0000000..67c15c6 --- /dev/null +++ b/tsconfig.node.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "composite": true, + "module": "ESNext", + "moduleResolution": "Node", + "allowSyntheticDefaultImports": true, + }, + "include": ["vite.config.ts"] +} \ No newline at end of file diff --git a/vite.config.js b/vite.config.ts similarity index 100% rename from vite.config.js rename to vite.config.ts