Pass config as props

This commit is contained in:
Ariful Alam 2022-03-25 22:45:11 +06:00
parent c6066fd038
commit 82eb282e17
12 changed files with 317 additions and 248 deletions

View File

@ -1,7 +1,8 @@
import config from '../gitprofile.config';
import GitProfile from './components/GitProfile'; import GitProfile from './components/GitProfile';
function App() { function App() {
return <GitProfile />; return <GitProfile config={config} />;
} }
export default App; export default App;

View File

@ -12,12 +12,12 @@ import Education from './education';
import Project from './project'; import Project from './project';
import Blog from './blog'; import Blog from './blog';
import { getInitialTheme, setupHotjar } from '../helpers/utils'; import { getInitialTheme, setupHotjar } from '../helpers/utils';
import config from '../../gitprofile.config';
import '../assets/index.css';
import { HelmetProvider } from 'react-helmet-async'; import { HelmetProvider } from 'react-helmet-async';
import PropTypes from 'prop-types';
import '../assets/index.css';
const GitProfile = () => { const GitProfile = ({ config }) => {
const [theme, setTheme] = useState(getInitialTheme()); const [theme, setTheme] = useState(getInitialTheme(config.themeConfig));
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [profile, setProfile] = useState(null); const [profile, setProfile] = useState(null);
const [repo, setRepo] = useState(null); const [repo, setRepo] = useState(null);
@ -31,7 +31,7 @@ const GitProfile = () => {
}, [theme]); }, [theme]);
useEffect(() => { useEffect(() => {
setupHotjar(); setupHotjar(config.hotjar);
}, []); }, []);
const loadData = useCallback(() => { const loadData = useCallback(() => {
@ -113,7 +113,12 @@ const GitProfile = () => {
return ( return (
<HelmetProvider> <HelmetProvider>
<HeadTagEditor profile={profile} theme={theme} /> <HeadTagEditor
profile={profile}
theme={theme}
googleAnalytics={config.googleAnalytics}
social={config.social}
/>
<div className="fade-in h-screen"> <div className="fade-in h-screen">
{error ? ( {error ? (
<ErrorPage <ErrorPage
@ -128,8 +133,7 @@ const GitProfile = () => {
subTitle={ subTitle={
error === 404 ? ( error === 404 ? (
<p> <p>
Please provide correct github username in{' '} Please provide correct github username in <code>config</code>
<code>gitprofile.config.js</code>
</p> </p>
) : error === 429 ? ( ) : error === 429 ? (
<p> <p>
@ -159,19 +163,37 @@ const GitProfile = () => {
theme={theme} theme={theme}
setTheme={setTheme} setTheme={setTheme}
loading={loading} loading={loading}
themeConfig={config.themeConfig}
/> />
)} )}
<AvatarCard profile={profile} loading={loading} /> <AvatarCard profile={profile} loading={loading} />
<Details profile={profile} loading={loading} /> <Details
<Skill loading={loading} /> profile={profile}
<Experience loading={loading} /> loading={loading}
<Education loading={loading} /> github={config.github}
social={config.social}
/>
<Skill loading={loading} skills={config.skills} />
<Experience
loading={loading}
experiences={config.experiences}
/>
<Education loading={loading} education={config.education} />
</div> </div>
</div> </div>
<div className="lg:col-span-2 col-span-1"> <div className="lg:col-span-2 col-span-1">
<div className="grid grid-cols-1 gap-6"> <div className="grid grid-cols-1 gap-6">
<Project repo={repo} loading={loading} /> <Project
<Blog loading={loading} /> repo={repo}
loading={loading}
github={config.github}
googleAnalytics={config.googleAnalytics}
/>
<Blog
loading={loading}
googleAnalytics={config.googleAnalytics}
blog={config.blog}
/>
</div> </div>
</div> </div>
</div> </div>
@ -204,4 +226,66 @@ const GitProfile = () => {
); );
}; };
GitProfile.propTypes = {
config: PropTypes.shape({
github: PropTypes.shape({
username: PropTypes.string.isRequired,
sortBy: PropTypes.oneOf(['stars', 'updated']).isRequired,
limit: PropTypes.number.isRequired,
exclude: PropTypes.shape({
forks: PropTypes.bool.isRequired,
projects: PropTypes.array.isRequired,
}).isRequired,
}).isRequired,
social: PropTypes.shape({
linkedin: PropTypes.string,
twitter: PropTypes.string,
facebook: PropTypes.string,
dribbble: PropTypes.string,
behance: PropTypes.string,
medium: PropTypes.string,
devto: PropTypes.string,
website: PropTypes.string,
phone: PropTypes.string,
email: PropTypes.string,
}).isRequired,
skills: PropTypes.array.isRequired,
experiences: PropTypes.arrayOf(
PropTypes.shape({
company: PropTypes.string,
position: PropTypes.string,
from: PropTypes.string,
to: PropTypes.string,
})
).isRequired,
education: PropTypes.arrayOf(
PropTypes.shape({
institution: PropTypes.string,
degree: PropTypes.string,
from: PropTypes.string,
to: PropTypes.string,
})
).isRequired,
blog: PropTypes.shape({
source: PropTypes.string,
username: PropTypes.string,
limit: PropTypes.number,
}).isRequired,
googleAnalytics: PropTypes.shape({
id: PropTypes.string,
}).isRequired,
hotjar: PropTypes.shape({
id: PropTypes.string,
snippetVersion: PropTypes.number,
}).isRequired,
themeConfig: PropTypes.shape({
default: PropTypes.string.isRequired,
disableSwitch: PropTypes.bool.isRequired,
respectPrefersColorScheme: PropTypes.bool.isRequired,
themes: PropTypes.array.isRequired,
customTheme: PropTypes.object.isRequired,
}).isRequired,
}).isRequired,
};
export default GitProfile; export default GitProfile;

View File

@ -54,7 +54,7 @@ const AvatarCard = ({ profile, loading }) => {
AvatarCard.propTypes = { AvatarCard.propTypes = {
profile: PropTypes.object, profile: PropTypes.object,
loading: PropTypes.bool, loading: PropTypes.bool.isRequired,
}; };
export default AvatarCard; export default AvatarCard;

View File

@ -4,15 +4,14 @@ import { Fragment, useEffect, useState } from 'react';
import { ga, skeleton } from '../../helpers/utils'; import { ga, skeleton } from '../../helpers/utils';
import LazyImage from '../lazy-image'; import LazyImage from '../lazy-image';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import config from '../../../gitprofile.config';
const displaySection = () => { const displaySection = (blog) => {
if ( if (
typeof config.blog !== 'undefined' && typeof blog !== 'undefined' &&
typeof config.blog.source !== 'undefined' && typeof blog.source !== 'undefined' &&
typeof config.blog.username !== 'undefined' && typeof blog.username !== 'undefined' &&
config.blog.source && blog.source &&
config.blog.username blog.username
) { ) {
return true; return true;
} else { } else {
@ -20,20 +19,20 @@ const displaySection = () => {
} }
}; };
const Blog = ({ loading }) => { const Blog = ({ loading, blog, googleAnalytics }) => {
const [articles, setArticles] = useState(null); const [articles, setArticles] = useState(null);
useEffect(() => { useEffect(() => {
if (displaySection()) { if (displaySection(blog)) {
if (config.blog.source === 'medium') { if (blog.source === 'medium') {
getMediumArticle({ getMediumArticle({
user: config.blog.username, user: blog.username,
}).then((res) => { }).then((res) => {
setArticles(res); setArticles(res);
}); });
} else if (config.blog.source === 'dev.to') { } else if (blog.source === 'dev.to') {
getDevtoArticle({ getDevtoArticle({
user: config.blog.username, user: blog.username,
}).then((res) => { }).then((res) => {
setArticles(res); setArticles(res);
}); });
@ -43,7 +42,7 @@ const Blog = ({ loading }) => {
const renderSkeleton = () => { const renderSkeleton = () => {
let array = []; let array = [];
for (let index = 0; index < config.blog.limit; index++) { for (let index = 0; index < blog.limit; index++) {
array.push( array.push(
<div className="card shadow-lg compact bg-base-100" key={index}> <div className="card shadow-lg compact bg-base-100" key={index}>
<div className="p-8 h-full w-full"> <div className="p-8 h-full w-full">
@ -101,7 +100,7 @@ const Blog = ({ loading }) => {
const renderArticles = () => { const renderArticles = () => {
return ( return (
articles && articles &&
articles.slice(0, config.blog.limit).map((article, index) => ( articles.slice(0, blog.limit).map((article, index) => (
<a <a
className="card shadow-lg compact bg-base-100 cursor-pointer" className="card shadow-lg compact bg-base-100 cursor-pointer"
key={index} key={index}
@ -110,7 +109,7 @@ const Blog = ({ loading }) => {
e.preventDefault(); e.preventDefault();
try { try {
if (config.googleAnalytics?.id) { if (googleAnalytics?.id) {
ga.event({ ga.event({
action: 'Click Blog Post', action: 'Click Blog Post',
params: { params: {
@ -174,7 +173,7 @@ const Blog = ({ loading }) => {
return ( return (
<Fragment> <Fragment>
{displaySection() && ( {displaySection(blog) && (
<div className="col-span-1 lg:col-span-2"> <div className="col-span-1 lg:col-span-2">
<div className="grid grid-cols-2 gap-6"> <div className="grid grid-cols-2 gap-6">
<div className="col-span-2"> <div className="col-span-2">
@ -207,7 +206,9 @@ const Blog = ({ loading }) => {
}; };
Blog.propTypes = { Blog.propTypes = {
loading: PropTypes.bool, loading: PropTypes.bool.isRequired,
blog: PropTypes.object.isRequired,
googleAnalytics: PropTypes.object.isRequired,
}; };
export default Blog; export default Blog;

View File

@ -14,7 +14,6 @@ import {
} from 'react-icons/fa'; } from 'react-icons/fa';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { skeleton } from '../../helpers/utils'; import { skeleton } from '../../helpers/utils';
import config from '../../../gitprofile.config';
const ListItem = ({ icon, title, value, link, skeleton = false }) => { const ListItem = ({ icon, title, value, link, skeleton = false }) => {
return ( return (
@ -43,7 +42,7 @@ const ListItem = ({ icon, title, value, link, skeleton = false }) => {
); );
}; };
const Details = ({ profile, loading }) => { const Details = ({ profile, loading, social, github }) => {
const renderSkeleton = () => { const renderSkeleton = () => {
let array = []; let array = [];
for (let index = 0; index < 4; index++) { for (let index = 0; index < 4; index++) {
@ -86,99 +85,89 @@ const Details = ({ profile, loading }) => {
<ListItem <ListItem
icon={<AiFillGithub className="mr-2" />} icon={<AiFillGithub className="mr-2" />}
title="GitHub:" title="GitHub:"
value={config.github.username} value={github.username}
link={`https://github.com/${config.github.username}`} link={`https://github.com/${github.username}`}
/> />
{typeof config.social.twitter !== 'undefined' && {typeof social.twitter !== 'undefined' && social.twitter && (
config.social.twitter && ( <ListItem
<ListItem icon={<SiTwitter className="mr-2" />}
icon={<SiTwitter className="mr-2" />} title="Twitter:"
title="Twitter:" value={social.twitter}
value={config.social.twitter} link={`https://twitter.com/${social.twitter}`}
link={`https://twitter.com/${config.social.twitter}`} />
/> )}
)} {typeof social.linkedin !== 'undefined' && social.linkedin && (
{typeof config.social.linkedin !== 'undefined' && <ListItem
config.social.linkedin && ( icon={<GrLinkedinOption className="mr-2" />}
<ListItem title="LinkedIn:"
icon={<GrLinkedinOption className="mr-2" />} value={social.linkedin}
title="LinkedIn:" link={`https://www.linkedin.com/in/${social.linkedin}`}
value={config.social.linkedin} />
link={`https://www.linkedin.com/in/${config.social.linkedin}`} )}
/> {typeof social.dribbble !== 'undefined' && social.dribbble && (
)} <ListItem
{typeof config.social.dribbble !== 'undefined' && icon={<CgDribbble className="mr-2" />}
config.social.dribbble && ( title="Dribbble:"
<ListItem value={social.dribbble}
icon={<CgDribbble className="mr-2" />} link={`https://dribbble.com/${social.dribbble}`}
title="Dribbble:" />
value={config.social.dribbble} )}
link={`https://dribbble.com/${config.social.dribbble}`} {typeof social.behance !== 'undefined' && social.behance && (
/> <ListItem
)} icon={<FaBehanceSquare className="mr-2" />}
{typeof config.social.behance !== 'undefined' && title="Behance:"
config.social.behance && ( value={social.behance}
<ListItem link={`https://www.behance.net/${social.behance}`}
icon={<FaBehanceSquare className="mr-2" />} />
title="Behance:" )}
value={config.social.behance} {typeof social.facebook !== 'undefined' && social.facebook && (
link={`https://www.behance.net/${config.social.behance}`} <ListItem
/> icon={<FaFacebook className="mr-2" />}
)} title="Facebook:"
{typeof config.social.facebook !== 'undefined' && value={social.facebook}
config.social.facebook && ( link={`https://www.facebook.com/${social.facebook}`}
<ListItem />
icon={<FaFacebook className="mr-2" />} )}
title="Facebook:" {typeof social.medium !== 'undefined' && social.medium && (
value={config.social.facebook} <ListItem
link={`https://www.facebook.com/${config.social.facebook}`} icon={<AiFillMediumSquare className="mr-2" />}
/> title="Medium:"
)} value={social.medium}
{typeof config.social.medium !== 'undefined' && link={`https://medium.com/@${social.medium}`}
config.social.medium && ( />
<ListItem )}
icon={<AiFillMediumSquare className="mr-2" />} {typeof social.devto !== 'undefined' && social.devto && (
title="Medium:" <ListItem
value={config.social.medium} icon={<FaDev className="mr-2" />}
link={`https://medium.com/@${config.social.medium}`} title="Dev:"
/> value={social.devto}
)} link={`https://dev.to/${social.devto}`}
{typeof config.social.devto !== 'undefined' && />
config.social.devto && ( )}
<ListItem {typeof social.website !== 'undefined' && social.website && (
icon={<FaDev className="mr-2" />} <ListItem
title="Dev:" icon={<FaGlobe className="mr-2" />}
value={config.social.devto} title="Website:"
link={`https://dev.to/${config.social.devto}`} value={social.website}
/> link={social.website}
)} />
{typeof config.social.website !== 'undefined' && )}
config.social.website && ( {typeof social.phone !== 'undefined' && social.phone && (
<ListItem <ListItem
icon={<FaGlobe className="mr-2" />} icon={<RiPhoneFill className="mr-2" />}
title="Website:" title="Phone:"
value={config.social.website} value={social.phone}
link={config.social.website} link={`tel:${social.phone}`}
/> />
)} )}
{typeof config.social.phone !== 'undefined' && {typeof social.email !== 'undefined' && social.email && (
config.social.phone && ( <ListItem
<ListItem icon={<MdMail className="mr-2" />}
icon={<RiPhoneFill className="mr-2" />} title="Email:"
title="Phone:" value={social.email}
value={config.social.phone} link={`mailto:${social.email}`}
link={`tel:${config.social.phone}`} />
/> )}
)}
{typeof config.social.email !== 'undefined' &&
config.social.email && (
<ListItem
icon={<MdMail className="mr-2" />}
title="Email:"
value={config.social.email}
link={`mailto:${config.social.email}`}
/>
)}
</Fragment> </Fragment>
)} )}
</div> </div>
@ -189,7 +178,9 @@ const Details = ({ profile, loading }) => {
Details.propTypes = { Details.propTypes = {
profile: PropTypes.object, profile: PropTypes.object,
loading: PropTypes.bool, loading: PropTypes.bool.isRequired,
social: PropTypes.object.isRequired,
github: PropTypes.object.isRequired,
}; };
ListItem.propTypes = { ListItem.propTypes = {

View File

@ -1,7 +1,6 @@
import { skeleton } from '../../helpers/utils'; import { skeleton } from '../../helpers/utils';
import { Fragment } from 'react'; import { Fragment } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import config from '../../../gitprofile.config';
const ListItem = ({ time, degree, institution }) => ( const ListItem = ({ time, degree, institution }) => (
<li className="mb-5 ml-4"> <li className="mb-5 ml-4">
@ -15,7 +14,7 @@ const ListItem = ({ time, degree, institution }) => (
</li> </li>
); );
const Education = ({ loading }) => { const Education = ({ loading, education }) => {
const renderSkeleton = () => { const renderSkeleton = () => {
let array = []; let array = [];
for (let index = 0; index < 2; index++) { for (let index = 0; index < 2; index++) {
@ -41,46 +40,46 @@ const Education = ({ loading }) => {
return ( return (
<> <>
{typeof config.education !== 'undefined' && {typeof education !== 'undefined' && education.length !== 0 && (
config.education.length !== 0 && ( <div className="card shadow-lg compact bg-base-100">
<div className="card shadow-lg compact bg-base-100"> <div className="card-body">
<div className="card-body"> <div className="mx-3">
<div className="mx-3"> <h5 className="card-title">
<h5 className="card-title"> {loading ? (
{loading ? ( skeleton({ width: 'w-32', height: 'h-8' })
skeleton({ width: 'w-32', height: 'h-8' }) ) : (
) : ( <span className="opacity-70">Education</span>
<span className="opacity-70">Education</span> )}
)} </h5>
</h5> </div>
</div> <div className="text-base-content text-opacity-60">
<div className="text-base-content text-opacity-60"> <ol className="relative border-l border-base-300 border-opacity-30 my-2 mx-4">
<ol className="relative border-l border-base-300 border-opacity-30 my-2 mx-4"> {loading ? (
{loading ? ( renderSkeleton()
renderSkeleton() ) : (
) : ( <Fragment>
<Fragment> {education.map((item, index) => (
{config.education.map((item, index) => ( <ListItem
<ListItem key={index}
key={index} time={`${item.from} - ${item.to}`}
time={`${item.from} - ${item.to}`} degree={item.degree}
degree={item.degree} institution={item.institution}
institution={item.institution} />
/> ))}
))} </Fragment>
</Fragment> )}
)} </ol>
</ol>
</div>
</div> </div>
</div> </div>
)} </div>
)}
</> </>
); );
}; };
Education.propTypes = { Education.propTypes = {
loading: PropTypes.bool, loading: PropTypes.bool.isRequired,
education: PropTypes.array.isRequired,
}; };
ListItem.propTypes = { ListItem.propTypes = {

View File

@ -1,7 +1,6 @@
import { skeleton } from '../../helpers/utils'; import { skeleton } from '../../helpers/utils';
import { Fragment } from 'react'; import { Fragment } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import config from '../../../gitprofile.config';
const ListItem = ({ time, position, company }) => ( const ListItem = ({ time, position, company }) => (
<li className="mb-5 ml-4"> <li className="mb-5 ml-4">
@ -15,7 +14,7 @@ const ListItem = ({ time, position, company }) => (
</li> </li>
); );
const Experience = ({ loading }) => { const Experience = ({ experiences, loading }) => {
const renderSkeleton = () => { const renderSkeleton = () => {
let array = []; let array = [];
for (let index = 0; index < 2; index++) { for (let index = 0; index < 2; index++) {
@ -41,40 +40,39 @@ const Experience = ({ loading }) => {
return ( return (
<> <>
{typeof config.experiences !== 'undefined' && {typeof experiences !== 'undefined' && experiences.length !== 0 && (
config.experiences.length !== 0 && ( <div className="card shadow-lg compact bg-base-100">
<div className="card shadow-lg compact bg-base-100"> <div className="card-body">
<div className="card-body"> <div className="mx-3">
<div className="mx-3"> <h5 className="card-title">
<h5 className="card-title"> {loading ? (
{loading ? ( skeleton({ width: 'w-32', height: 'h-8' })
skeleton({ width: 'w-32', height: 'h-8' }) ) : (
) : ( <span className="opacity-70">Experience</span>
<span className="opacity-70">Experience</span> )}
)} </h5>
</h5> </div>
</div> <div className="text-base-content text-opacity-60">
<div className="text-base-content text-opacity-60"> <ol className="relative border-l border-base-300 border-opacity-30 my-2 mx-4">
<ol className="relative border-l border-base-300 border-opacity-30 my-2 mx-4"> {loading ? (
{loading ? ( renderSkeleton()
renderSkeleton() ) : (
) : ( <Fragment>
<Fragment> {experiences.map((experience, index) => (
{config.experiences.map((experience, index) => ( <ListItem
<ListItem key={index}
key={index} time={`${experience.from} - ${experience.to}`}
time={`${experience.from} - ${experience.to}`} position={experience.position}
position={experience.position} company={experience.company}
company={experience.company} />
/> ))}
))} </Fragment>
</Fragment> )}
)} </ol>
</ol>
</div>
</div> </div>
</div> </div>
)} </div>
)}
</> </>
); );
}; };
@ -86,7 +84,8 @@ ListItem.propTypes = {
}; };
Experience.propTypes = { Experience.propTypes = {
loading: PropTypes.bool, experiences: PropTypes.array.isRequired,
loading: PropTypes.bool.isRequired,
}; };
export default Experience; export default Experience;

View File

@ -2,25 +2,24 @@ import { Fragment } from 'react';
import { Helmet } from 'react-helmet-async'; import { Helmet } from 'react-helmet-async';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { isThemeDarkish } from '../../helpers/utils'; import { isThemeDarkish } from '../../helpers/utils';
import config from '../../../gitprofile.config';
const HeadTagEditor = ({ profile, theme }) => { const HeadTagEditor = ({ profile, theme, googleAnalytics, social }) => {
return ( return (
<Fragment> <Fragment>
{profile && ( {profile && (
<Helmet> <Helmet>
{config.googleAnalytics?.id && ( {googleAnalytics?.id && (
<script <script
async async
src={`https://www.googletagmanager.com/gtag/js?id=${config.googleAnalytics.id}`} src={`https://www.googletagmanager.com/gtag/js?id=${googleAnalytics.id}`}
></script> ></script>
)} )}
{config.googleAnalytics?.id && ( {googleAnalytics?.id && (
<script> <script>
{`window.dataLayer = window.dataLayer || []; {`window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);} function gtag(){dataLayer.push(arguments);}
gtag('js', new Date()); gtag('js', new Date());
gtag('config', '${config.googleAnalytics.id}');`} gtag('config', '${googleAnalytics.id}');`}
</script> </script>
)} )}
<title>Portfolio of {profile.name}</title> <title>Portfolio of {profile.name}</title>
@ -38,9 +37,7 @@ const HeadTagEditor = ({ profile, theme }) => {
<meta <meta
property="og:url" property="og:url"
content={ content={
typeof config.social.website !== 'undefined' typeof social.website !== 'undefined' ? social.website : ''
? config.social.website
: ''
} }
/> />
<meta property="og:type" content="website" /> <meta property="og:type" content="website" />
@ -60,7 +57,9 @@ const HeadTagEditor = ({ profile, theme }) => {
HeadTagEditor.propTypes = { HeadTagEditor.propTypes = {
profile: PropTypes.object, profile: PropTypes.object,
theme: PropTypes.string, theme: PropTypes.string.isRequired,
googleAnalytics: PropTypes.object.isRequired,
social: PropTypes.object.isRequired,
}; };
export default HeadTagEditor; export default HeadTagEditor;

View File

@ -2,12 +2,11 @@ import { Fragment } from 'react';
import { AiOutlineStar, AiOutlineFork } from 'react-icons/ai'; import { AiOutlineStar, AiOutlineFork } from 'react-icons/ai';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { ga, languageColor, skeleton } from '../../helpers/utils'; import { ga, languageColor, skeleton } from '../../helpers/utils';
import config from '../../../gitprofile.config';
const Project = ({ repo, loading }) => { const Project = ({ repo, loading, github, googleAnalytics }) => {
const renderSkeleton = () => { const renderSkeleton = () => {
let array = []; let array = [];
for (let index = 0; index < config.github.limit; index++) { for (let index = 0; index < github.limit; index++) {
array.push( array.push(
<div className="card shadow-lg compact bg-base-100" key={index}> <div className="card shadow-lg compact bg-base-100" key={index}>
<div className="flex justify-between flex-col p-8 h-full w-full"> <div className="flex justify-between flex-col p-8 h-full w-full">
@ -61,7 +60,7 @@ const Project = ({ repo, loading }) => {
e.preventDefault(); e.preventDefault();
try { try {
if (config.googleAnalytics?.id) { if (googleAnalytics?.id) {
ga.event({ ga.event({
action: 'Click project', action: 'Click project',
params: { params: {
@ -145,7 +144,7 @@ const Project = ({ repo, loading }) => {
skeleton({ width: 'w-10', height: 'h-5' }) skeleton({ width: 'w-10', height: 'h-5' })
) : ( ) : (
<a <a
href={`https://github.com/${config.github.username}?tab=repositories`} href={`https://github.com/${github.username}?tab=repositories`}
target="_blank" target="_blank"
rel="noreferrer" rel="noreferrer"
className="opacity-50" className="opacity-50"
@ -170,7 +169,9 @@ const Project = ({ repo, loading }) => {
Project.propTypes = { Project.propTypes = {
repo: PropTypes.array, repo: PropTypes.array,
loading: PropTypes.bool, loading: PropTypes.bool.isRequired,
github: PropTypes.object.isRequired,
googleAnalytics: PropTypes.object.isRequired,
}; };
export default Project; export default Project;

View File

@ -1,8 +1,7 @@
import { skeleton } from '../../helpers/utils'; import { skeleton } from '../../helpers/utils';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import config from '../../../gitprofile.config';
const Skill = ({ loading }) => { const Skill = ({ loading, skills }) => {
const renderSkeleton = () => { const renderSkeleton = () => {
let array = []; let array = [];
for (let index = 0; index < 12; index++) { for (let index = 0; index < 12; index++) {
@ -18,7 +17,7 @@ const Skill = ({ loading }) => {
return ( return (
<> <>
{typeof config.skills !== 'undefined' && config.skills.length !== 0 && ( {typeof skills !== 'undefined' && skills.length !== 0 && (
<div className="card shadow-lg compact bg-base-100"> <div className="card shadow-lg compact bg-base-100">
<div className="card-body"> <div className="card-body">
<div className="mx-3"> <div className="mx-3">
@ -34,7 +33,7 @@ const Skill = ({ loading }) => {
<div className="-m-1 flex flex-wrap justify-center"> <div className="-m-1 flex flex-wrap justify-center">
{loading {loading
? renderSkeleton() ? renderSkeleton()
: config.skills.map((skill, index) => ( : skills.map((skill, index) => (
<div <div
key={index} key={index}
className="m-1 text-xs inline-flex items-center font-bold leading-sm px-3 py-1 badge-primary bg-opacity-90 rounded-full" className="m-1 text-xs inline-flex items-center font-bold leading-sm px-3 py-1 badge-primary bg-opacity-90 rounded-full"
@ -52,7 +51,8 @@ const Skill = ({ loading }) => {
}; };
Skill.propTypes = { Skill.propTypes = {
loading: PropTypes.bool, loading: PropTypes.bool.isRequired,
skills: PropTypes.array.isRequired,
}; };
export default Skill; export default Skill;

View File

@ -1,9 +1,8 @@
import { AiOutlineControl } from 'react-icons/ai'; import { AiOutlineControl } from 'react-icons/ai';
import { skeleton } from '../../helpers/utils'; import { skeleton } from '../../helpers/utils';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import config from '../../../gitprofile.config';
const ThemeChanger = ({ theme, setTheme, loading }) => { const ThemeChanger = ({ theme, setTheme, loading, themeConfig }) => {
const changeTheme = (e, selectedTheme) => { const changeTheme = (e, selectedTheme) => {
e.preventDefault(); e.preventDefault();
document.querySelector('html').setAttribute('data-theme', selectedTheme); document.querySelector('html').setAttribute('data-theme', selectedTheme);
@ -28,7 +27,7 @@ const ThemeChanger = ({ theme, setTheme, loading }) => {
<span className="text-base-content text-opacity-40 capitalize text-sm"> <span className="text-base-content text-opacity-40 capitalize text-sm">
{loading {loading
? skeleton({ width: 'w-16', height: 'h-5' }) ? skeleton({ width: 'w-16', height: 'h-5' })
: theme === config.themeConfig.default : theme === themeConfig.default
? 'Default' ? 'Default'
: theme} : theme}
</span> </span>
@ -62,9 +61,9 @@ const ThemeChanger = ({ theme, setTheme, loading }) => {
> >
<ul className="p-4 menu compact"> <ul className="p-4 menu compact">
{[ {[
config.themeConfig.default, themeConfig.default,
...config.themeConfig.themes.filter( ...themeConfig.themes.filter(
(item) => item !== config.themeConfig.default (item) => item !== themeConfig.default
), ),
].map((item, index) => ( ].map((item, index) => (
<li key={index}> <li key={index}>
@ -74,9 +73,7 @@ const ThemeChanger = ({ theme, setTheme, loading }) => {
className={`${theme === item ? 'active' : ''}`} className={`${theme === item ? 'active' : ''}`}
> >
<span className="opacity-60 capitalize"> <span className="opacity-60 capitalize">
{item === config.themeConfig.default {item === themeConfig.default ? 'Default' : item}
? 'Default'
: item}
</span> </span>
</a> </a>
</li> </li>
@ -92,9 +89,10 @@ const ThemeChanger = ({ theme, setTheme, loading }) => {
}; };
ThemeChanger.propTypes = { ThemeChanger.propTypes = {
theme: PropTypes.string, theme: PropTypes.string.isRequired,
setTheme: PropTypes.func, setTheme: PropTypes.func.isRequired,
loading: PropTypes.bool, loading: PropTypes.bool.isRequired,
themeConfig: PropTypes.object.isRequired,
}; };
export default ThemeChanger; export default ThemeChanger;

View File

@ -1,32 +1,28 @@
import colors from '../data/colors.json'; import colors from '../data/colors.json';
import { hotjar } from 'react-hotjar'; import { hotjar } from 'react-hotjar';
import config from '../../gitprofile.config';
export const getInitialTheme = () => { export const getInitialTheme = (themeConfig) => {
if (config.themeConfig.disableSwitch) { if (themeConfig.disableSwitch) {
return config.themeConfig.default; return themeConfig.default;
} }
if ( if (
typeof window !== 'undefined' && typeof window !== 'undefined' &&
!(localStorage.getItem('gitprofile-theme') === null) && !(localStorage.getItem('gitprofile-theme') === null) &&
config.themeConfig.themes.includes(localStorage.getItem('gitprofile-theme')) themeConfig.themes.includes(localStorage.getItem('gitprofile-theme'))
) { ) {
let theme = localStorage.getItem('gitprofile-theme'); let theme = localStorage.getItem('gitprofile-theme');
return theme; return theme;
} }
if ( if (themeConfig.respectPrefersColorScheme && !themeConfig.disableSwitch) {
config.themeConfig.respectPrefersColorScheme &&
!config.themeConfig.disableSwitch
) {
return window.matchMedia('(prefers-color-scheme: dark)').matches return window.matchMedia('(prefers-color-scheme: dark)').matches
? 'dark' ? 'dark'
: config.themeConfig.default; : themeConfig.default;
} }
return config.themeConfig.default; return themeConfig.default;
}; };
export const skeleton = ({ export const skeleton = ({
@ -92,12 +88,12 @@ export const isThemeDarkish = (theme) => {
} }
}; };
export const setupHotjar = () => { export const setupHotjar = (hotjarConfig) => {
if (config.hotjar?.id) { if (hotjarConfig?.id) {
let snippetVersion = config.hotjar?.snippetVersion let snippetVersion = hotjarConfig?.snippetVersion
? config.hotjar?.snippetVersion ? hotjarConfig?.snippetVersion
: 6; : 6;
hotjar.initialize(config.hotjar.id, snippetVersion); hotjar.initialize(hotjarConfig.id, snippetVersion);
} }
}; };