Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1fb8ae7c8b | |||
| 87e0ed3a47 | |||
| 5ceda514a8 | |||
| 05e9ece8e1 | |||
| 438a3077a3 | |||
| d0409422e3 | |||
| ecdf2590a5 | |||
| c3b59cbf5f | |||
| 18baab3c1b | |||
| 216dbdf371 | |||
| 093786c919 | |||
| 97284c5c15 | |||
| 927a7c798a | |||
| ee0221f7e1 |
@@ -6,7 +6,7 @@ REACT_APP_USERS_ENDPOINT="https://apigate.lotus.g1.wrenchboard.com/en/wrench/api
|
||||
REACT_APP_SITE_NAME='WrenchBoard'
|
||||
REACT_APP_DASH_URL='https://dev-users.wrenchboard.com'
|
||||
REACT_APP_DASH_URL_LOGIN="https://dev-users.wrenchboard.com/login"
|
||||
REACT_APP_DASH_URL_SIGNUP="https://dev-users.wrenchboard.com/signup"
|
||||
REACT_APP_DASH_URL_SIGNUP="https://dev-users.wrenchboard.com/signup?cnt=us"
|
||||
REACT_APP_ANDROID_APP='https://play.google.com/store/apps/details?id=com.wrenchboard.users'
|
||||
REACT_APP_APPLE_APP='https://itunes.apple.com/us/app/wrenchboard/id1435718367?ls=1&mt=8'
|
||||
REACT_APP_FACEBOOK_LINK='https://www.facebook.com/wrenchboard'
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@ REACT_APP_USERS_ENDPOINT="https://apigate.lotus.g1.wrenchboard.com/en/wrench/api
|
||||
REACT_APP_SITE_NAME='WrenchBoard'
|
||||
REACT_APP_DASH_URL='https://dev-users.wrenchboard.com'
|
||||
REACT_APP_DASH_URL_LOGIN="https://dev-users.wrenchboard.com/login"
|
||||
REACT_APP_DASH_URL_SIGNUP="https://dev-users.wrenchboard.com/signup"
|
||||
REACT_APP_DASH_URL_SIGNUP="https://dev-users.wrenchboard.com/signup?cnt=us"
|
||||
REACT_APP_ANDROID_APP='https://play.google.com/store/apps/details?id=com.wrenchboard.users'
|
||||
REACT_APP_APPLE_APP='https://itunes.apple.com/us/app/wrenchboard/id1435718367?ls=1&mt=8'
|
||||
REACT_APP_FACEBOOK_LINK='https://www.facebook.com/wrenchboard'
|
||||
|
||||
+3
-3
@@ -1,12 +1,12 @@
|
||||
ESLINT_NO_DEV_ERRORS=true
|
||||
|
||||
REACT_APP_AUX_ENDPOINT="https://apigate.orion.g1.wrenchboard.com/en/wrench/api/v1"
|
||||
REACT_APP_USERS_ENDPOINT="https://apigate.orion.g1.wrenchboard.com/en/wrench/api/v1"
|
||||
REACT_APP_AUX_ENDPOINT="https://apigate.lotus.g1.wrenchboard.com/en/wrench/api/v1"
|
||||
REACT_APP_USERS_ENDPOINT="https://apigate.lotus.g1.wrenchboard.com/en/wrench/api/v1"
|
||||
|
||||
REACT_APP_SITE_NAME='WrenchBoard'
|
||||
REACT_APP_DASH_URL='https://users.wrenchboard.com'
|
||||
REACT_APP_DASH_URL_LOGIN="https://users.wrenchboard.com/login"
|
||||
REACT_APP_DASH_URL_SIGNUP="https://users.wrenchboard.com/signup"
|
||||
REACT_APP_DASH_URL_SIGNUP="https://users.wrenchboard.com/signup?cnt=us"
|
||||
REACT_APP_ANDROID_APP='https://play.google.com/store/apps/details?id=com.wrenchboard.users'
|
||||
REACT_APP_APPLE_APP='https://itunes.apple.com/us/app/wrenchboard/id1435718367?ls=1&mt=8'
|
||||
REACT_APP_FACEBOOK_LINK='https://www.facebook.com/wrenchboard'
|
||||
|
||||
+3
-3
@@ -5,9 +5,9 @@
|
||||
<link type="image/png" rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta name="description" content="wrenchboard.com is an online marketplace to make money from your skills, find, buy and sell professional services. Connect with freelancers to get work done faster. Trade your freelance services." />
|
||||
<meta name="title" content="WrenchBoard: Find a Freelancer | Sell Professional Services" />
|
||||
<meta name="keywords" content="Online Jobs, Online Workers, work online Nigeria, hire a freelancer, hire freelancers, freelance marketplace, freelancer hire, freelance service, freelance professional services, How to make money online, find workers online, Online Services, digital services, freelancers community in Ghana, freelancers community in Nigeria, freelancer site in Africa, Best freelance website in Africa, Freelance Designers, Photographers, Writers in Nigeria, freelancers, freelance outsourcing in Nigeria, freelance IT services in Nigeria, hire freelancers online in Nigeria, freelance services online in Nigeria, freelance contractor in Nigeria, freelance sites in Nigeria, freelance jobs in Nigeria, freelance projects in Nigeria, freelance jobs online in Nigeria, professional freelancers in Nigeria, buy professional services in Nigeria, professional services jobs, professional business services, professional service providers in Nigeria, freelancing services, freelancing sites in Nigeria, freelancers for hire in Nigeria, freelancer search in Nigeria, search freelancer in Nigeria, find freelancers in Nigeria, Find workers US. Outsource from US to Nigeria, find a freelancer in Nigeria, freelancing projects in Nigeria, web freelancing in Nigeria, outsourcing sites freelancers in Nigeria, website for freelancers in Nigeria, marketplace for freelancers " />
|
||||
<meta name="description" content="WrenchBoard.com is the place to set family goals and reward achievements. Find tasks to earn from, or send tasks for others to perform for you." />
|
||||
<meta name="title" content="WrenchBoard: Reward Accomplishments | Get Family Engaged" />
|
||||
<meta name="keywords" content="Empower families to reward accomplishment, set goals, and encourage kids to understand goals, earning, and the benefit of savings – in one app experience. " />
|
||||
|
||||
|
||||
<!-- <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" /> -->
|
||||
|
||||
+7
-3
@@ -14,6 +14,9 @@ import HomeOne from './components/HomeOne';
|
||||
// import HomeThree from './components/HomeThree';
|
||||
// import Hometwo from './components/HomeTwo';
|
||||
import News from './components/News';
|
||||
import Blog from "./components/Blog";
|
||||
import BlogDetail from "./components/Blog/BlogDetail";
|
||||
|
||||
import SingleNews from './components/News/SingleNews';
|
||||
import Service from './components/Service';
|
||||
import UseCases from './components/UseCases';
|
||||
@@ -45,10 +48,11 @@ function Routes() {
|
||||
<Switch>
|
||||
<Route exact path="/" component={HomeOne} />
|
||||
<Route exact path="/eoffer" component={HomeOne} />
|
||||
<Route exact path="/news" component={News} />
|
||||
<Route exact path="/blog" component={News} />
|
||||
{/*<Route exact path="/news" component={News} />*/}
|
||||
<Route exact path="/blog" component={Blog} />
|
||||
<Route exact path="/blog/blogdetail/:id" component={BlogDetail} />
|
||||
<Route exact path="/use-cases" component={UseCases} />
|
||||
<Route exact path="/news/single-news" component={SingleNews} />
|
||||
{/*<Route exact path="/news/single-news" component={SingleNews} />*/}
|
||||
<Route exact path="/service" component={Service} />
|
||||
<Route exact path="/terms" component={Terms} />
|
||||
<Route exact path="/privacy" component={Privacy} />
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
:root {
|
||||
--loader-width: auto;
|
||||
--loader-height: auto;
|
||||
--text-container-width: 400px;
|
||||
--text-container-height: auto;
|
||||
--line-height: 1.5;
|
||||
}
|
||||
|
||||
.image-skeleton-loader,
|
||||
.blog-text-skeleton-loader {
|
||||
width: var(--loader-width);
|
||||
height: var(--loader-height);
|
||||
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
|
||||
background-size: 200% 100%;
|
||||
animation: loading 1.5s infinite;
|
||||
}
|
||||
|
||||
.image-skeleton-loader{
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.blog-text-skeleton-loader {
|
||||
line-height: calc(var(--text-container-height) * var(--line-height));
|
||||
}
|
||||
|
||||
@keyframes loading {
|
||||
0% {
|
||||
background-position: -200% 0;
|
||||
}
|
||||
100% {
|
||||
background-position: 200% 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
import singlePost from "../../assets/images/single-post/1.jpg";
|
||||
import { BlogLoader, ImageLoader } from "../../lib/SkeletonLoaders";
|
||||
import LazyImage from "../../lib/LazyImage";
|
||||
|
||||
/**
|
||||
* Renders a blog post component.
|
||||
* @returns {JSX.Element} The rendered blog post component.
|
||||
*/
|
||||
function Blog({ blogItem, imgUrl, loader }) {
|
||||
// Generate a unique ID
|
||||
const uniqueId = `element_${Math.random().toString(36).substr(2, 9)}`;
|
||||
|
||||
const blogImg = `${imgUrl}/${blogItem?.meta_value || singlePost}`;
|
||||
|
||||
const imgLoaderStyles = {
|
||||
"--loader-width": "750px",
|
||||
"--loader-height": "550px",
|
||||
};
|
||||
|
||||
const headerLoaderStyles = {
|
||||
"--text-container-width": "300px",
|
||||
"--text-container-height": "34px",
|
||||
};
|
||||
|
||||
const bodyTextLoaderStyles = {
|
||||
"--text-container-width": "770px",
|
||||
"--text-container-height": "150px",
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="single-post-area">
|
||||
<div className="post-thumb" style={{ marginTop: "0" }}>
|
||||
{loader ? (
|
||||
<div style={imgLoaderStyles}>
|
||||
<ImageLoader />
|
||||
</div>
|
||||
) : (
|
||||
<LazyImage src={blogImg} alt={blogItem?.meta_value || "single-post.jpg"} key={uniqueId} />
|
||||
)}
|
||||
</div>
|
||||
{loader ? (
|
||||
<div style={headerLoaderStyles}>
|
||||
<BlogLoader />
|
||||
</div>
|
||||
) : (
|
||||
<h4 className="article-title">{blogItem?.post_title}</h4>
|
||||
)}
|
||||
{loader ? (
|
||||
<div style={bodyTextLoaderStyles}>
|
||||
<BlogLoader />
|
||||
</div>
|
||||
) : (
|
||||
<div
|
||||
dangerouslySetInnerHTML={{ __html: blogItem?.post_content }}
|
||||
></div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* <blockquote>
|
||||
<p>
|
||||
I don't want no agro brilliant are you taking the piss skive off super
|
||||
boot chancer don't get shirty.
|
||||
</p>
|
||||
<cite>Indigo Violet</cite>
|
||||
</blockquote> */}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default Blog;
|
||||
@@ -0,0 +1,87 @@
|
||||
import React, { useEffect, useMemo, useState } from "react";
|
||||
import { useParams } from "react-router-dom";
|
||||
import useToggle from "../../Hooks/useToggle";
|
||||
import BackToTop from "../BackToTop";
|
||||
import FooterHomeOne from "../HomeOne/FooterHomeOne";
|
||||
import Drawer from "../Mobile/Drawer";
|
||||
import Blog from "./Blog";
|
||||
import BlogData from "../../Services/BlogData";
|
||||
import BlogSideBar from "./BlogSideBar";
|
||||
import HeaderNews from "./HeaderNews";
|
||||
import HeroNews from "./HeroNews";
|
||||
import StickyHeaderNav from "../StickyHeader/StickyHeaderNav";
|
||||
|
||||
/**
|
||||
* Renders the blog detail page.
|
||||
*/
|
||||
function BlogDetail() {
|
||||
const [drawer, drawerAction] = useToggle(false);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [blogs, setBlogs] = useState([]);
|
||||
const { id } = useParams();
|
||||
|
||||
useEffect(() => {
|
||||
const fetchBlogs = async () => {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
const res = await BlogData();
|
||||
setBlogs(res.data);
|
||||
} catch (err) {
|
||||
console.log("Error loading blogdata", err);
|
||||
} finally {
|
||||
setTimeout(() => setIsLoading(false), 1500);
|
||||
}
|
||||
};
|
||||
|
||||
fetchBlogs();
|
||||
}, []);
|
||||
|
||||
const blogItem = useMemo(() => {
|
||||
return blogs.blogdata?.find((item) => +item.id === +id);
|
||||
}, [blogs, id]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* Renders the drawer component */}
|
||||
<Drawer drawer={drawer} action={drawerAction.toggle} />
|
||||
|
||||
{/* Renders the header component */}
|
||||
<HeaderNews action={drawerAction.toggle} />
|
||||
|
||||
{/* Renders the hero section */}
|
||||
<HeroNews
|
||||
title="Blog"
|
||||
breadcrumb={[
|
||||
{ link: "/", title: "Home" },
|
||||
{ link: "/blog", title: "Blogs" },
|
||||
{
|
||||
link: `/blog/blogdetail/${id}`,
|
||||
title: isLoading ? "please wait..." : blogItem ? blogItem.post_title : "Post not found",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
{/* Renders the blog content and sidebar */}
|
||||
<section className="blogpage-section">
|
||||
<div className="container">
|
||||
<div className="row">
|
||||
<div className="col-lg-8 col-md-7">
|
||||
<Blog blogItem={blogItem} imgUrl={blogs?.image_url} loader={isLoading} />
|
||||
</div>
|
||||
<div className="col-lg-4 col-md-5">
|
||||
<BlogSideBar blogs={blogs} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Renders the footer */}
|
||||
<FooterHomeOne />
|
||||
|
||||
{/* Renders the back-to-top button */}
|
||||
<BackToTop />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default BlogDetail;
|
||||
@@ -0,0 +1,69 @@
|
||||
import React from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import BlogImg1 from "../../assets/images/blog/p1.jpg";
|
||||
|
||||
/**
|
||||
* Renders a sidebar for a blog.
|
||||
* @param {Object} blogs - An object containing the data for the blog posts.
|
||||
* @returns {JSX.Element} - JSX code that renders the blog sidebar.
|
||||
*/
|
||||
function BlogSideBar({ blogs }) {
|
||||
/**
|
||||
* Renders other blog posts.
|
||||
* This is an Array of JSX elements representing the other blog posts.
|
||||
*/
|
||||
const renderOtherBlogPosts = () => {
|
||||
return blogs?.blogdata?.slice(0, 4).map((post) => {
|
||||
const blogDate = new Date(post.post_date).toLocaleDateString("en-US", {
|
||||
year: "numeric",
|
||||
month: "short",
|
||||
day: "numeric",
|
||||
});
|
||||
|
||||
const blogImg =
|
||||
post.meta_value != null
|
||||
? `${blogs?.image_url}/${post.meta_value}`
|
||||
: BlogImg1;
|
||||
|
||||
return (
|
||||
<div className="popular-post" key={post.id}>
|
||||
<Link to={`/blog/blogdetail/${post?.id}`}>
|
||||
<img src={blogImg} alt="blog-img" style={{top: "20px"}} loading="lazy" />
|
||||
</Link>
|
||||
<h5>
|
||||
<Link to={`/blog/blogdetail/${post?.id}`}>{post?.post_title}</Link>
|
||||
</h5>
|
||||
<span>{blogDate}</span>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="blog-sidebar">
|
||||
<aside className="widget widget-categories">
|
||||
{/*<h3 className="widget-title">Categories</h3>*/}
|
||||
<ul>
|
||||
<li>
|
||||
<Link to="/about-us">About</Link>
|
||||
</li>
|
||||
<li>
|
||||
<a href={process.env.REACT_APP_DASH_URL_SIGNUP}>Sign up</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href={process.env.REACT_APP_DASH_URL_LOGIN}>Login</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://blog.wrenchboard.com/">More Articles</a>
|
||||
</li>
|
||||
</ul>
|
||||
</aside>
|
||||
<aside className="widget widget-trend-post">
|
||||
<h3 className="widget-title">Other Posts</h3>
|
||||
{renderOtherBlogPosts()}
|
||||
</aside>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default BlogSideBar;
|
||||
@@ -0,0 +1,89 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import blogOne from "../../assets/images/blog/1.jpg";
|
||||
import BlogData from "../../Services/BlogData";
|
||||
|
||||
/**
|
||||
* Fetches blog data from an API and renders the blogs on the page.
|
||||
* Displays a maximum of six blogs on the home page and all blogs on other pages.
|
||||
*
|
||||
* @param {string} pathname - The current path of the page.
|
||||
* @returns {JSX.Element} - The rendered HTML of the blogs component.
|
||||
*/
|
||||
function Blogs({ pathname }) {
|
||||
const [blogs, setBlogs] = useState([]);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchBlogData = async () => {
|
||||
try {
|
||||
const res = await BlogData();
|
||||
setBlogs(res.data);
|
||||
} catch (err) {
|
||||
console.log("Error loading blogdata", err);
|
||||
}
|
||||
};
|
||||
|
||||
fetchBlogData();
|
||||
}, []);
|
||||
|
||||
const renderBlogItem = (blog) => {
|
||||
const options = {
|
||||
weekday: "short",
|
||||
year: "numeric",
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
};
|
||||
const postDt = new Date(blog.post_date).toLocaleDateString(
|
||||
"en-US",
|
||||
options
|
||||
);
|
||||
const blgImg =
|
||||
blog.meta_value != null
|
||||
? `${blogs?.blogconfig?.media_url}/${blog.meta_value}`
|
||||
: blogOne;
|
||||
|
||||
return (
|
||||
<div key={blog.id} className="col-lg-4 col-md-6">
|
||||
<div
|
||||
className="appie-blog-item mt-30 wow animated fadeInUp"
|
||||
data-wow-duration="3000ms"
|
||||
data-wow-delay="200ms"
|
||||
>
|
||||
<Link to={`/blog/blogdetail/${blog?.id}`} className="thumb">
|
||||
<img
|
||||
src={blgImg}
|
||||
alt={blog.post_title}
|
||||
style={{ cursor: "pointer" }}
|
||||
/>
|
||||
</Link>
|
||||
<div className="content">
|
||||
<div className="blog-meta">
|
||||
<ul>
|
||||
<li style={{ cursor: "pointer" }}>{postDt}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<h3 className="title">
|
||||
<Link to={`/blog/blogdetail/${blog?.id}`}>{blog.post_title}</Link>
|
||||
</h3>
|
||||
<Link to={`/blog/blogdetail/${blog?.id}`}>
|
||||
Learn More <i className="fal fa-arrow-right" />
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="row">
|
||||
{pathname === "/" // ON HOME PAGE LIMIT TO SIX(6) BLOGS
|
||||
? blogs?.blogdata?.slice(0, 6).map(renderBlogItem)
|
||||
: // ON OTHER PAGES SHOW ALL BLOG
|
||||
blogs?.blogdata?.map(renderBlogItem)}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default Blogs;
|
||||
@@ -0,0 +1,54 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import logo from '../../assets/images/wrenchboard-logo-text.png';
|
||||
import StickyMenu from '../../lib/StickyMenu';
|
||||
import Navigation from '../Navigation';
|
||||
import getConfig from './../../Config/config'
|
||||
|
||||
function HeaderNews({ action }) {
|
||||
var site = getConfig()[0];
|
||||
useEffect(() => {
|
||||
StickyMenu();
|
||||
});
|
||||
return (
|
||||
<>
|
||||
<header className="appie-header-area appie-header-page-area appie-sticky">
|
||||
<div className="container">
|
||||
<div className="header-nav-box header-nav-box-3 header-nav-box-inner-page">
|
||||
<div className="row align-items-center">
|
||||
<div className="col-lg-2 col-md-4 col-sm-5 col-6 order-1 order-sm-1">
|
||||
<div className="wrench-logo-box">
|
||||
<a href="/">
|
||||
<img src={logo} alt="" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-lg-6 col-md-1 col-sm-1 order-3 order-sm-2">
|
||||
<div className="appie-header-main-menu">
|
||||
<Navigation />
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-lg-4 col-md-7 col-sm-6 col-6 order-2 order-sm-3">
|
||||
<div className="appie-btn-box text-right">
|
||||
<a className="login-btn" href={process.env.REACT_APP_DASH_URL_LOGIN}>
|
||||
<i className="fal fa-user"></i> Login
|
||||
</a>
|
||||
<a className="main-btn ml-30" href={process.env.REACT_APP_DASH_URL_SIGNUP}>
|
||||
Get Started
|
||||
</a>
|
||||
<div
|
||||
onClick={(e) => action(e)}
|
||||
className="toggle-btn ml-30 canvas_open d-lg-none d-block"
|
||||
>
|
||||
<i className="fa fa-bars"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default HeaderNews;
|
||||
@@ -0,0 +1,31 @@
|
||||
import React from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
function HeroNews({ title, breadcrumb }) {
|
||||
return (
|
||||
<>
|
||||
<div className="appie-page-title-area">
|
||||
<div className="container">
|
||||
<div className="row">
|
||||
<div className="col-lg-12">
|
||||
<div className="appie-page-title-item">
|
||||
<h3 className="title">{title}</h3>
|
||||
<nav aria-label="breadcrumb">
|
||||
<ol className="breadcrumb">
|
||||
{breadcrumb.map((value) => (
|
||||
<li key={Math.random()} className="breadcrumb-item">
|
||||
<Link to={value.link}>{value.title}</Link>
|
||||
</li>
|
||||
))}
|
||||
</ol>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default HeroNews;
|
||||
@@ -0,0 +1,41 @@
|
||||
import React from "react";
|
||||
import useToggle from "../../Hooks/useToggle";
|
||||
import BackToTop from "../BackToTop";
|
||||
import FooterHomeOne from "../HomeOne/FooterHomeOne";
|
||||
import Drawer from "../Mobile/Drawer";
|
||||
import Blogs from "./Blogs";
|
||||
import BlogSideBar from "./BlogSideBar";
|
||||
import HeaderNews from "./HeaderNews";
|
||||
import HeroNews from "./HeroNews";
|
||||
import StickyHeaderNav from "../StickyHeader/StickyHeaderNav";
|
||||
|
||||
function Blog() {
|
||||
const [drawer, drawerAction] = useToggle(false);
|
||||
return (
|
||||
<>
|
||||
<Drawer drawer={drawer} action={drawerAction.toggle} />
|
||||
{/* <StickyHeaderNav action={drawerAction.toggle} /> */}
|
||||
<HeaderNews action={drawerAction.toggle} />
|
||||
<HeroNews
|
||||
title="Blogs"
|
||||
breadcrumb={[
|
||||
{ link: "/", title: "Home" },
|
||||
{ link: "/blog", title: "Blogs" },
|
||||
]}
|
||||
/>
|
||||
<section className="blogpage-section">
|
||||
<div className="container">
|
||||
<div className="row">
|
||||
<div className="col-lg-12 col-md-7">
|
||||
<Blogs />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<FooterHomeOne />
|
||||
<BackToTop />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default Blog;
|
||||
@@ -1,26 +1,20 @@
|
||||
import React, { useState } from 'react';
|
||||
import blogOne from '../../assets/images/blog-1.jpg';
|
||||
import blogTwo from '../../assets/images/blog-2.jpg';
|
||||
import blogThree from '../../assets/images/blog-3.jpg';
|
||||
import Blogs from '../News/Blogs';
|
||||
|
||||
|
||||
function BlogHomeOne() {
|
||||
return (
|
||||
<>
|
||||
<section className="appie-blog-area pt-45 pb-95">
|
||||
<div className="container">
|
||||
<div className="row">
|
||||
<div className="col-lg-12">
|
||||
<div className="appie-section-title text-center">
|
||||
<h3 className="appie-title">Our latest blog posts</h3>
|
||||
</div>
|
||||
<section className="appie-blog-area pt-45 pb-95">
|
||||
<div className="container">
|
||||
<div className="row">
|
||||
<div className="col-lg-12">
|
||||
<div className="appie-section-title text-center">
|
||||
<h3 className="appie-title">Our latest blog posts</h3>
|
||||
</div>
|
||||
</div>
|
||||
<Blogs pathname='/' />
|
||||
</div>
|
||||
</section>
|
||||
</>
|
||||
<Blogs pathname='/' />
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,96 +1,81 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import blogOne from '../../assets/images/blog/1.jpg';
|
||||
import BlogData from '../../Services/BlogData';
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import blogOne from "../../assets/images/blog/1.jpg";
|
||||
import BlogData from "../../Services/BlogData";
|
||||
|
||||
function Blogs({pathname}) {
|
||||
const [blogs, setBlogs] = useState([])
|
||||
/**
|
||||
* Fetches blog data from an API and renders the blogs on the page.
|
||||
* Displays a maximum of six blogs on the home page and all blogs on other pages.
|
||||
*/
|
||||
function Blogs({ pathname }) {
|
||||
const [blogs, setBlogs] = useState([]);
|
||||
|
||||
useEffect(()=>{
|
||||
BlogData().then(res => {
|
||||
setBlogs(res.data)
|
||||
}).catch(err => {
|
||||
console.log('Error loading blogdata', err)
|
||||
})
|
||||
},[])
|
||||
useEffect(() => {
|
||||
const fetchBlogs = async () => {
|
||||
try {
|
||||
const res = await BlogData();
|
||||
setBlogs(res.data);
|
||||
} catch (err) {
|
||||
console.log("Error loading blogdata", err);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="row">
|
||||
{ pathname == '/' ? // ON HOME PAGE LIMIT TO SIX(6) BLOGS
|
||||
blogs?.blogdata?.map((i, index)=> {
|
||||
var options = { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' };
|
||||
var postDt = new Date(i.post_date).toLocaleDateString("en-US", options);
|
||||
var blgImg = i.meta_value != null ? `${blogs?.blogconfig?.media_url}/${i.meta_value}` : blogOne;
|
||||
if(index < 6){
|
||||
return (
|
||||
<div key={i.id} className="col-lg-4 col-md-6">
|
||||
<div
|
||||
className="appie-blog-item mt-30 wow animated fadeInUp"
|
||||
data-wow-duration="3000ms"
|
||||
data-wow-delay="200ms"
|
||||
>
|
||||
<div className="thumb">
|
||||
<img src={blgImg} alt={i.post_title} />
|
||||
</div>
|
||||
<div className="content">
|
||||
<div className="blog-meta">
|
||||
<ul>
|
||||
<li>{postDt}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<h3 className="title">
|
||||
<a href={i.guid}>
|
||||
{i.post_title}
|
||||
</a>
|
||||
</h3>
|
||||
<a href={i.guid}>
|
||||
Learn More <i className="fal fa-arrow-right" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
})
|
||||
: // ON OTHER PAGES SHOW ALL BLOG
|
||||
blogs?.blogdata?.map((i, index)=> {
|
||||
var options = { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' };
|
||||
var postDt = new Date(i.post_date).toLocaleDateString("en-US", options);
|
||||
var blgImg = i.meta_value != null ? `${blogs?.blogconfig?.media_url}/${i.meta_value}` : blogOne;
|
||||
return (
|
||||
<div key={i.id} className="col-lg-4 col-md-6">
|
||||
<div
|
||||
className="appie-blog-item mt-30 wow animated fadeInUp"
|
||||
data-wow-duration="3000ms"
|
||||
data-wow-delay="200ms"
|
||||
>
|
||||
<div className="thumb">
|
||||
<img src={blgImg} alt={i.post_title} />
|
||||
</div>
|
||||
<div className="content">
|
||||
<div className="blog-meta">
|
||||
<ul>
|
||||
<li>{postDt}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<h3 className="title">
|
||||
<a href={i.guid}>
|
||||
{i.post_title}
|
||||
</a>
|
||||
</h3>
|
||||
<a href={i.guid}>
|
||||
Learn More <i className="fal fa-arrow-right" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
}
|
||||
fetchBlogs();
|
||||
}, []);
|
||||
|
||||
const renderBlogs = () => {
|
||||
return blogs?.blogdata?.map((blog, index) => {
|
||||
const options = {
|
||||
weekday: "short",
|
||||
year: "numeric",
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
};
|
||||
const postDt = new Date(blog.post_date).toLocaleDateString(
|
||||
"en-US",
|
||||
options
|
||||
);
|
||||
const blgImg =
|
||||
blog.meta_value != null
|
||||
? `${blogs?.blogconfig?.media_url}/${blog.meta_value}`
|
||||
: blogOne;
|
||||
|
||||
return (
|
||||
<div key={blog.id} className="col-lg-4 col-md-6">
|
||||
<div
|
||||
className="appie-blog-item mt-30 wow animated fadeInUp"
|
||||
data-wow-duration="3000ms"
|
||||
data-wow-delay="200ms"
|
||||
>
|
||||
<Link to={`/blog/blogdetail/${blog?.id}`} className="thumb">
|
||||
<img src={blgImg} alt={blog.post_title} style={{cursor: "pointer"}} />
|
||||
</Link>
|
||||
<div className="content">
|
||||
<div className="blog-meta">
|
||||
<ul>
|
||||
<li style={{cursor: "pointer"}}>{postDt}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<h3 className="title">
|
||||
<Link to={`/blog/blogdetail/${blog?.id}`}>{blog.post_title}</Link>
|
||||
</h3>
|
||||
<Link to={`/blog/blogdetail/${blog?.id}`}>
|
||||
Learn More <i className="fal fa-arrow-right" />
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
</>
|
||||
);
|
||||
return (
|
||||
<>
|
||||
<div className="row">
|
||||
{pathname === "/" ? renderBlogs()?.slice(0, 6) : renderBlogs()}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default Blogs;
|
||||
export default Blogs;
|
||||
@@ -0,0 +1,52 @@
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
|
||||
/**
|
||||
* Renders an image lazily using the Intersection Observer API.
|
||||
* The image is initially hidden and becomes visible once it enters the viewport.
|
||||
* This approach improves performance by only loading images that are actually visible to the user.
|
||||
*
|
||||
* @returns {JSX.Element} - The lazy image component.
|
||||
*/
|
||||
function LazyImage({ src, alt }) {
|
||||
const imgRef = useRef();
|
||||
const [isVisible, setIsVisible] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const observer = new IntersectionObserver(
|
||||
(entries) => {
|
||||
const [entry] = entries;
|
||||
if (entry.isIntersecting) {
|
||||
setIsVisible(true);
|
||||
observer.unobserve(imgRef.current); // Stop observing once the image is in the viewport
|
||||
}
|
||||
},
|
||||
{
|
||||
root: null, // Viewport
|
||||
rootMargin: '0px', // No margin
|
||||
threshold: 0.1, // Percentage of the image that needs to be visible
|
||||
}
|
||||
);
|
||||
|
||||
if (imgRef.current) {
|
||||
observer.observe(imgRef.current);
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (imgRef.current) {
|
||||
observer.unobserve(imgRef.current);
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<img
|
||||
ref={imgRef}
|
||||
src={isVisible ? src : ''}
|
||||
alt={alt}
|
||||
loading="lazy"
|
||||
className={isVisible ? 'visible' : 'hidden'} // You can apply CSS classes for animations
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default LazyImage;
|
||||
@@ -0,0 +1,14 @@
|
||||
import "../assets/css/skeleton-loader.css"
|
||||
|
||||
/**
|
||||
* Renders a placeholder `<div>` element with the class name "image-skeleton-loader".
|
||||
* This component is typically used as a placeholder for an image while it is being loaded.
|
||||
* The CSS class "image-skeleton-loader or blog-text-skeleton-loader" can be used to style the placeholder element.
|
||||
*
|
||||
* @returns {React.Element} The rendered `<div>` element with the class name "image-skeleton-loader and blog-text-skeleton-loader".
|
||||
*/
|
||||
export const ImageLoader = () => <div className="image-skeleton-loader" />;
|
||||
|
||||
export const BlogLoader = () => <div className="blog-text-skeleton-loader" />
|
||||
|
||||
|
||||
Reference in New Issue
Block a user