35 Commits

Author SHA1 Message Date
MD. Ariful Alam
fadc5f3f7e Bump version 2021-10-14 23:17:16 +06:00
MD. Ariful Alam
edd365ef01 Merge branch 'main' of https://github.com/arifszn/ezprofile into main 2021-10-14 23:10:50 +06:00
MD. Ariful Alam
16aadfa664 Change status code for too many requests 2021-10-14 23:08:35 +06:00
MD. Ariful Alam
1cadcc1f59 Update README.md 2021-10-14 23:07:27 +06:00
MD. Ariful Alam
03c566615a Update dependencies 2021-10-14 22:58:29 +06:00
MD. Ariful Alam
7a4d8aefdf Update config.js 2021-10-09 11:44:20 +06:00
MD. Ariful Alam
d9de322944 Update config.js 2021-10-02 00:11:42 +06:00
MD. Ariful Alam
bc5bec50f4 Update README.md 2021-09-25 13:58:49 +06:00
MD. Ariful Alam
e6006e387a Refactor hotjar setup 2021-09-23 22:34:44 +06:00
MD. Ariful Alam
e4005d288d Merge pull request #9 from anton-gustafsson/add-hotjar
Add hotjar support
2021-09-23 22:21:54 +06:00
Anton Jacobsson
0424045f55 feat(all): add hotjar 2021-09-22 22:10:14 +02:00
MD. Ariful Alam
a71c4ca330 Publish 1.0.1 2021-09-19 18:07:02 +06:00
MD. Ariful Alam
ebf5af2451 Update CI-CD.yml 2021-09-19 17:55:10 +06:00
MD. Ariful Alam
6477cceb86 Fix icon placement for large text 2021-09-19 17:47:21 +06:00
MD. Ariful Alam
8f167cc940 Migrate from redux to context 2021-09-16 00:30:22 +06:00
MD. Ariful Alam
d5c715e16e Add section title and change padding 2021-09-15 18:47:32 +06:00
MD. Ariful Alam
343ad767f0 Display default theme at top 2021-09-15 18:36:37 +06:00
MD. Ariful Alam
fd1a96c160 Merge pull request #6 from arifszn/Warhammer4000-CI-Patch
Updated CI Config
2021-09-13 11:54:50 +06:00
Tanimul Haque Khan
de964e620b Updated CI Config
-Updated Node setup to v2
-Removed manual caching with setup actions. It's managed by a separate action
2021-09-13 11:48:56 +06:00
MD. Ariful Alam
bb7c671e3b Update README.md 2021-09-12 21:46:05 +06:00
MD. Ariful Alam
67246fd888 Update README.md 2021-09-12 21:39:41 +06:00
MD. Ariful Alam
9b7a270f92 Update README.md 2021-09-11 14:09:14 +06:00
MD. Ariful Alam
31181711df Merge pull request #4 from arifszn/arifszn-patch-1
Update deployment guide
2021-09-11 14:07:33 +06:00
MD. Ariful Alam
c4241c65b9 Remove reponame 2021-09-11 14:05:56 +06:00
MD. Ariful Alam
a69bee5cd0 Update deployment guide 2021-09-11 12:51:05 +06:00
MD. Ariful Alam
d7696f070e Merge pull request #3 from Warhammer4000/patch-1
Updated Readme on how to use continuous deployment
2021-09-11 12:39:13 +06:00
Tanimul Haque Khan
6041dc51aa Updated Readme on how to use continuous deployment
- Please check if the formatting is acceptable 
- Please check if the removal of npm deploy is acceptable. (It's better to remove technical stuff in a package like this to make it more user friendly)

Ideally what the readme should do is say, Fork the repo. And make a release with your updated config.js and done.
2021-09-08 12:18:40 +06:00
MD. Ariful Alam
b3c06be528 Display phone number in details 2021-09-08 01:48:09 +06:00
MD. Ariful Alam
d36d0f74e1 Add padding in bio 2021-09-08 01:24:28 +06:00
MD. Ariful Alam
5b337e1ddb Update README.md 2021-09-07 23:54:16 +06:00
MD. Ariful Alam
1a054c39b6 Update License 2021-09-07 23:53:07 +06:00
MD. Ariful Alam
287e750548 Merge branch 'main' of https://github.com/arifszn/ezprofile into main 2021-09-07 21:04:40 +06:00
MD. Ariful Alam
07cd0ad4ed Remove anchor link 2021-09-07 21:04:35 +06:00
MD. Ariful Alam
2d1cf94612 Merge pull request #1 from Studio-23-xyz/main
Create CI-CD.yml
2021-09-07 20:26:27 +06:00
Tanimul Haque Khan
71d2c30130 Create CI-CD.yml 2021-09-07 13:58:48 +06:00
25 changed files with 2085 additions and 3014 deletions

39
.github/workflows/CI-CD.yml vendored Normal file
View File

@@ -0,0 +1,39 @@
name: Build and Publish to gh-pages Branch
on:
push:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Set up Node
uses: actions/setup-node@v2
with:
node-version: '14'
- name: Cache dependencies
uses: actions/cache@v2
with:
path: |
**/node_modules
key: ${{ runner.os }}-${{ hashFiles('**/package-lock.json') }}
- name: Install dependencies
run: npm install
- name: Build
run: npm run build
env:
CI: ""
- name: Deploy
uses: JamesIves/github-pages-deploy-action@4.1.4
with:
branch: gh-pages
folder: build

View File

@@ -186,7 +186,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Copyright 2021 MD. Ariful Alam
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View File

@@ -18,12 +18,13 @@
<a href="#arifszn"><img src="https://arifszn.github.io/assets/img/drop-shadow.png" width="60%" alt="Shadow"/></a>
</p>
**ezProfile** is an easy-to-customize personal dev portfolio builder that is created with React.js. When you manage the code in a GitHub repository, it will automatically render a webpage with the owner's profile information, including a photo, bio, and repositories. Also, it includes space to highlight your details, job history, education history, skills, and recent blog posts.
**ezProfile** is an easy-to-customize personal dev portfolio builder that is created with React.js. When you manage the code in a GitHub repository, it will automatically render a webpage with the owner's profile information, including a photo, bio, and public repositories. Also, it includes space to highlight your details, job history, education history, skills, and recent blog posts.
It's all possible using [GitHub API](https://developer.github.com/v3/) (for automatically populating your website with content) and [Article-api](https://github.com/arifszn/article-api) (for fetching recent blog posts).
✓ [21 Themes](#themes)\
✓ [Google Analytics](#google-analytics)\
✓ [Hotjar](#hotjar)\
✓ [Meta Tags](#meta-tags)\
✓ [Avatar and Bio](#avatar-and-bio)\
✓ [Social Links](#social-links)\
@@ -36,31 +37,40 @@ It's all possible using [GitHub API](https://developer.github.com/v3/) (for auto
To view a live example, **[click here](https://arifszn.github.io/ezprofile)**.
## 🛠 Installation & Set Up
These instructions will get you a copy of the project up and running on your local machine.
These instructions will get you a copy of the project and deploy your website online!
You'll need [Git](https://git-scm.com) and [Node.js](https://nodejs.org/en/download/) (which comes with [npm](http://npmjs.com)) installed on your computer.
- **[Fork](https://docs.github.com/en/get-started/quickstart/fork-a-repo)** the repo so you have your own project to customize by clicking the fork icon on the top right side. A "fork" is a copy of a repository.
- Rename your forked repository to <code>username.github.io</code> in github, where <code>username</code> is your GitHub username (or organization name).
- Go to your repo's **Actions** page and enable workflows.\
![Workflows](https://arifszn.github.io/assets/img/hosted/ezprofile/workflows.png)
- Open <code>package.json</code>, and change <code>homepage</code>'s value to <code>https://username.github.io</code>.
1. **[Fork](https://docs.github.com/en/get-started/quickstart/fork-a-repo)** the repo so you have your own project to customize. A "fork" is a copy of a repository.
2. Once you've found a home for your forked repository, **[clone](https://docs.github.com/en/github/creating-cloning-and-archiving-repositories/cloning-a-repository-from-github/cloning-a-repository)** it.
3. Change into your new directory.
```sh
cd ezprofile
```js
// package.json
{
// ...
"homepage": "https://username.github.io",
}
```
4. Install dependencies
```sh
npm install
```
- Now commit to your **main** branch with your changes.
- The CI/CD pipeline will publish your page at the gh-pages branch automatically.
- Go to your repo's **Settings** -> **Pages** -> **Source** and change the branch to gh-pages and click **save**.
- Your personal portfolio will be live at <code>username.github.io</code>.
- Any time you commit a change to the **main** branch the website will automatically update.
You can skip the above steps and do a manual deployment by running <code>npm run deploy</code>. For more info, visit [here](https://create-react-app.dev/docs/deployment/#github-pages).
As this is a create react app, you can also host your website to Netlify, Vercel, Heroku, or other popular services. Please refer to this [doc](https://create-react-app.dev/docs/deployment) for a detailed deployment guide to other services.
If you see only <code>README</code> at <code>username.github.io</code>, be sure to change your GitHub Page's source to <code>gh-pages</code> branch. See [how to](https://docs.github.com/en/pages/getting-started-with-github-pages/configuring-a-publishing-source-for-your-github-pages-site).
5. Start the development server
```sh
npm start
```
## 🎨 Customization
@@ -92,6 +102,7 @@ module.exports = {
medium: '',
devto: '',
website: '',
phone: '',
email: ''
},
skills: [
@@ -136,6 +147,10 @@ module.exports = {
// GA3 tracking id/GA4 tag id
id: '' // UA-XXXXXXXXX-X | G-XXXXXXXXXX
},
hotjar: {
id: '',
snippetVersion : 6
},
themeConfig: {
default: 'light',
@@ -226,8 +241,22 @@ module.exports = {
Besides tracking visitors, ezFolio will track click events on projects and blog posts, and send them to Google Analytics.\
<br/>
![Google Analytics](https://www.arifszn.com/assets/img/hosted/ezprofile/event.png)
![Event](https://www.arifszn.com/assets/img/hosted/ezprofile/event.png)
### Hotjar
ezProfile supports hotjar. If you do not want to use Hotjar, keep the <code>id</code> empty.
```js
// config.js
module.exports = {
// ...
hotjar: {
id: '',
snippetVersion : 6
},
}
```
### Meta Tags
@@ -243,7 +272,7 @@ Your github avatar and bio will be displayed here.\
### Social Links
ezProfile supports linking your social media services you're using, including LinkedIn, Twitter, Facebook, Dribbble, Behance, Medium, dev.to, personal website, and email.
ezProfile supports linking your social media services you're using, including LinkedIn, Twitter, Facebook, Dribbble, Behance, Medium, dev.to, personal website, phone and email.
```js
// config.js
module.exports = {
@@ -257,6 +286,7 @@ module.exports = {
medium: '',
devto: '',
website: 'https://arifszn.github.io',
phone: '',
email: ''
},
}
@@ -375,35 +405,10 @@ module.exports = {
The posts are fetched by [Article-api](https://github.com/arifszn/article-api).
## 🚀 Deploy
Once you are done with your setup and have completed all steps above, you need to put your website online! The fastest approach is to use [GitHub Pages](https://pages.github.com) which is completely free.
**1. Github Pages:**
- Rename your forked repository to <code>username.github.io</code> in github, where <code>username</code> is your GitHub username (or organization name).
- Open <code>package.json</code>, and change <code>homepage</code>'s value to <code>https://username.github.io</code>.
```js
// package.json
{
// ...
"homepage": "https://username.github.io",
}
```
- Run <code>npm run deploy</code>.
- If you see only <code>README</code> at <code>username.github.io</code>, be sure to change your GitHub Page's source to <code>gh-pages</code> branch. See [how to](https://docs.github.com/en/pages/getting-started-with-github-pages/configuring-a-publishing-source-for-your-github-pages-site).
Your personal portfolio will be live at <code>username.github.io</code>. For more info, visit [here](https://create-react-app.dev/docs/deployment/#github-pages).
<br/>
**2. Other:** You can also host your website to Netlify, Vercel, Heroku, or other popular services. Please refer to this [doc](https://create-react-app.dev/docs/deployment) for a detailed deployment guide.
## 📢 Please Read
I intend to keep my works open source. Please do not discourage me by claiming this work by copying it as your own or removing the footer notice.
I intend to keep my works open source. Please do not discourage me by claiming this work by copying it as your own or removing/changing the footer notice. However You are open to use this project by forking it and change any code necessary by giving attribute to the original author.
## 💖 Support

4289
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "ezprofile",
"version": "1.0.0",
"version": "1.1.0",
"description": "Kickstart your personal portfolio with Github Api and blog",
"homepage": "https://arifszn.github.io/ezprofile",
"private": true,
@@ -12,12 +12,11 @@
},
"dependencies": {
"@craco/craco": "^6.2.0",
"@reduxjs/toolkit": "^1.6.1",
"@testing-library/jest-dom": "^5.11.4",
"@testing-library/react": "^11.1.0",
"@testing-library/user-event": "^12.1.10",
"article-api": "^1.0.5",
"axios": "^0.21.1",
"axios": "^0.23.0",
"daisyui": "^1.12.1",
"gh-pages": "^3.2.3",
"moment": "^2.29.1",
@@ -25,8 +24,8 @@
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-helmet-async": "^1.1.0",
"react-hotjar": "^3.0.1",
"react-icons": "^4.2.0",
"react-redux": "^7.2.4",
"react-scripts": "4.0.3",
"sass": "^1.38.0",
"web-vitals": "^1.0.1"

View File

@@ -1,26 +1,25 @@
import axios from "axios";
import { Fragment, useCallback, useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { Fragment, useCallback, useContext, useEffect, useState } from "react";
import AvatarCard from "./components/AvatarCard";
import ErrorPage from "./components/ErrorPage";
import ThemeChanger from "./components/ThemeChanger";
import config from "./config";
import moment from 'moment';
import { setLoading } from "./store/slices/loadingSlice";
import { setProfile } from "./store/slices/profileSlice";
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 { setRepo } from "./store/slices/repoSlice";
import Blog from "./components/Blog";
import MetaTags from "./components/MetaTags";
import { LoadingContext } from "./contexts/LoadingContext";
import { ThemeContext } from "./contexts/ThemeContext";
function App() {
const dispatch = useDispatch();
const theme = useSelector(state => state.theme);
const [theme] = useContext(ThemeContext);
const [, setLoading] = useContext(LoadingContext);
const [profile, setProfile] = useState(null);
const [repo, setRepo] = useState(null);
const [error, setError] = useState(null);
const [rateLimit, setRateLimit] = useState(null);
@@ -43,7 +42,7 @@ function App() {
company: data.company ? data.company : ''
}
dispatch(setProfile(profileData));
setProfile(profileData);
})
.then(() => {
let excludeRepo = ``;
@@ -64,7 +63,7 @@ function App() {
.then(response => {
let data = response.data;
dispatch(setRepo(data.items));
setRepo(data.items);
})
.catch((error) => {
handleError(error);
@@ -74,9 +73,9 @@ function App() {
handleError(error);
})
.finally(() => {
dispatch(setLoading(false));
setLoading(false);
});
}, [dispatch])
}, [setLoading])
useEffect(() => {
loadData();
@@ -91,7 +90,7 @@ function App() {
});
if (error.response.status === 403) {
setError(403);
setError(429);
} else if (error.response.status === 404) {
setError(404);
} else {
@@ -104,7 +103,7 @@ function App() {
return (
<Fragment>
<MetaTags/>
<MetaTags profile={profile}/>
<div className="fade-in h-screen">
{
@@ -112,7 +111,7 @@ function App() {
<ErrorPage
status={`${error}`}
title={(error === 404) ? 'The Github Username is Incorrect' : (
error === 403 ? 'Too Many Request.' : `Ops!!`
error === 429 ? 'Too Many Requests.' : `Ops!!`
)}
subTitle={
(error === 404) ? (
@@ -120,7 +119,7 @@ function App() {
Please provide correct github username in <code>src\config.js</code>
</p>
) : (
error === 403 ? (
error === 429 ? (
<p>
Oh no, you hit the{' '}
<a
@@ -147,8 +146,8 @@ function App() {
<ThemeChanger/>
)
}
<AvatarCard />
<Details />
<AvatarCard profile={profile}/>
<Details profile={profile}/>
<Skill/>
<Experience/>
<Education/>
@@ -156,7 +155,7 @@ function App() {
</div>
<div className="lg:col-span-2 col-span-1">
<div className="grid grid-cols-1 gap-6">
<Project />
<Project repo={repo}/>
<Blog/>
</div>
</div>

View File

@@ -1,16 +1,17 @@
import { useSelector } from "react-redux";
import { fallbackImage, skeleton } from "../helpers/utils";
import LazyImage from "./LazyImage";
import PropTypes from 'prop-types';
import { useContext } from "react";
import { LoadingContext } from "../contexts/LoadingContext";
const AvatarCard = () => {
const profile = useSelector(state => state.profile);
const loading = useSelector(state => state.loading);
const AvatarCard = (props) => {
const [loading] = useContext(LoadingContext);
return (
<div className="card shadow-lg compact bg-base-100">
<div className="grid place-items-center py-8">
{
loading ? (
(loading || !props.profile) ? (
<div className="avatar opacity-90">
<div className="mb-8 rounded-full w-32 h-32">
{
@@ -27,8 +28,8 @@ const AvatarCard = () => {
<div className="mb-8 rounded-full w-32 h-32 ring ring-primary ring-offset-base-100 ring-offset-2">
{
<LazyImage
src={profile.avatar ? profile.avatar : fallbackImage}
alt={profile.name}
src={props.profile.avatar ? props.profile.avatar : fallbackImage}
alt={props.profile.name}
placeholder={
skeleton({
width: 'w-full',
@@ -42,19 +43,19 @@ const AvatarCard = () => {
</div>
)
}
<div className="text-center mx-auto md:mx-8">
<div className="text-center mx-auto px-8">
<h5 className="font-bold text-2xl">
{
loading ? (
(loading || !props.profile) ? (
skeleton({ width: 'w-48', height: 'h-8' })
) : <span className="opacity-70">{profile.name}</span>
) : <span className="opacity-70">{props.profile.name}</span>
}
</h5>
<div className="mt-3 text-base-content text-opacity-60">
{
loading ? (
(loading || !props.profile) ? (
skeleton({ width: 'w-48', height: 'h-5' })
) : profile.bio
) : props.profile.bio
}
</div>
</div>
@@ -63,4 +64,8 @@ const AvatarCard = () => {
)
}
AvatarCard.propTypes = {
profile: PropTypes.object
}
export default AvatarCard;

View File

@@ -1,9 +1,9 @@
import { getDevtoArticle, getMediumArticle } from "article-api";
import moment from "moment";
import { Fragment, useEffect, useState } from "react";
import { Fragment, useContext, useEffect, useState } from "react";
import { CgHashtag } from 'react-icons/cg';
import { useSelector } from "react-redux";
import config from "../config";
import { LoadingContext } from "../contexts/LoadingContext";
import { ga, skeleton } from "../helpers/utils";
import LazyImage from "./LazyImage";
@@ -23,7 +23,7 @@ const displaySection = () => {
const Blog = () => {
const [articles, setArticles] = useState(null);
const loading = useSelector(state => state.loading);
const [loading] = useContext(LoadingContext);
useEffect(() => {
if (displaySection()) {
@@ -141,7 +141,7 @@ const Blog = () => {
{
article.categories.map((category, index2) => (
<div key={index2} className="flex text-sm mr-3 items-center opacity-50 font-bold font-mono">
<CgHashtag />
<span><CgHashtag /></span>
<span>{category}</span>
</div>
))

View File

@@ -3,14 +3,16 @@ import { AiFillGithub, AiFillMediumSquare } from 'react-icons/ai';
import { SiTwitter } from 'react-icons/si';
import { GrLinkedinOption } from 'react-icons/gr';
import { CgDribbble } from 'react-icons/cg';
import { RiPhoneFill } from 'react-icons/ri';
import { FaBehanceSquare, FaBuilding, FaDev, FaFacebook, FaGlobe } from 'react-icons/fa';
import { useSelector } from 'react-redux';
import config from '../config';
import { skeleton } from '../helpers/utils';
import PropTypes from 'prop-types';
import { useContext } from 'react';
import { LoadingContext } from '../contexts/LoadingContext';
const Details = () => {
const profile = useSelector(state => state.profile);
const loading = useSelector(state => state.loading);
const Details = (props) => {
const [loading] = useContext(LoadingContext);
const renderSkeleton = () => {
let array = [];
@@ -33,31 +35,42 @@ const Details = () => {
<div className="card-body">
<ul className="menu row-span-3 bg-base-100 text-base-content text-opacity-60">
{
loading ? renderSkeleton() : (
(loading || !props.profile) ? renderSkeleton() : (
<>
{
profile && profile.location && (
props.profile.location && (
<li>
<span>
<div>
<MdLocationOn className="mr-2"/>
{profile.location}
</div>
<div>
{props.profile.location}
</div>
</span>
</li>
)
}
{
profile && profile.company && (
props.profile.company && (
<li>
<span>
<div>
<FaBuilding className="mr-2"/>
{profile.company}
</div>
<div>
{props.profile.company}
</div>
</span>
</li>
)
}
<li>
<span>
<div>
<AiFillGithub className="mr-2"/>
</div>
<div>
<a
href={`https://github.com/${config.github.username}`}
target="_blank"
@@ -66,13 +79,17 @@ const Details = () => {
>
{config.github.username}
</a>
</div>
</span>
</li>
{
typeof config.social.linkedin !== 'undefined' && config.social.linkedin && (
<li>
<span>
<div>
<GrLinkedinOption className="mr-2"/>
</div>
<div>
<a
href={`https://www.linkedin.com/in/${config.social.linkedin}`}
target="_blank"
@@ -81,6 +98,7 @@ const Details = () => {
>
{config.social.linkedin}
</a>
</div>
</span>
</li>
)
@@ -89,7 +107,10 @@ const Details = () => {
typeof config.social.twitter !== 'undefined' && config.social.twitter && (
<li>
<span>
<div>
<SiTwitter className="mr-2"/>
</div>
<div>
<a
href={`https://twitter.com/${config.social.twitter}`}
target="_blank"
@@ -98,6 +119,7 @@ const Details = () => {
>
{config.social.twitter}
</a>
</div>
</span>
</li>
)
@@ -106,7 +128,10 @@ const Details = () => {
typeof config.social.dribbble !== 'undefined' && config.social.dribbble && (
<li>
<span>
<div>
<CgDribbble className="mr-2"/>
</div>
<div>
<a
href={`https://dribbble.com/${config.social.dribbble}`}
target="_blank"
@@ -115,6 +140,7 @@ const Details = () => {
>
{config.social.dribbble}
</a>
</div>
</span>
</li>
)
@@ -123,7 +149,10 @@ const Details = () => {
typeof config.social.behance !== 'undefined' && config.social.behance && (
<li>
<span>
<div>
<FaBehanceSquare className="mr-2"/>
</div>
<div>
<a
href={`https://www.behance.net/${config.social.behance}`}
target="_blank"
@@ -132,6 +161,7 @@ const Details = () => {
>
{config.social.behance}
</a>
</div>
</span>
</li>
)
@@ -140,7 +170,10 @@ const Details = () => {
typeof config.social.facebook !== 'undefined' && config.social.facebook && (
<li>
<span>
<div>
<FaFacebook className="mr-2"/>
</div>
<div>
<a
href={`https://www.facebook.com/${config.social.facebook}`}
target="_blank"
@@ -149,6 +182,7 @@ const Details = () => {
>
{config.social.facebook}
</a>
</div>
</span>
</li>
)
@@ -157,7 +191,10 @@ const Details = () => {
typeof config.social.medium !== 'undefined' && config.social.medium && (
<li>
<span>
<div>
<AiFillMediumSquare className="mr-2"/>
</div>
<div>
<a
href={`https://medium.com/@${config.social.medium}`}
target="_blank"
@@ -166,6 +203,7 @@ const Details = () => {
>
{config.social.medium}
</a>
</div>
</span>
</li>
)
@@ -174,7 +212,10 @@ const Details = () => {
typeof config.social.devto !== 'undefined' && config.social.devto && (
<li>
<span>
<div>
<FaDev className="mr-2"/>
</div>
<div>
<a
href={`https://dev.to/${config.social.devto}`}
target="_blank"
@@ -183,6 +224,7 @@ const Details = () => {
>
{config.social.devto}
</a>
</div>
</span>
</li>
)
@@ -191,7 +233,10 @@ const Details = () => {
typeof config.social.website !== 'undefined' && config.social.website && (
<li>
<span>
<div>
<FaGlobe className="mr-2"/>
</div>
<div>
<a
href={`${config.social.website}`}
target="_blank"
@@ -200,6 +245,27 @@ const Details = () => {
>
{config.social.website}
</a>
</div>
</span>
</li>
)
}
{
typeof config.social.phone !== 'undefined' && config.social.phone && (
<li>
<span>
<div>
<RiPhoneFill className="mr-2"/>
</div>
<div>
<a
href={`tel:${config.social.phone}`}
rel="noreferrer"
className="text-base-content-important"
>
{config.social.phone}
</a>
</div>
</span>
</li>
)
@@ -208,7 +274,10 @@ const Details = () => {
typeof config.social.email !== 'undefined' && config.social.email && (
<li>
<span>
<div>
<MdMail className="mr-2"/>
</div>
<div>
<a
href={`mailto:${config.social.email}`}
target="_blank"
@@ -217,6 +286,7 @@ const Details = () => {
>
{config.social.email}
</a>
</div>
</span>
</li>
)
@@ -230,4 +300,8 @@ const Details = () => {
)
}
Details.propTypes = {
profile: PropTypes.object
}
export default Details;

View File

@@ -1,10 +1,11 @@
import { useSelector } from "react-redux";
import config from "../config";
import { GoPrimitiveDot } from 'react-icons/go';
import { skeleton } from "../helpers/utils";
import { useContext } from "react";
import { LoadingContext } from "../contexts/LoadingContext";
const Education = () => {
const loading = useSelector(state => state.loading);
const [loading] = useContext(LoadingContext);
const renderSkeleton = () => {
let array = [];
@@ -42,7 +43,7 @@ const Education = () => {
<div className="card-body">
<ul className="menu row-span-3 bg-base-100 text-base-content">
<li>
<div className="pb-0-important mx-5">
<div className="pb-0-important mx-3">
<h5 className="card-title">
{
loading ? skeleton({width: 'w-32', height: 'h-8'}) : (
@@ -57,7 +58,9 @@ const Education = () => {
config.education.map((item, index) => (
<li key={index}>
<span>
<div>
<GoPrimitiveDot className="mr-2 opacity-40"/>
</div>
<div>
<div className="block justify-between">
<div className="font-medium opacity-70">

View File

@@ -1,10 +1,11 @@
import { useSelector } from "react-redux";
import config from "../config";
import { GoPrimitiveDot } from 'react-icons/go';
import { skeleton } from "../helpers/utils";
import { useContext } from "react";
import { LoadingContext } from "../contexts/LoadingContext";
const Experience = () => {
const loading = useSelector(state => state.loading);
const [loading] = useContext(LoadingContext);
const renderSkeleton = () => {
let array = [];
@@ -42,11 +43,13 @@ const Experience = () => {
<div className="card-body">
<ul className="menu row-span-3 bg-base-100 text-base-content">
<li>
<div className="pb-0-important mx-5">
<div className="pb-0-important mx-3">
<h5 className="card-title">
{loading ? skeleton({width: 'w-32', height: 'h-8'}) : (
{
loading ? skeleton({width: 'w-32', height: 'h-8'}) : (
<span className="opacity-70">Experience</span>
)}
)
}
</h5>
</div>
</li>
@@ -55,7 +58,9 @@ const Experience = () => {
config.experiences.map((experience, index) => (
<li key={index}>
<span>
<div>
<GoPrimitiveDot className="mr-2 opacity-40"/>
</div>
<div>
<div className="block justify-between">
<div className="font-medium opacity-70">

View File

@@ -1,17 +1,17 @@
import React, { Fragment } from 'react';
import React, { Fragment, useContext } from 'react';
import { Helmet } from "react-helmet-async";
import { useSelector } from 'react-redux';
import config from '../config';
import { isThemeDarkish } from '../helpers/utils';
import PropTypes from 'prop-types';
import { ThemeContext } from '../contexts/ThemeContext';
const MetaTags = () => {
const profile = useSelector(state => state.profile);
const theme = useSelector(state => state.theme);
const MetaTags = (props) => {
const [theme] = useContext(ThemeContext);
return (
<Fragment>
{
profile && (
props.profile && (
<Helmet>
{
config.googleAnalytics.id && (
@@ -33,25 +33,25 @@ const MetaTags = () => {
</script>
)
}
<title>Portfolio of {profile.name}</title>
<title>Portfolio of {props.profile.name}</title>
<meta name="theme-color" content={isThemeDarkish(theme) ? '#000000' : '#ffffff'}/>
<meta name="description" content={profile.bio} />
<meta name="description" content={props.profile.bio} />
<meta itemprop="name" content={`Portfolio of ${profile.name}`} />
<meta itemprop="description" content={profile.bio} />
<meta itemprop="image" content={profile.avatar} />
<meta itemprop="name" content={`Portfolio of ${props.profile.name}`} />
<meta itemprop="description" content={props.profile.bio} />
<meta itemprop="image" content={props.profile.avatar} />
<meta property="og:url" content={typeof config.social.website !== 'undefined' ? config.social.website : ''} />
<meta property="og:type" content="website" />
<meta property="og:title" content={`Portfolio of ${profile.name}`} />
<meta property="og:description" content={profile.bio} />
<meta property="og:image" content={profile.avatar} />
<meta property="og:title" content={`Portfolio of ${props.profile.name}`} />
<meta property="og:description" content={props.profile.bio} />
<meta property="og:image" content={props.profile.avatar} />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content={`Portfolio of ${profile.name}`} />
<meta name="twitter:description" content={profile.bio} />
<meta name="twitter:image" content={profile.avatar} />
<meta name="twitter:title" content={`Portfolio of ${props.profile.name}`} />
<meta name="twitter:description" content={props.profile.bio} />
<meta name="twitter:image" content={props.profile.avatar} />
</Helmet>
)
}
@@ -59,4 +59,8 @@ const MetaTags = () => {
)
}
MetaTags.propTypes = {
profile: PropTypes.object
}
export default MetaTags;

View File

@@ -1,12 +1,12 @@
import { Fragment } from "react";
import { useSelector } from "react-redux";
import { Fragment, useContext } from "react";
import { ga, languageColor, skeleton } from "../helpers/utils";
import { AiOutlineStar, AiOutlineFork } from 'react-icons/ai';
import config from "../config";
import PropTypes from 'prop-types';
import { LoadingContext } from "../contexts/LoadingContext";
const Project = () => {
const loading = useSelector(state => state.loading);
const repo = useSelector(state => state.repo);
const Project = (props) => {
const [loading] = useContext(LoadingContext);
const renderSkeleton = () => {
let array = [];
@@ -51,7 +51,7 @@ const Project = () => {
}
const renderProjects = () => {
return repo.map((item, index) => (
return props.repo.map((item, index) => (
<div
className="card shadow-lg compact bg-base-100 cursor-pointer"
key={index}
@@ -146,7 +146,7 @@ const Project = () => {
</div>
<div className="col-span-2">
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
{(loading || !repo) ? renderSkeleton() : renderProjects()}
{(loading || !props.repo) ? renderSkeleton() : renderProjects()}
</div>
</div>
</div>
@@ -155,4 +155,8 @@ const Project = () => {
)
}
Project.propTypes = {
repo: PropTypes.array
}
export default Project;

View File

@@ -1,9 +1,10 @@
import { useSelector } from "react-redux";
import { useContext } from "react";
import config from "../config";
import { LoadingContext } from "../contexts/LoadingContext";
import { skeleton } from "../helpers/utils";
const Skill = () => {
const loading = useSelector(state => state.loading);
const [loading] = useContext(LoadingContext);
const renderSkeleton = () => {
let array = [];
@@ -24,6 +25,15 @@ const Skill = () => {
(typeof config.skills !== 'undefined' && config.skills.length !== 0) && (
<div className="card shadow-lg compact bg-base-100">
<div className="card-body">
<div className="mx-3">
<h5 className="card-title">
{
loading ? skeleton({width: 'w-32', height: 'h-8'}) : (
<span className="opacity-70">Tech Stack</span>
)
}
</h5>
</div>
<div className="p-3 flow-root">
<div className="-m-1 flex flex-wrap">
{

View File

@@ -1,22 +1,25 @@
import { useDispatch, useSelector } from 'react-redux';
import { setTheme } from '../store/slices/themeSlice';
import config from '../config';
import { skeleton } from '../helpers/utils';
import { AiOutlineControl } from 'react-icons/ai';
import { useContext } from 'react';
import { ThemeContext } from '../contexts/ThemeContext';
import { LoadingContext } from '../contexts/LoadingContext';
const ThemeChanger = () => {
const dispatch = useDispatch();
const theme = useSelector(state => state.theme);
const loading = useSelector(state => state.loading);
const [theme, setTheme] = useContext(ThemeContext);
const [loading] = useContext(LoadingContext);
const changeTheme = (e, selectedTheme) => {
e.preventDefault();
dispatch(setTheme(selectedTheme));
document.querySelector('html').setAttribute('data-theme', selectedTheme);
localStorage.setItem('ezprofileTheme', selectedTheme);
setTheme(selectedTheme);
}
return (
<div className="card overflow-visible shadow-lg compact bg-base-100">
<div className="flex-row items-center space-x-4 flex pl-8 pr-2 py-4">
<div className="flex-row items-center space-x-4 flex pl-6 pr-2 py-4">
<div className="flex-1">
<h5 className="card-title">
{
@@ -26,7 +29,9 @@ const ThemeChanger = () => {
}
</h5>
<span className="text-base-content text-opacity-40 capitalize text-sm">
{loading ? skeleton({ width: 'w-16', height: 'h-5' }) : (theme === config.themeConfig.default ? 'Default' : theme)}
{
loading ? skeleton({ width: 'w-16', height: 'h-5' }) : (theme === config.themeConfig.default ? 'Default' : theme)
}
</span>
</div>
<div className="flex-0">
@@ -45,10 +50,10 @@ const ThemeChanger = () => {
<div tabIndex={0} className="mt-16 overflow-y-auto shadow-2xl top-px dropdown-content h-96 w-52 rounded-b-box bg-base-200 text-base-content">
<ul className="p-4 menu compact">
{
config.themeConfig.themes.map((item, index) => (
[config.themeConfig.default, ...config.themeConfig.themes.filter(item => item !== config.themeConfig.default)].map((item, index) => (
<li key={index}>
{/* eslint-disable-next-line */}
<a
href="/"
onClick={(e) => changeTheme(e, item)}
className={`${theme === item ? 'active' : ''}`}
>

View File

@@ -18,6 +18,7 @@ module.exports = {
medium: '',
devto: '',
website: 'https://arifszn.github.io',
phone: '',
email: 'contact@arifszn.com'
},
skills: [
@@ -30,6 +31,7 @@ module.exports = {
'Jquery',
'MySQL',
'Git',
'Docker',
'CSS',
'Antd',
'Tailwind',
@@ -38,7 +40,7 @@ module.exports = {
experiences: [
{
company: 'Monstarlab Bangladesh',
position: 'Software Engineer',
position: 'Backend Engineer',
from: 'September 2021',
to: 'Present'
},
@@ -79,11 +81,15 @@ module.exports = {
// Display blog posts from your medium or dev.to account. (Optional)
source: 'dev.to', // medium | dev.to
username: 'arifszn',
limit: 3 // How many posts to display. Max is 10.
limit: 2 // How many posts to display. Max is 10.
},
googleAnalytics: {
// GA3 tracking id/GA4 tag id
id: '' // UA-XXXXXXXXX-X | G-XXXXXXXXXX
// GA3 tracking id/GA4 tag id UA-XXXXXXXXX-X | G-XXXXXXXXXX
id: 'G-WLLB5E14M6' // Please remove this and use your own tag id
},
hotjar: {
id: '2617601', // Please remove this and use your own id
snippetVersion : 6
},
themeConfig: {
default: 'light',

View File

@@ -0,0 +1,15 @@
import { createContext, useState } from "react";
const initialValue = true;
export const LoadingContext = createContext();
export const LoadingProvider = (props) => {
const [loading, setLoading] = useState(initialValue);
return (
<LoadingContext.Provider value={[loading, setLoading]}>
{props.children}
</LoadingContext.Provider>
);
}

View File

@@ -0,0 +1,16 @@
import { createContext, useState } from "react";
import { getInitialTheme } from "../helpers/utils";
const initialValue = getInitialTheme();
export const ThemeContext = createContext();
export const ThemeProvider = (props) => {
const [theme, setTheme] = useState(initialValue);
return (
<ThemeContext.Provider value={[theme, setTheme]}>
{props.children}
</ThemeContext.Provider>
);
}

View File

@@ -1,7 +1,8 @@
import config from "../config";
import colors from './colors.json';
import { hotjar } from 'react-hotjar';
export const getThemeValue = () => {
export const getInitialTheme = () => {
if (config.themeConfig.disableSwitch) {
return config.themeConfig.default;
}
@@ -68,3 +69,11 @@ export const isThemeDarkish = (theme) => {
return false;
}
}
export const setupHotjar = () => {
if (config.hotjar?.id) {
let snippetVersion = config.hotjar?.snippetVersion ? config.hotjar?.snippetVersion : 6;
hotjar.initialize(config.hotjar.id, snippetVersion);
}
}

View File

@@ -2,20 +2,24 @@ import React from 'react';
import ReactDOM from 'react-dom';
import './index.scss';
import App from './App';
import { Provider } from 'react-redux';
import reportWebVitals from './reportWebVitals';
import { store } from './store/store';
import { HelmetProvider } from 'react-helmet-async';
import { ThemeProvider } from './contexts/ThemeContext';
import { LoadingProvider } from './contexts/LoadingContext';
import { setupHotjar } from './helpers/utils';
ReactDOM.render(
<React.StrictMode>
<Provider store={store}>
<ThemeProvider>
<LoadingProvider>
<HelmetProvider>
<App/>
</HelmetProvider>
</Provider>
</LoadingProvider>
</ThemeProvider>
</React.StrictMode>,
document.getElementById('root')
);
reportWebVitals();
setupHotjar();

View File

@@ -1,19 +0,0 @@
import { createSlice } from '@reduxjs/toolkit';
const initialState = true;
export const loadingSlice = createSlice({
name: 'loading',
initialState: initialState,
reducers: {
setLoading: (state, action) => {
state = action.payload;
return state;
}
}
})
export const { setLoading } = loadingSlice.actions;
export default loadingSlice.reducer;

View File

@@ -1,19 +0,0 @@
import { createSlice } from '@reduxjs/toolkit';
const initialState = null;
export const profileSlice = createSlice({
name: 'profile',
initialState: initialState,
reducers: {
setProfile: (state, action) => {
state = action.payload;
return state;
}
}
})
export const { setProfile } = profileSlice.actions;
export default profileSlice.reducer;

View File

@@ -1,19 +0,0 @@
import { createSlice } from '@reduxjs/toolkit';
const initialState = null;
export const repoSlice = createSlice({
name: 'repo',
initialState: initialState,
reducers: {
setRepo: (state, action) => {
state = action.payload;
return state;
}
}
})
export const { setRepo } = repoSlice.actions;
export default repoSlice.reducer;

View File

@@ -1,23 +0,0 @@
import { createSlice } from '@reduxjs/toolkit';
import { getThemeValue } from '../../helpers/utils';
const initialState = getThemeValue();
export const themeSlice = createSlice({
name: 'theme',
initialState: initialState,
reducers: {
setTheme: (state, action) => {
state = action.payload;
document.querySelector('html').setAttribute('data-theme', state);
localStorage.setItem('ezprofileTheme', state);
return state;
}
}
})
export const { setTheme } = themeSlice.actions;
export default themeSlice.reducer;

View File

@@ -1,16 +0,0 @@
import { combineReducers, configureStore } from '@reduxjs/toolkit';
import loadingSlice from './slices/loadingSlice';
import profileSlice from './slices/profileSlice';
import repoSlice from './slices/repoSlice';
import themeSlice from './slices/themeSlice';
const rootReducer = combineReducers({
profile: profileSlice,
theme: themeSlice,
loading: loadingSlice,
repo: repoSlice,
})
export const store = configureStore({
reducer: rootReducer
})