diff --git a/.env b/.env new file mode 100644 index 00000000..3d33ff69 --- /dev/null +++ b/.env @@ -0,0 +1,8 @@ +REACT_APP_FIREBASE_API_KEY='AIzaSyCBCFnmsJqYCndQXvmdg9NMfnRAOSRoc5w' +REACT_APP_FIREBASE_AUTH_DOMAIN="blogapp-727ff.firebaseapp.com" +REACT_APP_FIREBASE_PROJECT_ID="blogapp-727ff" +REACT_APP_FIREBASE_STORAGE_BUCKET="blogapp-727ff.appspot.com" +REACT_APP_FIREBASE_MESSAGING_SENDER_ID="292761108323" +REACT_APP_FIREBASE_APP_ID="1:292761108323:web:e386bbdfa372db1f34dd56" +REACT_APP_FIREBASE_MEASUREMENT_IDL="KG-FN7MSVX5T" + diff --git a/.gitignore b/.gitignore index 4d29575d..f21726c7 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,7 @@ .env.development.local .env.test.local .env.production.local +.env npm-debug.log* yarn-debug.log* diff --git a/package.json b/package.json index 8fb0ab69..6c827d5a 100644 --- a/package.json +++ b/package.json @@ -3,11 +3,20 @@ "version": "0.1.0", "private": true, "dependencies": { + "@ant-design/icons": "^4.7.0", "@testing-library/jest-dom": "^5.16.4", "@testing-library/react": "^13.3.0", "@testing-library/user-event": "^13.5.0", + "antd": "^4.22.0", + "crypto-js": "^4.1.1", + "dayjs": "^1.11.4", + "firebase": "^9.9.1", + "jodit-react": "^1.3.13", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-firebase-hooks": "^5.0.3", + "react-multi-carousel": "^2.8.2", + "react-router-dom": "^6.3.0", "react-scripts": "5.0.1", "web-vitals": "^2.1.4" }, diff --git a/public/EdTech-white.png b/public/EdTech-white.png new file mode 100644 index 00000000..3589b0df Binary files /dev/null and b/public/EdTech-white.png differ diff --git a/public/EdTech.png b/public/EdTech.png new file mode 100644 index 00000000..bada5546 Binary files /dev/null and b/public/EdTech.png differ diff --git a/public/_redirects b/public/_redirects new file mode 100644 index 00000000..f8243379 --- /dev/null +++ b/public/_redirects @@ -0,0 +1 @@ +/* /index.html 200 \ No newline at end of file diff --git a/public/favicon.ico b/public/favicon.ico deleted file mode 100644 index a11777cc..00000000 Binary files a/public/favicon.ico and /dev/null differ diff --git a/public/favicon.png b/public/favicon.png new file mode 100644 index 00000000..7cf9c9e8 Binary files /dev/null and b/public/favicon.png differ diff --git a/public/index.html b/public/index.html index aa069f27..78867869 100644 --- a/public/index.html +++ b/public/index.html @@ -2,7 +2,7 @@ - + - React App + EduTech diff --git a/src/App.css b/src/App.css deleted file mode 100644 index 74b5e053..00000000 --- a/src/App.css +++ /dev/null @@ -1,38 +0,0 @@ -.App { - text-align: center; -} - -.App-logo { - height: 40vmin; - pointer-events: none; -} - -@media (prefers-reduced-motion: no-preference) { - .App-logo { - animation: App-logo-spin infinite 20s linear; - } -} - -.App-header { - background-color: #282c34; - min-height: 100vh; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - font-size: calc(10px + 2vmin); - color: white; -} - -.App-link { - color: #61dafb; -} - -@keyframes App-logo-spin { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } -} diff --git a/src/App.js b/src/App.js index 37845757..9f87b9df 100644 --- a/src/App.js +++ b/src/App.js @@ -1,24 +1,31 @@ -import logo from './logo.svg'; -import './App.css'; - -function App() { +import React from 'react'; +import { BrowserRouter, Routes, Route } from 'react-router-dom' +import Beranda from './pages/Beranda'; +import CreateBlog from './pages/CreateBlog'; +import Dashboard from './pages/Dashboard'; +import Login from './pages/Login'; +import Register from './pages/Register'; +import { StateProvider } from './context/StateContext'; +import ProtectedRoute from './utils/ProtectedRoute'; +import DetailPost from './pages/DetailPost'; +import ContactPage from './pages/ContactPage'; +import About from './pages/About'; +const App = () => { return ( -
-
- logo -

- Edit src/App.js and save to reload. -

- - Learn React - -
-
+ + + + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + + + ); } diff --git a/src/App.test.js b/src/App.test.js deleted file mode 100644 index 1f03afee..00000000 --- a/src/App.test.js +++ /dev/null @@ -1,8 +0,0 @@ -import { render, screen } from '@testing-library/react'; -import App from './App'; - -test('renders learn react link', () => { - render(); - const linkElement = screen.getByText(/learn react/i); - expect(linkElement).toBeInTheDocument(); -}); diff --git a/src/assets/css/Login.css b/src/assets/css/Login.css new file mode 100644 index 00000000..85797e28 --- /dev/null +++ b/src/assets/css/Login.css @@ -0,0 +1,83 @@ +@import url("https://fonts.googleapis.com/css?family=Montserrat"); +.login-container { + margin: 0; + padding: 50px; + background: linear-gradient(#f0f0f0, #7e8081); + min-height: 100vh; + display: flex; + justify-content: center; + align-items: center; +} +.login-content { + background: #233142; + font-family: "Montserrat"; + border-radius: 15px; + backdrop-filter: blur(1rem); + box-shadow: 0px 0 17px -7px #e6f7ff; + min-height: 80vh; + margin-left: 50px; + margin-right: 50px; + width: 100%; +} +@media screen and (max-width: 992px) { + .left-content { + margin-top: 100px; + } + .right-content { + margin-top: 50px; + } +} +@media screen and (max-width: 1169px) { + .left-content { + margin: 0; + } + .login-content { + margin-left: 0; + margin-right: 0; + } + .login-subtitle2 { + margin-bottom: 50px; + } +} +.left-content { + background-color: #455d7a; + margin: auto; + max-width: 500px; + min-width: 300px; + border-radius: 20px; +} +.image-login { + max-width: 500px; + min-width: 300px; +} +.right-content { + padding-left: 40px; + padding-right: 60px; +} +.login-title { + font-family: "Iceland"; + font-size: 45px; + font-weight: 500; + color: #e6f7ff; + margin-bottom: 0; +} +.login-line { + margin-top: 0; + margin-left: 0; + background: #e6f7ff; + height: 4px; + width: 40%; +} +.login-subtitle { + font-weight: 400; + color: #e6f7ff; +} +.login-subtitle2 { + margin-top: 25px; + text-align: center; + font-weight: 400; + color: #e6f7ff; +} +.login-form { + margin-top: 20px; +} diff --git a/src/assets/css/Register.css b/src/assets/css/Register.css new file mode 100644 index 00000000..899a94e1 --- /dev/null +++ b/src/assets/css/Register.css @@ -0,0 +1,78 @@ +@import url("https://fonts.googleapis.com/css?family=Montserrat"); +.register-container { + margin: 0; + padding: 50px; + background: linear-gradient(#7e8081, #f0f0f0); + min-height: 100vh; + display: flex; + justify-content: center; + align-items: center; +} +.register-content { + background: #233142; + font-family: "Montserrat"; + border-radius: 15px; + backdrop-filter: blur(1rem); + box-shadow: 0px 0 17px -7px #e6f7ff; + min-height: 80vh; + margin-left: 50px; + margin-right: 50px; + width: 100%; +} +@media screen and (max-width: 992px) { + .image-register { + margin: 20px 0px; + } + .right-content { + margin-top: 50px; + } +} +@media screen and (max-width: 1169px) { + .left-content { + margin: 0; + } + .register-content { + margin-left: 0; + margin-right: 0; + } + .register-subtitle2 { + margin-bottom: 50px; + } +} +.register-left-content { + background-color: #455d7a; + margin: auto; + width: fit-content; + border-radius: 20px; +} +.image-register { + max-width: 500px; + min-width: 300px; +} +.register-right-content { + padding-left: 40px; + padding-right: 60px; +} +.register-title { + font-family: "Iceland"; + font-size: 45px; + font-weight: 500; + color: #e6f7ff; + margin-bottom: 0; +} +.register-line { + margin-top: 0; + margin-left: 0; + background: #e6f7ff; + height: 4px; + width: 40%; +} +.register-subtitle { + font-weight: 400; + color: #e6f7ff; +} +.register-subtitle2 { + text-align: center; + font-weight: 400; + color: #e6f7ff; +} diff --git a/src/assets/css/about.css b/src/assets/css/about.css new file mode 100644 index 00000000..32672717 --- /dev/null +++ b/src/assets/css/about.css @@ -0,0 +1,34 @@ +.container-about { + margin-top: 50px; + margin-bottom: 50px; + display: flex; + justify-content: center; + align-items: center; + flex-direction: column; + min-height: 60vh; +} + +.content-about { + width: 800px; + display: flex; + flex-direction: column; + justify-content: flex-start; + align-items: flex-start; +} +.heading-about { + font-family: "Montserrat"; + font-size: 13px; +} + +.line-about { + height: 3px; + background-color: #000; + width: 10%; + margin-left: 0; +} +.brand-about { + font-family: "Montserrat"; + font-weight: 1000; + font-size: 60px; + margin-top: 20; +} diff --git a/src/assets/css/beranda.css b/src/assets/css/beranda.css new file mode 100644 index 00000000..c0974535 --- /dev/null +++ b/src/assets/css/beranda.css @@ -0,0 +1,18 @@ +.content-heading { + font-size: 30px; + color: #fff; + font-family: "Montserrat"; + font-weight: 600; +} +.content-category-heading { + font-size: 30px; + color: #000; + font-family: "iceland"; + font-weight: 600; +} +.content-article-heading { + font-size: 30px; + color: #000; + font-family: "Montserrat"; + font-weight: 800; +} diff --git a/src/assets/css/contact.css b/src/assets/css/contact.css new file mode 100644 index 00000000..2f1c3800 --- /dev/null +++ b/src/assets/css/contact.css @@ -0,0 +1,15 @@ +.contact-container { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + margin-top: 70px; + margin-bottom: 30px; +} + +.contact-data-company { + font-size: 40px; + color: #000; + font-family: "iceland"; + font-weight: 600; +} diff --git a/src/assets/css/createblog.css b/src/assets/css/createblog.css new file mode 100644 index 00000000..9fb15fd2 --- /dev/null +++ b/src/assets/css/createblog.css @@ -0,0 +1,13 @@ +.container-createblog { + display: flex; + flex-direction: column; +} +.create-blog-title { + margin: 20px; + font-family: "Montserrat"; + font-size: 20px; + font-weight: 600; +} +.createblog-form { + margin-left: 20px; +} diff --git a/src/assets/css/detailpost.css b/src/assets/css/detailpost.css new file mode 100644 index 00000000..8512f464 --- /dev/null +++ b/src/assets/css/detailpost.css @@ -0,0 +1,32 @@ +.container-detail { + display: flex; + flex-direction: column; + justify-content: flex-start; +} + +.content-category-heading-detail { + font-size: 30px; + color: #000; + font-family: "iceland"; + font-weight: 600; +} + +.content-category-heading-detail-time { + font-size: 20px; + color: #000; + font-family: "iceland"; + font-weight: 400; +} +.content-category-heading-detail-title { + font-size: 45px; + color: #000; + font-family: "Montserrat"; + font-weight: 800; + margin-top: 20px; +} +.content-article-heading-detail { + font-size: 30px; + color: #000; + font-family: "Montserrat"; + font-weight: 800; +} diff --git a/src/assets/css/header.css b/src/assets/css/header.css new file mode 100644 index 00000000..fd8ef4ce --- /dev/null +++ b/src/assets/css/header.css @@ -0,0 +1,10 @@ +.header-layout-wrapper { + display: flex; + justify-content: space-between; + align-items: center; +} +.right-layout-header { + display: flex; + align-items: center; + gap: 20px; +} diff --git a/src/assets/css/navbar.css b/src/assets/css/navbar.css new file mode 100644 index 00000000..7f643dd3 --- /dev/null +++ b/src/assets/css/navbar.css @@ -0,0 +1,11 @@ +.header-wrapper { + display: flex; + justify-content: space-around; + align-items: center; +} +.header-mobile-icon { + display: block; + font-size: 1.5rem; + cursor: pointer; + color: #000; +} diff --git a/src/assets/images/avatar.png b/src/assets/images/avatar.png new file mode 100644 index 00000000..ea8c2b29 Binary files /dev/null and b/src/assets/images/avatar.png differ diff --git a/src/assets/images/contact.png b/src/assets/images/contact.png new file mode 100644 index 00000000..5f79c65a Binary files /dev/null and b/src/assets/images/contact.png differ diff --git a/src/assets/images/ilustration1.svg b/src/assets/images/ilustration1.svg new file mode 100644 index 00000000..a92b57c8 --- /dev/null +++ b/src/assets/images/ilustration1.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/images/ilustration2.svg b/src/assets/images/ilustration2.svg new file mode 100644 index 00000000..7bd0ca0c --- /dev/null +++ b/src/assets/images/ilustration2.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/images/login-image.png b/src/assets/images/login-image.png new file mode 100644 index 00000000..a6859674 Binary files /dev/null and b/src/assets/images/login-image.png differ diff --git a/src/components/FooterPage.js b/src/components/FooterPage.js new file mode 100644 index 00000000..8d015900 --- /dev/null +++ b/src/components/FooterPage.js @@ -0,0 +1,12 @@ +import React from 'react'; +import { Layout } from 'antd'; +const { Footer } = Layout; +const FooterPage = () => { + return ( + + ) +} + +export default FooterPage; \ No newline at end of file diff --git a/src/components/Loading.js b/src/components/Loading.js new file mode 100644 index 00000000..23a2ca00 --- /dev/null +++ b/src/components/Loading.js @@ -0,0 +1,34 @@ +import React from 'react'; +import { Space, Spin } from 'antd' + +const Loading = () => { + return ( +
+ + + + Loading + + +
+ ) +} + +export default Loading \ No newline at end of file diff --git a/src/components/Navbar.js b/src/components/Navbar.js new file mode 100644 index 00000000..d7cce930 --- /dev/null +++ b/src/components/Navbar.js @@ -0,0 +1,129 @@ +import React, { useState, useContext } from 'react'; +import { Layout, Image, Menu, Button, Dropdown } from 'antd'; +import { useNavigate } from "react-router-dom"; +import { useAuthState } from "react-firebase-hooks/auth"; +import { signOut } from "firebase/auth"; +import { auth } from "../config/firebase"; +import { MenuOutlined } from "@ant-design/icons"; +import Loading from './Loading'; +import StateContext from '../context/StateContext'; +import "../assets/css/navbar.css"; +const { Header } = Layout + +const Navbar = () => { + const [user, isLoading] = useAuthState(auth); + const { navMenu, setNavMenu } = useContext(StateContext); + const navigate = useNavigate(); + const [mobile, setMobile] = useState(false); + const checkResponsive = () => { + if (window.innerWidth <= 580) { + setMobile(true); + } else { + setMobile(false); + } + }; + + window.addEventListener("resize", checkResponsive); + const handleMenu = (e) => { + switch (e?.key) { + case '1': + setNavMenu(['1']); + navigate("/"); + break; + case '2': + navigate("/about"); + setNavMenu(['2']); + break; + case '3': + setNavMenu(['3']); + navigate("/contact"); + break; + case '4': + handleAuth(); + break; + default: + break; + } + } + const handleAuth = () => { + if (user) { + signOut(auth) + .then(() => { + navigate("/login"); + }) + .catch((error) => { + console.log(error); + }); + } else { + return navigate("/login"); + } + }; + const menu = () => { + return ( +
+ handleMenu(e)} + > + Homepage + About Us + Contact + {user ? 'Logout' : 'Login'} + + +
+ ); + } + return ( + <> + { + isLoading ? ( + + ) : ( + <> +
+ {mobile ? ( +
+ navigate("/")} style={{ cursor: 'pointer' }} preview={false} src="/EdTech.png" width={120} /> + + +
+ +
+
+
+ ) : ( +
+ navigate("/")} style={{ cursor: 'pointer' }} preview={false} src="/EdTech.png" width={120} /> + handleMenu(e)} + > + Homepage + About Us + Contact + + +
+ )} + +
+ + ) + } + + ) +} + +export default Navbar \ No newline at end of file diff --git a/src/config/firebase.js b/src/config/firebase.js new file mode 100644 index 00000000..7afec991 --- /dev/null +++ b/src/config/firebase.js @@ -0,0 +1,27 @@ +// Import the functions you need from the SDKs you need +import { initializeApp } from "firebase/app"; +import { getAnalytics } from "firebase/analytics"; +import { getAuth } from "firebase/auth"; +import { getFirestore } from 'firebase/firestore'; +import { getStorage } from 'firebase/storage' +// TODO: Add SDKs for Firebase products that you want to use +// https://firebase.google.com/docs/web/setup#available-libraries + +// Your web app's Firebase configuration +// For Firebase JS SDK v7.20.0 and later, measurementId is optional +// Initialize Firebase +const config = { + apiKey: process.env.REACT_APP_FIREBASE_API_KEY, + authDomain: process.env.REACT_APP_FIREBASE_AUTH_DOMAIN, + projectId: process.env.REACT_APP_FIREBASE_PROJECT_ID, + storageBucket: process.env.REACT_APP_FIREBASE_STORAGE_BUCKET, + messagingSenderId: process.env.REACT_APP_FIREBASE_MESSAGING_SENDER_ID, + appId: process.env.REACT_APP_FIREBASE_APP_ID, + measurementId: process.env.REACT_APP_FIREBASE_MEASUREMENT_IDL +} +const app = initializeApp(config); +const analytics = getAnalytics(app); +const auth = getAuth(app); +const fireStore = getFirestore(app) +const storage = getStorage(app) +export { analytics, auth, fireStore, storage }; \ No newline at end of file diff --git a/src/context/StateContext.js b/src/context/StateContext.js new file mode 100644 index 00000000..01f569fa --- /dev/null +++ b/src/context/StateContext.js @@ -0,0 +1,25 @@ +import { createContext, useState } from 'react'; +const StateContext = createContext(); + +export default StateContext; + + +export const StateProvider = ({ children }) => { + const [menuActive, setMenuActive] = useState(['1']); + const [navMenu, setNavMenu] = useState(['1']); + const [theme, setTheme] = useState('light'); + const contextData = { + menuActive, + setMenuActive, + theme, + setTheme, + navMenu, + setNavMenu + } + + return ( + + {children} + + ) +} \ No newline at end of file diff --git a/src/index.css b/src/index.css index ec2585e8..4e229a52 100644 --- a/src/index.css +++ b/src/index.css @@ -1,13 +1,12 @@ +@import url("https://fonts.googleapis.com/css2?family=Iceland&display=swap"); body { margin: 0; - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', - 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', - sans-serif; + font-family: "Iceland", cursive; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } code { - font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', + font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace; } diff --git a/src/index.js b/src/index.js index d563c0fb..9332e175 100644 --- a/src/index.js +++ b/src/index.js @@ -2,7 +2,8 @@ import React from 'react'; import ReactDOM from 'react-dom/client'; import './index.css'; import App from './App'; -import reportWebVitals from './reportWebVitals'; +import 'antd/dist/antd.min.css'; +import "react-multi-carousel/lib/styles.css"; const root = ReactDOM.createRoot(document.getElementById('root')); root.render( @@ -11,7 +12,3 @@ root.render( ); -// If you want to start measuring performance in your app, pass a function -// to log results (for example: reportWebVitals(console.log)) -// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals -reportWebVitals(); diff --git a/src/layout/layout.js b/src/layout/layout.js new file mode 100644 index 00000000..32c6fc56 --- /dev/null +++ b/src/layout/layout.js @@ -0,0 +1,95 @@ +import { Layout, Image, Button, Menu, Switch } from 'antd'; +import React, { useContext } from 'react'; +import avatar from '../assets/images/avatar.png'; +import '../assets/css/header.css'; +import { useNavigate } from 'react-router-dom' +import { + ContainerOutlined, + HomeOutlined +} from '@ant-design/icons'; +import { signOut } from 'firebase/auth'; +import { auth } from '../config/firebase'; +import StateContext from '../context/StateContext' + +const { Header, Sider, Content } = Layout; + + +const LayoutDashboard = ({ children }) => { + const { menuActive, setMenuActive, theme, setTheme } = useContext(StateContext) + const navigate = useNavigate(); + + const handleMenu = (e) => { + switch (e?.key) { + case '1': + setMenuActive(['1']); + navigate("/dashboard"); + break; + case '2': + navigate("/dashboard/create"); + setMenuActive(['2']); + break; + default: + break; + } + } + const handleLogout = () => { + signOut(auth).then(() => { + navigate("/login"); + }).catch((error) => { + console.log(error); + }); + } + return ( + +
+
+ { + theme === 'light' ? : + } + +
+ Theme + setTheme(e ? 'light' : 'dark')} + checkedChildren="Light" + unCheckedChildren="Dark" + /> + + +
+
+
+ + +
+ handleMenu(e)} + items={[ + { + key: '1', + icon: , + label: 'Homepage', + }, + { + key: '2', + icon: , + label: 'Create Post', + }, + ]} + /> + + + + + {children} + + + ) +} + +export default LayoutDashboard; \ No newline at end of file diff --git a/src/logo.svg b/src/logo.svg deleted file mode 100644 index 9dfc1c05..00000000 --- a/src/logo.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/pages/About.jsx b/src/pages/About.jsx new file mode 100644 index 00000000..3e1a0414 --- /dev/null +++ b/src/pages/About.jsx @@ -0,0 +1,49 @@ +import React from "react"; +import Navbar from "../components/Navbar"; +import FooterPage from "../components/FooterPage"; +import { Layout, Typography } from "antd"; +import "../assets/css/about.css"; +const { Content } = Layout; +const { Paragraph } = Typography; +const About = () => { + return ( + + + +
+
+ About +
+ EdTech + + Education Technology is the largest English-language technology + media company that focuses on Asia. From the latest news to the + hottest trends and the boldest startups to the strongest titans, + we cover everything tech in the region. Our goal is to build and + serve Asia’s tech and startup community. Apart from producing and + delivering quality editorial content, we connect brands with early + adopters via Studios, our advertising agency unit. We organize + tech conferences and events across Asia, and we operate the + region’s go-to startup and technology jobs marketplace. + +
+
+
+ +
+ ); +}; + +export default About; diff --git a/src/pages/Beranda.jsx b/src/pages/Beranda.jsx new file mode 100644 index 00000000..3d8ba62b --- /dev/null +++ b/src/pages/Beranda.jsx @@ -0,0 +1,395 @@ +import React, { useEffect, useState } from "react"; +import { Layout, Image, Button, Space, Typography, Row, Col } from "antd"; +import FooterPage from "../components/FooterPage"; +import "../assets/css/beranda.css"; +import { useNavigate } from "react-router-dom"; +import { fireStore } from "../config/firebase"; +import { collection, query, where, getDocs } from "firebase/firestore"; +import Carousel from "react-multi-carousel"; +import { DoubleRightOutlined } from "@ant-design/icons"; +import Navbar from "../components/Navbar"; +import Loading from "../components/Loading"; +const { Content } = Layout; +const { Paragraph } = Typography; + +const Beranda = () => { + const navigate = useNavigate(); + const [loading, setLoading] = useState(false); + const [dataBanner, setDataBanner] = useState([]); + const [category1, setCategory1] = useState([]); + const [category2, setCategory2] = useState([]); + const [category3, setCategory3] = useState([]); + + const responsive = { + desktop: { breakpoint: { max: 3000, min: 1024 }, items: 1 }, + mobile: { breakpoint: { max: 464, min: 0 }, items: 1 }, + tablet: { breakpoint: { max: 1024, min: 464 }, items: 1 }, + }; + + const getArticleBanner = async () => { + setLoading(true); + try { + const q = query( + collection(fireStore, "blog_post"), + where("category", "==", "Blog") + ); + const q2 = query( + collection(fireStore, "blog_post"), + where("category", "==", "Website") + ); + const q3 = query( + collection(fireStore, "blog_post"), + where("category", "==", "Android") + ); + const q4 = query( + collection(fireStore, "blog_post"), + where("category", "==", "Laptop") + ); + + const articleBanner = getDocs(q); + const DataCategory1 = getDocs(q2); + const DataCategory2 = getDocs(q3); + const DataCategory3 = getDocs(q4); + + const [data1, data2, data3, data4] = await Promise.all([ + articleBanner, + DataCategory1, + DataCategory2, + DataCategory3, + ]); + setDataBanner( + data1.docs.map((doc) => ({ + ...doc.data(), + id: doc.id, + })) + ); + setCategory1( + data2.docs.map((doc) => ({ + ...doc.data(), + id: doc.id, + })) + ); + setCategory2( + data3.docs.map((doc) => ({ + ...doc.data(), + id: doc.id, + })) + ); + setCategory3( + data4.docs.map((doc) => ({ + ...doc.data(), + id: doc.id, + })) + ); + + setLoading(false); + } catch (error) { + console.log(error); + setLoading(false); + } + }; + const handleDetail = (id) => { + navigate(`/detail/${id}`); + }; + useEffect(() => { + getArticleBanner(); + }, []); + return ( + + {loading ? ( + + ) : ( + <> + + + + {dataBanner && + dataBanner?.map((data, index) => ( +
+ {data?.title} + +
+ + +
+ ))} + + + +
+ + Website Category + +
+
+
+ {category1 && + category1?.map((data) => ( +
+ + + + {data?.title} + + +
+ + + +
+ ))} +
+ + +
+ + + Laptop Category + + + Find out information about Laptop computers. A laptop is a + personal computer that can be easily moved and used in a + variety of locations + + +
+
+ {category3 && + category3?.map((data) => ( +
+ + + + {data?.title} + + +
+ + + +
+ ))} +
+ + +
+ + Android Category + +
+
+ {category2 && + category2?.map((data) => ( +
+ + + + {data?.title} + + +
+ + + +
+ ))} +
+ + + + + + )} + + ); +}; + +export default Beranda; diff --git a/src/pages/ContactPage.jsx b/src/pages/ContactPage.jsx new file mode 100644 index 00000000..20f92293 --- /dev/null +++ b/src/pages/ContactPage.jsx @@ -0,0 +1,143 @@ +import React from "react"; +import { + Layout, + Input, + Typography, + Row, + Col, + Card, + Form, + Button, + notification, +} from "antd"; +import Navbar from "../components/Navbar"; +import FooterPage from "../components/FooterPage"; +import imageCard from "../assets/images/contact.png"; +import "../assets/css/contact.css"; +const { Content } = Layout; +const { Paragraph } = Typography; +const { TextArea } = Input; + +const { Meta } = Card; +const ContactPage = () => { + const handleContact = (value) => { + notification.success({ + message: `Terimakasih ${value?.name}`, + description: + "Pesan anda telah kami terima, akan kami tanggapi secepatnya!", + duration: 5, + }); + }; + return ( + + + + + +
+ } + > + + +
+ + +
+ Contact +
+ + Questions and comments are important to us, so please use the + form below to contact our crew. We will reply as soon as + possible. Guarantee a response within 24 hours! + +
+ Your Name} + rules={[ + { + required: true, + message: "Nama tidak boleh kosong!", + }, + ]} + > + + + Your Email} + > + + + Your Message} + > +