From 07e1e090a1765dba15a89cd20d5beb57a2a00756 Mon Sep 17 00:00:00 2001 From: Ariful Alam Date: Thu, 24 Mar 2022 22:22:45 +0600 Subject: [PATCH 01/33] Test run --- package.json | 9 +++++++++ src/App.jsx | 1 + src/{ => assets}/index.css | 0 src/main.jsx | 1 - vite.config.js | 13 +++++++++++++ 5 files changed, 23 insertions(+), 1 deletion(-) rename src/{ => assets}/index.css (100%) diff --git a/package.json b/package.json index 2357f3b..e660010 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,15 @@ "type": "git", "url": "https://github.com/arifszn/gitprofile.git" }, + "files": ["dist"], + "main": "./dist/my-lib.umd.js", + "module": "./dist/my-lib.es.js", + "exports": { + ".": { + "import": "./dist/my-lib.es.js", + "require": "./dist/my-lib.umd.js" + } + }, "scripts": { "dev": "vite", "build": "vite build", diff --git a/src/App.jsx b/src/App.jsx index dd2b845..f941509 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -13,6 +13,7 @@ import Project from './components/project'; import Blog from './components/blog'; import { getInitialTheme, setupHotjar } from './helpers/utils'; import config from '../gitprofile.config'; +import './assets/index.css'; function App() { const [theme, setTheme] = useState(getInitialTheme()); diff --git a/src/index.css b/src/assets/index.css similarity index 100% rename from src/index.css rename to src/assets/index.css diff --git a/src/main.jsx b/src/main.jsx index d88f50c..5c33570 100644 --- a/src/main.jsx +++ b/src/main.jsx @@ -1,6 +1,5 @@ import React from 'react'; import ReactDOM from 'react-dom'; -import './index.css'; import App from './App'; import { HelmetProvider } from 'react-helmet-async'; diff --git a/vite.config.js b/vite.config.js index ff568c7..515c326 100644 --- a/vite.config.js +++ b/vite.config.js @@ -1,6 +1,7 @@ import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react'; import postcss from './postcss.config.js'; +import path from 'path'; // https://vitejs.dev/config/ export default defineConfig({ @@ -11,4 +12,16 @@ export default defineConfig({ css: { postcss, }, + build: { + lib: { + entry: path.resolve(__dirname, 'src/App.jsx'), + name: 'MyLib', + fileName: (format) => `my-lib.${format}.js`, + }, + rollupOptions: { + // make sure to externalize deps that shouldn't be bundled + // into your library + external: ['react', 'react-dom'], + }, + }, }); From b3715bca0ac682d32de44445839f6aa779f4cc0a Mon Sep 17 00:00:00 2001 From: Ariful Alam Date: Thu, 24 Mar 2022 23:26:36 +0600 Subject: [PATCH 02/33] Move entry file to components folder --- src/App.jsx | 203 +-------------------------------- src/components/GitProfile.jsx | 207 ++++++++++++++++++++++++++++++++++ src/main.jsx | 5 +- 3 files changed, 210 insertions(+), 205 deletions(-) create mode 100644 src/components/GitProfile.jsx diff --git a/src/App.jsx b/src/App.jsx index f941509..a8c2e3e 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,206 +1,7 @@ -import axios from 'axios'; -import { Fragment, useCallback, useEffect, useState } from 'react'; -import moment from 'moment'; -import HeadTagEditor from './components/head-tag-editor'; -import ErrorPage from './components/error-page'; -import ThemeChanger from './components/theme-changer'; -import AvatarCard from './components/avatar-card'; -import Details from './components/details'; -import Skill from './components/skill'; -import Experience from './components/experience'; -import Education from './components/education'; -import Project from './components/project'; -import Blog from './components/blog'; -import { getInitialTheme, setupHotjar } from './helpers/utils'; -import config from '../gitprofile.config'; -import './assets/index.css'; +import GitProfile from './components/GitProfile'; function App() { - const [theme, setTheme] = useState(getInitialTheme()); - const [loading, setLoading] = useState(true); - const [profile, setProfile] = useState(null); - const [repo, setRepo] = useState(null); - const [error, setError] = useState(null); - const [rateLimit, setRateLimit] = useState(null); - - useEffect(() => { - if (theme) { - document.documentElement.setAttribute('data-theme', theme); - } - }, [theme]); - - useEffect(() => { - setupHotjar(); - }, []); - - const loadData = useCallback(() => { - axios - .get(`https://api.github.com/users/${config.github.username}`) - .then((response) => { - let data = response.data; - - let profileData = { - avatar: data.avatar_url, - name: data.name ? data.name : '', - bio: data.bio ? data.bio : '', - location: data.location ? data.location : '', - company: data.company ? data.company : '', - }; - - setProfile(profileData); - }) - .then(() => { - let excludeRepo = ``; - - config.github.exclude.projects.forEach((project) => { - excludeRepo += `+-repo:${config.github.username}/${project}`; - }); - - let query = `user:${config.github.username}+fork:${!config.github - .exclude.forks}${excludeRepo}`; - - let url = `https://api.github.com/search/repositories?q=${query}&sort=${config.github.sortBy}&per_page=${config.github.limit}&type=Repositories`; - - axios - .get(url, { - headers: { - 'Content-Type': 'application/vnd.github.v3+json', - }, - }) - .then((response) => { - let data = response.data; - - setRepo(data.items); - }) - .catch((error) => { - handleError(error); - }); - }) - .catch((error) => { - handleError(error); - }) - .finally(() => { - setLoading(false); - }); - }, [setLoading]); - - useEffect(() => { - loadData(); - }, [loadData]); - - const handleError = (error) => { - console.error('Error:', error); - try { - setRateLimit({ - remaining: error.response.headers['x-ratelimit-remaining'], - reset: moment( - new Date(error.response.headers['x-ratelimit-reset'] * 1000) - ).fromNow(), - }); - - if (error.response.status === 403) { - setError(429); - } else if (error.response.status === 404) { - setError(404); - } else { - setError(500); - } - } catch (error2) { - setError(500); - } - }; - - return ( - - -
- {error ? ( - - Please provide correct github username in{' '} - gitprofile.config.js -

- ) : error === 429 ? ( -

- Oh no, you hit the{' '} - - rate limit - - ! Try again later{rateLimit && ` ${rateLimit.reset}`}. -

- ) : ( - `Something went wrong` - ) - } - /> - ) : ( - -
-
-
-
- {!config.themeConfig.disableSwitch && ( - - )} - -
- - - -
-
-
-
- - -
-
-
-
- {/* DO NOT REMOVE/MODIFY THE FOOTER. FOR MORE INFO https://github.com/arifszn/gitprofile#-please-read */} - -
- )} -
-
- ); + return ; } export default App; diff --git a/src/components/GitProfile.jsx b/src/components/GitProfile.jsx new file mode 100644 index 0000000..c651e80 --- /dev/null +++ b/src/components/GitProfile.jsx @@ -0,0 +1,207 @@ +import axios from 'axios'; +import { Fragment, useCallback, useEffect, useState } from 'react'; +import moment from 'moment'; +import HeadTagEditor from './head-tag-editor'; +import ErrorPage from './error-page'; +import ThemeChanger from './theme-changer'; +import AvatarCard from './avatar-card'; +import Details from './details'; +import Skill from './skill'; +import Experience from './experience'; +import Education from './education'; +import Project from './project'; +import Blog from './blog'; +import { getInitialTheme, setupHotjar } from '../helpers/utils'; +import config from '../../gitprofile.config'; +import '../assets/index.css'; +import { HelmetProvider } from 'react-helmet-async'; + +const GitProfile = () => { + const [theme, setTheme] = useState(getInitialTheme()); + const [loading, setLoading] = useState(true); + const [profile, setProfile] = useState(null); + const [repo, setRepo] = useState(null); + const [error, setError] = useState(null); + const [rateLimit, setRateLimit] = useState(null); + + useEffect(() => { + if (theme) { + document.documentElement.setAttribute('data-theme', theme); + } + }, [theme]); + + useEffect(() => { + setupHotjar(); + }, []); + + const loadData = useCallback(() => { + axios + .get(`https://api.github.com/users/${config.github.username}`) + .then((response) => { + let data = response.data; + + let profileData = { + avatar: data.avatar_url, + name: data.name ? data.name : '', + bio: data.bio ? data.bio : '', + location: data.location ? data.location : '', + company: data.company ? data.company : '', + }; + + setProfile(profileData); + }) + .then(() => { + let excludeRepo = ``; + + config.github.exclude.projects.forEach((project) => { + excludeRepo += `+-repo:${config.github.username}/${project}`; + }); + + let query = `user:${config.github.username}+fork:${!config.github + .exclude.forks}${excludeRepo}`; + + let url = `https://api.github.com/search/repositories?q=${query}&sort=${config.github.sortBy}&per_page=${config.github.limit}&type=Repositories`; + + axios + .get(url, { + headers: { + 'Content-Type': 'application/vnd.github.v3+json', + }, + }) + .then((response) => { + let data = response.data; + + setRepo(data.items); + }) + .catch((error) => { + handleError(error); + }); + }) + .catch((error) => { + handleError(error); + }) + .finally(() => { + setLoading(false); + }); + }, [setLoading]); + + useEffect(() => { + loadData(); + }, [loadData]); + + const handleError = (error) => { + console.error('Error:', error); + try { + setRateLimit({ + remaining: error.response.headers['x-ratelimit-remaining'], + reset: moment( + new Date(error.response.headers['x-ratelimit-reset'] * 1000) + ).fromNow(), + }); + + if (error.response.status === 403) { + setError(429); + } else if (error.response.status === 404) { + setError(404); + } else { + setError(500); + } + } catch (error2) { + setError(500); + } + }; + + return ( + + +
+ {error ? ( + + Please provide correct github username in{' '} + gitprofile.config.js +

+ ) : error === 429 ? ( +

+ Oh no, you hit the{' '} + + rate limit + + ! Try again later{rateLimit && ` ${rateLimit.reset}`}. +

+ ) : ( + `Something went wrong` + ) + } + /> + ) : ( + +
+
+
+
+ {!config.themeConfig.disableSwitch && ( + + )} + +
+ + + +
+
+
+
+ + +
+
+
+
+ {/* DO NOT REMOVE/MODIFY THE FOOTER. FOR MORE INFO https://github.com/arifszn/gitprofile#-please-read */} + +
+ )} +
+
+ ); +}; + +export default GitProfile; diff --git a/src/main.jsx b/src/main.jsx index 5c33570..c1f31c5 100644 --- a/src/main.jsx +++ b/src/main.jsx @@ -1,13 +1,10 @@ import React from 'react'; import ReactDOM from 'react-dom'; import App from './App'; -import { HelmetProvider } from 'react-helmet-async'; ReactDOM.render( - - - + , document.getElementById('root') ); From 7964ae57bbe231cfb9c12d0a1631dba91f57d716 Mon Sep 17 00:00:00 2001 From: Ariful Alam Date: Thu, 24 Mar 2022 23:32:27 +0600 Subject: [PATCH 03/33] Create package.config.js --- package.config.js | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 package.config.js diff --git a/package.config.js b/package.config.js new file mode 100644 index 0000000..e0354e4 --- /dev/null +++ b/package.config.js @@ -0,0 +1,27 @@ +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; +import postcss from './postcss.config.js'; +import path from 'path'; + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [react()], + css: { + postcss, + }, + build: { + lib: { + entry: path.resolve(__dirname, 'src/components/GitProfile.jsx'), + name: 'GitProfile', + fileName: (format) => `gitprofile.${format}.js`, + }, + rollupOptions: { + external: ['react', 'react-dom'], + output: { + globals: { + react: 'React', + }, + }, + }, + }, +}); From 39125b018c7d26e614831d5daae48b52b8a6f53f Mon Sep 17 00:00:00 2001 From: Ariful Alam Date: Thu, 24 Mar 2022 23:34:19 +0600 Subject: [PATCH 04/33] Add `build:package` script --- package.json | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index e660010..0841339 100644 --- a/package.json +++ b/package.json @@ -9,19 +9,22 @@ "type": "git", "url": "https://github.com/arifszn/gitprofile.git" }, - "files": ["dist"], - "main": "./dist/my-lib.umd.js", - "module": "./dist/my-lib.es.js", + "files": [ + "dist" + ], + "main": "./dist/gitprofile.umd.js", + "module": "./dist/gitprofile.es.js", "exports": { ".": { - "import": "./dist/my-lib.es.js", - "require": "./dist/my-lib.umd.js" + "import": "./dist/gitprofile.es.js", + "require": "./dist/gitprofile.umd.js" } }, "scripts": { "dev": "vite", "build": "vite build", "preview": "vite preview", + "build:package": "vite build --config package.config.js", "lint": "eslint --ext .js,.jsx .", "lint:fix": "eslint --ext .js,.jsx --fix .", "prettier": "prettier --check './**/*.{js,jsx,ts,tsx,css,md,json}' --config ./.prettierrc", From bbac82bd1607c65aee4771c4df9c0a4f2a7bd08a Mon Sep 17 00:00:00 2001 From: Ariful Alam Date: Thu, 24 Mar 2022 23:36:26 +0600 Subject: [PATCH 05/33] Remove custom build option --- vite.config.js | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/vite.config.js b/vite.config.js index 515c326..ff568c7 100644 --- a/vite.config.js +++ b/vite.config.js @@ -1,7 +1,6 @@ import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react'; import postcss from './postcss.config.js'; -import path from 'path'; // https://vitejs.dev/config/ export default defineConfig({ @@ -12,16 +11,4 @@ export default defineConfig({ css: { postcss, }, - build: { - lib: { - entry: path.resolve(__dirname, 'src/App.jsx'), - name: 'MyLib', - fileName: (format) => `my-lib.${format}.js`, - }, - rollupOptions: { - // make sure to externalize deps that shouldn't be bundled - // into your library - external: ['react', 'react-dom'], - }, - }, }); From 505871198570295f3ff7d0ec4509bf7eb824073c Mon Sep 17 00:00:00 2001 From: Ariful Alam Date: Fri, 25 Mar 2022 18:45:30 +0600 Subject: [PATCH 06/33] Include style.css in `exports` --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 0841339..eeb535c 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,8 @@ ".": { "import": "./dist/gitprofile.es.js", "require": "./dist/gitprofile.umd.js" - } + }, + "./dist/style.css": "./dist/style.css" }, "scripts": { "dev": "vite", From c6066fd038f3a3f4dfb30ce47c3e4d376a9de376 Mon Sep 17 00:00:00 2001 From: Ariful Alam Date: Fri, 25 Mar 2022 18:47:28 +0600 Subject: [PATCH 07/33] Support SSR --- src/components/theme-changer/index.jsx | 4 +++- src/helpers/utils.jsx | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/components/theme-changer/index.jsx b/src/components/theme-changer/index.jsx index 67d9baa..77f86a7 100644 --- a/src/components/theme-changer/index.jsx +++ b/src/components/theme-changer/index.jsx @@ -7,7 +7,9 @@ const ThemeChanger = ({ theme, setTheme, loading }) => { const changeTheme = (e, selectedTheme) => { e.preventDefault(); document.querySelector('html').setAttribute('data-theme', selectedTheme); - localStorage.setItem('gitprofile-theme', selectedTheme); + + typeof window !== 'undefined' && + localStorage.setItem('gitprofile-theme', selectedTheme); setTheme(selectedTheme); }; diff --git a/src/helpers/utils.jsx b/src/helpers/utils.jsx index efa1272..d13f7f0 100644 --- a/src/helpers/utils.jsx +++ b/src/helpers/utils.jsx @@ -8,6 +8,7 @@ export const getInitialTheme = () => { } if ( + typeof window !== 'undefined' && !(localStorage.getItem('gitprofile-theme') === null) && config.themeConfig.themes.includes(localStorage.getItem('gitprofile-theme')) ) { From 82eb282e179fc3f6ddaaf06f976657f76cd7e9ce Mon Sep 17 00:00:00 2001 From: Ariful Alam Date: Fri, 25 Mar 2022 22:45:11 +0600 Subject: [PATCH 08/33] Pass config as props --- src/App.jsx | 3 +- src/components/GitProfile.jsx | 112 ++++++++++++-- src/components/avatar-card/index.jsx | 2 +- src/components/blog/index.jsx | 37 ++--- src/components/details/index.jsx | 181 +++++++++++------------ src/components/education/index.jsx | 69 +++++---- src/components/experience/index.jsx | 69 +++++---- src/components/head-tag-editor/index.jsx | 19 ++- src/components/project/index.jsx | 13 +- src/components/skill/index.jsx | 10 +- src/components/theme-changer/index.jsx | 22 ++- src/helpers/utils.jsx | 28 ++-- 12 files changed, 317 insertions(+), 248 deletions(-) diff --git a/src/App.jsx b/src/App.jsx index a8c2e3e..5a777f1 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,7 +1,8 @@ +import config from '../gitprofile.config'; import GitProfile from './components/GitProfile'; function App() { - return ; + return ; } export default App; diff --git a/src/components/GitProfile.jsx b/src/components/GitProfile.jsx index c651e80..e928956 100644 --- a/src/components/GitProfile.jsx +++ b/src/components/GitProfile.jsx @@ -12,12 +12,12 @@ import Education from './education'; import Project from './project'; import Blog from './blog'; import { getInitialTheme, setupHotjar } from '../helpers/utils'; -import config from '../../gitprofile.config'; -import '../assets/index.css'; import { HelmetProvider } from 'react-helmet-async'; +import PropTypes from 'prop-types'; +import '../assets/index.css'; -const GitProfile = () => { - const [theme, setTheme] = useState(getInitialTheme()); +const GitProfile = ({ config }) => { + const [theme, setTheme] = useState(getInitialTheme(config.themeConfig)); const [loading, setLoading] = useState(true); const [profile, setProfile] = useState(null); const [repo, setRepo] = useState(null); @@ -31,7 +31,7 @@ const GitProfile = () => { }, [theme]); useEffect(() => { - setupHotjar(); + setupHotjar(config.hotjar); }, []); const loadData = useCallback(() => { @@ -113,7 +113,12 @@ const GitProfile = () => { return ( - +
{error ? ( { subTitle={ error === 404 ? (

- Please provide correct github username in{' '} - gitprofile.config.js + Please provide correct github username in config

) : error === 429 ? (

@@ -159,19 +163,37 @@ const GitProfile = () => { theme={theme} setTheme={setTheme} loading={loading} + themeConfig={config.themeConfig} /> )} -

- - - +
+ + +
- - + +
@@ -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; diff --git a/src/components/avatar-card/index.jsx b/src/components/avatar-card/index.jsx index bcc94d1..8e8350b 100644 --- a/src/components/avatar-card/index.jsx +++ b/src/components/avatar-card/index.jsx @@ -54,7 +54,7 @@ const AvatarCard = ({ profile, loading }) => { AvatarCard.propTypes = { profile: PropTypes.object, - loading: PropTypes.bool, + loading: PropTypes.bool.isRequired, }; export default AvatarCard; diff --git a/src/components/blog/index.jsx b/src/components/blog/index.jsx index 7a7175e..a655cdc 100644 --- a/src/components/blog/index.jsx +++ b/src/components/blog/index.jsx @@ -4,15 +4,14 @@ import { Fragment, useEffect, useState } from 'react'; import { ga, skeleton } from '../../helpers/utils'; import LazyImage from '../lazy-image'; import PropTypes from 'prop-types'; -import config from '../../../gitprofile.config'; -const displaySection = () => { +const displaySection = (blog) => { if ( - typeof config.blog !== 'undefined' && - typeof config.blog.source !== 'undefined' && - typeof config.blog.username !== 'undefined' && - config.blog.source && - config.blog.username + typeof blog !== 'undefined' && + typeof blog.source !== 'undefined' && + typeof blog.username !== 'undefined' && + blog.source && + blog.username ) { return true; } else { @@ -20,20 +19,20 @@ const displaySection = () => { } }; -const Blog = ({ loading }) => { +const Blog = ({ loading, blog, googleAnalytics }) => { const [articles, setArticles] = useState(null); useEffect(() => { - if (displaySection()) { - if (config.blog.source === 'medium') { + if (displaySection(blog)) { + if (blog.source === 'medium') { getMediumArticle({ - user: config.blog.username, + user: blog.username, }).then((res) => { setArticles(res); }); - } else if (config.blog.source === 'dev.to') { + } else if (blog.source === 'dev.to') { getDevtoArticle({ - user: config.blog.username, + user: blog.username, }).then((res) => { setArticles(res); }); @@ -43,7 +42,7 @@ const Blog = ({ loading }) => { const renderSkeleton = () => { let array = []; - for (let index = 0; index < config.blog.limit; index++) { + for (let index = 0; index < blog.limit; index++) { array.push(
@@ -101,7 +100,7 @@ const Blog = ({ loading }) => { const renderArticles = () => { return ( articles && - articles.slice(0, config.blog.limit).map((article, index) => ( + articles.slice(0, blog.limit).map((article, index) => ( { e.preventDefault(); try { - if (config.googleAnalytics?.id) { + if (googleAnalytics?.id) { ga.event({ action: 'Click Blog Post', params: { @@ -174,7 +173,7 @@ const Blog = ({ loading }) => { return ( - {displaySection() && ( + {displaySection(blog) && (
@@ -207,7 +206,9 @@ const Blog = ({ loading }) => { }; Blog.propTypes = { - loading: PropTypes.bool, + loading: PropTypes.bool.isRequired, + blog: PropTypes.object.isRequired, + googleAnalytics: PropTypes.object.isRequired, }; export default Blog; diff --git a/src/components/details/index.jsx b/src/components/details/index.jsx index e18dcbf..6850ae8 100644 --- a/src/components/details/index.jsx +++ b/src/components/details/index.jsx @@ -14,7 +14,6 @@ import { } from 'react-icons/fa'; import PropTypes from 'prop-types'; import { skeleton } from '../../helpers/utils'; -import config from '../../../gitprofile.config'; const ListItem = ({ icon, title, value, link, skeleton = false }) => { 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 = () => { let array = []; for (let index = 0; index < 4; index++) { @@ -86,99 +85,89 @@ const Details = ({ profile, loading }) => { } title="GitHub:" - value={config.github.username} - link={`https://github.com/${config.github.username}`} + value={github.username} + link={`https://github.com/${github.username}`} /> - {typeof config.social.twitter !== 'undefined' && - config.social.twitter && ( - } - title="Twitter:" - value={config.social.twitter} - link={`https://twitter.com/${config.social.twitter}`} - /> - )} - {typeof config.social.linkedin !== 'undefined' && - config.social.linkedin && ( - } - title="LinkedIn:" - value={config.social.linkedin} - link={`https://www.linkedin.com/in/${config.social.linkedin}`} - /> - )} - {typeof config.social.dribbble !== 'undefined' && - config.social.dribbble && ( - } - title="Dribbble:" - value={config.social.dribbble} - link={`https://dribbble.com/${config.social.dribbble}`} - /> - )} - {typeof config.social.behance !== 'undefined' && - config.social.behance && ( - } - title="Behance:" - value={config.social.behance} - link={`https://www.behance.net/${config.social.behance}`} - /> - )} - {typeof config.social.facebook !== 'undefined' && - config.social.facebook && ( - } - title="Facebook:" - value={config.social.facebook} - link={`https://www.facebook.com/${config.social.facebook}`} - /> - )} - {typeof config.social.medium !== 'undefined' && - config.social.medium && ( - } - title="Medium:" - value={config.social.medium} - link={`https://medium.com/@${config.social.medium}`} - /> - )} - {typeof config.social.devto !== 'undefined' && - config.social.devto && ( - } - title="Dev:" - value={config.social.devto} - link={`https://dev.to/${config.social.devto}`} - /> - )} - {typeof config.social.website !== 'undefined' && - config.social.website && ( - } - title="Website:" - value={config.social.website} - link={config.social.website} - /> - )} - {typeof config.social.phone !== 'undefined' && - config.social.phone && ( - } - title="Phone:" - value={config.social.phone} - link={`tel:${config.social.phone}`} - /> - )} - {typeof config.social.email !== 'undefined' && - config.social.email && ( - } - title="Email:" - value={config.social.email} - link={`mailto:${config.social.email}`} - /> - )} + {typeof social.twitter !== 'undefined' && social.twitter && ( + } + title="Twitter:" + value={social.twitter} + link={`https://twitter.com/${social.twitter}`} + /> + )} + {typeof social.linkedin !== 'undefined' && social.linkedin && ( + } + title="LinkedIn:" + value={social.linkedin} + link={`https://www.linkedin.com/in/${social.linkedin}`} + /> + )} + {typeof social.dribbble !== 'undefined' && social.dribbble && ( + } + title="Dribbble:" + value={social.dribbble} + link={`https://dribbble.com/${social.dribbble}`} + /> + )} + {typeof social.behance !== 'undefined' && social.behance && ( + } + title="Behance:" + value={social.behance} + link={`https://www.behance.net/${social.behance}`} + /> + )} + {typeof social.facebook !== 'undefined' && social.facebook && ( + } + title="Facebook:" + value={social.facebook} + link={`https://www.facebook.com/${social.facebook}`} + /> + )} + {typeof social.medium !== 'undefined' && social.medium && ( + } + title="Medium:" + value={social.medium} + link={`https://medium.com/@${social.medium}`} + /> + )} + {typeof social.devto !== 'undefined' && social.devto && ( + } + title="Dev:" + value={social.devto} + link={`https://dev.to/${social.devto}`} + /> + )} + {typeof social.website !== 'undefined' && social.website && ( + } + title="Website:" + value={social.website} + link={social.website} + /> + )} + {typeof social.phone !== 'undefined' && social.phone && ( + } + title="Phone:" + value={social.phone} + link={`tel:${social.phone}`} + /> + )} + {typeof social.email !== 'undefined' && social.email && ( + } + title="Email:" + value={social.email} + link={`mailto:${social.email}`} + /> + )} )}
@@ -189,7 +178,9 @@ const Details = ({ profile, loading }) => { Details.propTypes = { profile: PropTypes.object, - loading: PropTypes.bool, + loading: PropTypes.bool.isRequired, + social: PropTypes.object.isRequired, + github: PropTypes.object.isRequired, }; ListItem.propTypes = { diff --git a/src/components/education/index.jsx b/src/components/education/index.jsx index e817c4c..e959df7 100644 --- a/src/components/education/index.jsx +++ b/src/components/education/index.jsx @@ -1,7 +1,6 @@ import { skeleton } from '../../helpers/utils'; import { Fragment } from 'react'; import PropTypes from 'prop-types'; -import config from '../../../gitprofile.config'; const ListItem = ({ time, degree, institution }) => (
  • @@ -15,7 +14,7 @@ const ListItem = ({ time, degree, institution }) => (
  • ); -const Education = ({ loading }) => { +const Education = ({ loading, education }) => { const renderSkeleton = () => { let array = []; for (let index = 0; index < 2; index++) { @@ -41,46 +40,46 @@ const Education = ({ loading }) => { return ( <> - {typeof config.education !== 'undefined' && - config.education.length !== 0 && ( -
    -
    -
    -
    - {loading ? ( - skeleton({ width: 'w-32', height: 'h-8' }) - ) : ( - Education - )} -
    -
    -
    -
      - {loading ? ( - renderSkeleton() - ) : ( - - {config.education.map((item, index) => ( - - ))} - - )} -
    -
    + {typeof education !== 'undefined' && education.length !== 0 && ( +
    +
    +
    +
    + {loading ? ( + skeleton({ width: 'w-32', height: 'h-8' }) + ) : ( + Education + )} +
    +
    +
    +
      + {loading ? ( + renderSkeleton() + ) : ( + + {education.map((item, index) => ( + + ))} + + )} +
    - )} +
    + )} ); }; Education.propTypes = { - loading: PropTypes.bool, + loading: PropTypes.bool.isRequired, + education: PropTypes.array.isRequired, }; ListItem.propTypes = { diff --git a/src/components/experience/index.jsx b/src/components/experience/index.jsx index 9ab1b19..c3ad474 100644 --- a/src/components/experience/index.jsx +++ b/src/components/experience/index.jsx @@ -1,7 +1,6 @@ import { skeleton } from '../../helpers/utils'; import { Fragment } from 'react'; import PropTypes from 'prop-types'; -import config from '../../../gitprofile.config'; const ListItem = ({ time, position, company }) => (
  • @@ -15,7 +14,7 @@ const ListItem = ({ time, position, company }) => (
  • ); -const Experience = ({ loading }) => { +const Experience = ({ experiences, loading }) => { const renderSkeleton = () => { let array = []; for (let index = 0; index < 2; index++) { @@ -41,40 +40,39 @@ const Experience = ({ loading }) => { return ( <> - {typeof config.experiences !== 'undefined' && - config.experiences.length !== 0 && ( -
    -
    -
    -
    - {loading ? ( - skeleton({ width: 'w-32', height: 'h-8' }) - ) : ( - Experience - )} -
    -
    -
    -
      - {loading ? ( - renderSkeleton() - ) : ( - - {config.experiences.map((experience, index) => ( - - ))} - - )} -
    -
    + {typeof experiences !== 'undefined' && experiences.length !== 0 && ( +
    +
    +
    +
    + {loading ? ( + skeleton({ width: 'w-32', height: 'h-8' }) + ) : ( + Experience + )} +
    +
    +
    +
      + {loading ? ( + renderSkeleton() + ) : ( + + {experiences.map((experience, index) => ( + + ))} + + )} +
    - )} +
    + )} ); }; @@ -86,7 +84,8 @@ ListItem.propTypes = { }; Experience.propTypes = { - loading: PropTypes.bool, + experiences: PropTypes.array.isRequired, + loading: PropTypes.bool.isRequired, }; export default Experience; diff --git a/src/components/head-tag-editor/index.jsx b/src/components/head-tag-editor/index.jsx index fa3320a..bfab1ef 100644 --- a/src/components/head-tag-editor/index.jsx +++ b/src/components/head-tag-editor/index.jsx @@ -2,25 +2,24 @@ import { Fragment } from 'react'; import { Helmet } from 'react-helmet-async'; import PropTypes from 'prop-types'; import { isThemeDarkish } from '../../helpers/utils'; -import config from '../../../gitprofile.config'; -const HeadTagEditor = ({ profile, theme }) => { +const HeadTagEditor = ({ profile, theme, googleAnalytics, social }) => { return ( {profile && ( - {config.googleAnalytics?.id && ( + {googleAnalytics?.id && ( )} - {config.googleAnalytics?.id && ( + {googleAnalytics?.id && ( )} Portfolio of {profile.name} @@ -38,9 +37,7 @@ const HeadTagEditor = ({ profile, theme }) => { @@ -60,7 +57,9 @@ const HeadTagEditor = ({ profile, theme }) => { HeadTagEditor.propTypes = { profile: PropTypes.object, - theme: PropTypes.string, + theme: PropTypes.string.isRequired, + googleAnalytics: PropTypes.object.isRequired, + social: PropTypes.object.isRequired, }; export default HeadTagEditor; diff --git a/src/components/project/index.jsx b/src/components/project/index.jsx index 6c67c97..3f9397d 100644 --- a/src/components/project/index.jsx +++ b/src/components/project/index.jsx @@ -2,12 +2,11 @@ import { Fragment } from 'react'; import { AiOutlineStar, AiOutlineFork } from 'react-icons/ai'; import PropTypes from 'prop-types'; import { ga, languageColor, skeleton } from '../../helpers/utils'; -import config from '../../../gitprofile.config'; -const Project = ({ repo, loading }) => { +const Project = ({ repo, loading, github, googleAnalytics }) => { const renderSkeleton = () => { let array = []; - for (let index = 0; index < config.github.limit; index++) { + for (let index = 0; index < github.limit; index++) { array.push(
    @@ -61,7 +60,7 @@ const Project = ({ repo, loading }) => { e.preventDefault(); try { - if (config.googleAnalytics?.id) { + if (googleAnalytics?.id) { ga.event({ action: 'Click project', params: { @@ -145,7 +144,7 @@ const Project = ({ repo, loading }) => { skeleton({ width: 'w-10', height: 'h-5' }) ) : ( { Project.propTypes = { repo: PropTypes.array, - loading: PropTypes.bool, + loading: PropTypes.bool.isRequired, + github: PropTypes.object.isRequired, + googleAnalytics: PropTypes.object.isRequired, }; export default Project; diff --git a/src/components/skill/index.jsx b/src/components/skill/index.jsx index f51f83b..3ed90f9 100644 --- a/src/components/skill/index.jsx +++ b/src/components/skill/index.jsx @@ -1,8 +1,7 @@ import { skeleton } from '../../helpers/utils'; import PropTypes from 'prop-types'; -import config from '../../../gitprofile.config'; -const Skill = ({ loading }) => { +const Skill = ({ loading, skills }) => { const renderSkeleton = () => { let array = []; for (let index = 0; index < 12; index++) { @@ -18,7 +17,7 @@ const Skill = ({ loading }) => { return ( <> - {typeof config.skills !== 'undefined' && config.skills.length !== 0 && ( + {typeof skills !== 'undefined' && skills.length !== 0 && (
    @@ -34,7 +33,7 @@ const Skill = ({ loading }) => {
    {loading ? renderSkeleton() - : config.skills.map((skill, index) => ( + : skills.map((skill, index) => (
    { }; Skill.propTypes = { - loading: PropTypes.bool, + loading: PropTypes.bool.isRequired, + skills: PropTypes.array.isRequired, }; export default Skill; diff --git a/src/components/theme-changer/index.jsx b/src/components/theme-changer/index.jsx index 77f86a7..00ed5f1 100644 --- a/src/components/theme-changer/index.jsx +++ b/src/components/theme-changer/index.jsx @@ -1,9 +1,8 @@ import { AiOutlineControl } from 'react-icons/ai'; import { skeleton } from '../../helpers/utils'; 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) => { e.preventDefault(); document.querySelector('html').setAttribute('data-theme', selectedTheme); @@ -28,7 +27,7 @@ const ThemeChanger = ({ theme, setTheme, loading }) => { {loading ? skeleton({ width: 'w-16', height: 'h-5' }) - : theme === config.themeConfig.default + : theme === themeConfig.default ? 'Default' : theme} @@ -62,9 +61,9 @@ const ThemeChanger = ({ theme, setTheme, loading }) => { >
    @@ -203,11 +219,13 @@ const GitProfile = ({ config }) => { github={config.github} googleAnalytics={config.googleAnalytics} /> - + {typeof config.blog !== 'undefined' && ( + + )}
    @@ -244,12 +262,12 @@ GitProfile.propTypes = { config: PropTypes.shape({ github: PropTypes.shape({ username: PropTypes.string.isRequired, - sortBy: PropTypes.oneOf(['stars', 'updated']).isRequired, - limit: PropTypes.number.isRequired, + sortBy: PropTypes.oneOf(['stars', 'updated']), + limit: PropTypes.number, exclude: PropTypes.shape({ forks: PropTypes.bool.isRequired, projects: PropTypes.array.isRequired, - }).isRequired, + }), }).isRequired, social: PropTypes.shape({ linkedin: PropTypes.string, @@ -262,8 +280,8 @@ GitProfile.propTypes = { website: PropTypes.string, phone: PropTypes.string, email: PropTypes.string, - }).isRequired, - skills: PropTypes.array.isRequired, + }), + skills: PropTypes.array, experiences: PropTypes.arrayOf( PropTypes.shape({ company: PropTypes.string, @@ -271,7 +289,7 @@ GitProfile.propTypes = { from: PropTypes.string, to: PropTypes.string, }) - ).isRequired, + ), education: PropTypes.arrayOf( PropTypes.shape({ institution: PropTypes.string, @@ -279,26 +297,26 @@ GitProfile.propTypes = { 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, }; diff --git a/src/helpers/utils.jsx b/src/helpers/utils.jsx index 19cb17a..326a651 100644 --- a/src/helpers/utils.jsx +++ b/src/helpers/utils.jsx @@ -97,3 +97,86 @@ export const setupHotjar = (hotjarConfig) => { hotjar.initialize(hotjarConfig.id, snippetVersion); } }; + +export const constructConfigWithMissingValues = (config) => { + if (typeof config.github.sortBy === 'undefined') { + Object.assign(config.github, { sortBy: 'stars' }); + } + + if (typeof config.github.limit === 'undefined') { + Object.assign(config.github, { limit: 6 }); + } + + if (typeof config.github.exclude === 'undefined') { + Object.assign(config.github, { exclude: { forks: false, projects: [] } }); + } + + if (typeof config.themeConfig === 'undefined') { + const themeConfig = { + default: 'corporate', + disableSwitch: false, + respectPrefersColorScheme: false, + themes: [ + 'light', + 'dark', + 'cupcake', + 'bumblebee', + 'emerald', + 'corporate', + 'synthwave', + 'retro', + 'cyberpunk', + 'valentine', + 'halloween', + 'garden', + 'forest', + 'aqua', + 'lofi', + 'pastel', + 'fantasy', + 'wireframe', + 'black', + 'luxury', + 'dracula', + 'cmyk', + 'autumn', + 'business', + 'acid', + 'lemonade', + 'night', + 'coffee', + 'winter', + 'procyon', + ], + customTheme: { + procyon: { + primary: '#fc055b', + secondary: '#219aaf', + accent: '#e8d03a', + neutral: '#2A2730', + 'base-100': '#E3E3ED', + '--rounded-box': '3rem', + '--rounded-btn': '3rem', + }, + }, + }; + + Object.assign(config, { themeConfig: themeConfig }); + } + + if (typeof config.googleAnalytics === 'undefined') { + const googleAnalytics = { + id: '', + }; + + Object.assign(config, { googleAnalytics: googleAnalytics }); + } + + if (typeof config.social === 'undefined') { + const social = {}; + + Object.assign(config, { social: social }); + } + + return config; +}; From 59a9a2fac4a9b8ce43d86e645c1782395b60a63f Mon Sep 17 00:00:00 2001 From: Ariful Alam Date: Sat, 26 Mar 2022 18:09:04 +0600 Subject: [PATCH 15/33] Add type definitions for social link --- types/index.d.ts | 71 +++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 64 insertions(+), 7 deletions(-) diff --git a/types/index.d.ts b/types/index.d.ts index 5af211b..cbdd4ac 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -4,7 +4,7 @@ import { Component } from 'react'; -interface github { +interface Github { /** * GitHub org/user name */ @@ -13,17 +13,17 @@ interface github { /** * stars | updated */ - sortBy: string; + sortBy?: string; /** * How many projects to display */ - limit: number; + limit?: number; /** * Exclude projects option */ - exclude: { + exclude?: { /** * Forked projects will not be displayed if set to true */ @@ -38,18 +38,75 @@ interface github { }; } -interface config { +interface Social { + /** + * LinkedIn + */ + linkedin?: string; + + /** + * Twitter + */ + twitter?: string; + + /** + * Facebook + */ + facebook?: string; + + /** + * Dribbble + */ + dribbble?: string; + + /** + * Behance + */ + behance?: string; + + /** + * Medium + */ + medium?: string; + + /** + * dev.to + */ + devto?: string; + + /** + * Website + */ + website?: string; + + /** + * Phone + */ + phone?: string; + + /** + * Email + */ + email?: string; +} + +interface Config { /** * GitHub Config */ - github: github; + github: Github; + + /** + * Social links + */ + social?: Social; } interface GitProfileProps { /** * Config values */ - config: config; + config: Config; } declare class GitProfile extends Component {} From 4cb107e168e96efbc5a6a7c30819ad6e20f2ddf6 Mon Sep 17 00:00:00 2001 From: Ariful Alam Date: Sat, 26 Mar 2022 18:23:09 +0600 Subject: [PATCH 16/33] Rename themeConfig.default to themeConfig.defaultTheme --- README.md | 6 +- src/components/GitProfile.jsx | 2 +- src/components/theme-changer/index.jsx | 8 +-- src/helpers/utils.jsx | 8 +-- types/index.d.ts | 93 ++++++++++++++++++++++++-- 5 files changed, 101 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 194f456..5f9c6af 100644 --- a/README.md +++ b/README.md @@ -147,14 +147,14 @@ const config = { snippetVersion: 6, }, themeConfig: { - default: 'light', + defaultTheme: 'light', // Hides the theme change switch // Useful if you want to support a single color mode disableSwitch: false, // Should we use the prefers-color-scheme media-query, - // using user system preferences, instead of the hardcoded default + // using user system preferences, instead of the hardcoded defaultTheme respectPrefersColorScheme: true, // Available themes. To remove any theme, exclude from here. @@ -220,7 +220,7 @@ The default theme can be specified. module.exports = { // ... themeConfig: { - default: 'light', + defaultTheme: 'light', // ... }, }; diff --git a/src/components/GitProfile.jsx b/src/components/GitProfile.jsx index 922bbe4..b02ad6d 100644 --- a/src/components/GitProfile.jsx +++ b/src/components/GitProfile.jsx @@ -311,7 +311,7 @@ GitProfile.propTypes = { snippetVersion: PropTypes.number, }), themeConfig: PropTypes.shape({ - default: PropTypes.string.isRequired, + defaultTheme: PropTypes.string.isRequired, disableSwitch: PropTypes.bool.isRequired, respectPrefersColorScheme: PropTypes.bool.isRequired, themes: PropTypes.array.isRequired, diff --git a/src/components/theme-changer/index.jsx b/src/components/theme-changer/index.jsx index 00ed5f1..0e5c92d 100644 --- a/src/components/theme-changer/index.jsx +++ b/src/components/theme-changer/index.jsx @@ -27,7 +27,7 @@ const ThemeChanger = ({ theme, setTheme, loading, themeConfig }) => { {loading ? skeleton({ width: 'w-16', height: 'h-5' }) - : theme === themeConfig.default + : theme === themeConfig.defaultTheme ? 'Default' : theme} @@ -61,9 +61,9 @@ const ThemeChanger = ({ theme, setTheme, loading, themeConfig }) => { >