feat: add option to upload json backup file

This commit is contained in:
Tailine Chagas
2020-10-11 16:49:09 -03:00
parent cc962a940a
commit 693ac86846
10 changed files with 212 additions and 149 deletions
+98 -73
View File
@@ -2,10 +2,20 @@ import React, { useState, useEffect } from "react"
import { withPrefix } from "gatsby"
import { latestBlogs } from "../utils/workflows"
import links from "../constants/page-links"
import { isMediumUsernameValid, isGitHubUsernameValid } from "../utils/validation"
import { ToolsIcon, XCircleIcon } from "@primer/octicons-react";
import {
isMediumUsernameValid,
isGitHubUsernameValid,
} from "../utils/validation"
import { ToolsIcon, XCircleIcon } from "@primer/octicons-react"
const AddonsItem = ({inputId, inputChecked, onInputChange, Icon, onIconClick, ...props}) => {
const AddonsItem = ({
inputId,
inputChecked,
onInputChange,
Icon,
onIconClick,
...props
}) => {
return (
<div className="py-2 flex justify-start items-center text-sm sm:text-lg">
<label htmlFor={inputId} className="cursor-pointer flex items-center">
@@ -17,71 +27,85 @@ const AddonsItem = ({inputId, inputChecked, onInputChange, Icon, onIconClick, ..
/>
<span className="pl-4">{props.children}</span>
</label>
{
Icon?
<button onClick={onIconClick} className="flex ml-3 focus:bg-gray-400" style={{outline: "none"}}>
<Icon className="transform scale-100 md:scale-125" />
</button>
:''
}
{Icon ? (
<button
onClick={onIconClick}
className="flex ml-3 focus:bg-gray-400"
style={{ outline: "none" }}
>
<Icon className="transform scale-100 md:scale-125" />
</button>
) : (
""
)}
</div>
)
}
const CustomizeBadge = ({githubName, badgeOptions, onBadgeUpdate}) => {
const CustomizeBadge = ({ githubName, badgeOptions, onBadgeUpdate }) => {
return (
<div className={`border-2 border-solid border-gray-900 bg-gray-100 p-2 ml-8`} style={{maxWidth: '21rem'}}>
<div
className={`border-2 border-solid border-gray-900 bg-gray-100 p-2 ml-8`}
style={{ maxWidth: "21rem" }}
>
<header className="text-base sm:text-lg">Customize Badge</header>
<hr className="border-gray-500"/>
<hr className="border-gray-500" />
<div className="text-sm sm:text-lg flex flex-col mt-2 ml-0 md:ml-4">
<label htmlFor="badge-style">Style:&nbsp;
<select
id="badge-style"
onChange={(e) => onBadgeUpdate('badgeStyle', e.target.value)}
value = {badgeOptions.badgeStyle}
<label htmlFor="badge-style">
Style:&nbsp;
<select
id="badge-style"
onChange={e => onBadgeUpdate("badgeStyle", e.target.value)}
value={badgeOptions.badgeStyle}
>
<option value="flat">Flat</option>
<option value="flat-square">Flat Square</option>
<option value="plastic">Plastic</option>
</select>
</select>
</label>
<label htmlFor="badge-color">Color:&nbsp;
<input
type="color"
id="badge-color"
defaultValue={`#${badgeOptions.badgeColor}`}
<label htmlFor="badge-color">
Color:&nbsp;
<input
type="color"
id="badge-color"
defaultValue={`#${badgeOptions.badgeColor}`}
className="w-6"
onChange={(e) => onBadgeUpdate('badgeColor', e.target.value.replace('#', ''))}
onChange={e =>
onBadgeUpdate("badgeColor", e.target.value.replace("#", ""))
}
/>
</label>
<label htmlFor="badge-label-text">Label Text:&nbsp;
<input
type="text"
id="badge-label-text"
placeholder="Profile views"
<label htmlFor="badge-label-text">
Label Text:&nbsp;
<input
type="text"
id="badge-label-text"
placeholder="Profile views"
className="w-2/4 bg-gray-300 pl-2"
onChange={(e) => onBadgeUpdate('badgeLabel', e.target.value.trim())}
onChange={e => onBadgeUpdate("badgeLabel", e.target.value.trim())}
defaultValue={badgeOptions.badgeLabel}
/>
</label>
<span className="mt-2 flex items-center">
Preview:&nbsp;
{
isGitHubUsernameValid(githubName)?
<img
src={`https://komarev.com/ghpvc/`
+ `?username=${githubName}`
+ `&label=${encodeURI(badgeOptions.badgeLabel)}`
+ `&color=${badgeOptions.badgeColor}`
+ `&style=${badgeOptions.badgeStyle}`
}
/>
: <span className="text-xxs md:text-sm text-red-600">Invalid GitHub username</span>
}
{isGitHubUsernameValid(githubName) ? (
<img
src={
`https://komarev.com/ghpvc/` +
`?username=${githubName}` +
`&label=${encodeURI(badgeOptions.badgeLabel)}` +
`&color=${badgeOptions.badgeColor}` +
`&style=${badgeOptions.badgeStyle}`
}
/>
) : (
<span className="text-xxs md:text-sm text-red-600">
Invalid GitHub username
</span>
)}
</span>
</div>
</div>
@@ -89,19 +113,19 @@ const CustomizeBadge = ({githubName, badgeOptions, onBadgeUpdate}) => {
}
const Addons = props => {
const [customizeBadgeOpen, setCustomizeOpen] = useState(false);
const [debounce, setDebounce] = useState(undefined);
const [customizeBadgeOpen, setCustomizeOpen] = useState(false)
const [debounce, setDebounce] = useState(undefined)
const [badgeOptions, setBadgeOptions] = useState({
badgeStyle: props.data.badgeStyle,
badgeColor: props.data.badgeColor,
badgeLabel: props.data.badgeLabel
});
badgeStyle: props.data.badgeStyle,
badgeColor: props.data.badgeColor,
badgeLabel: props.data.badgeLabel,
})
useEffect(() => {
setBadgeOptions({
badgeStyle: props.data.badgeStyle,
badgeColor: props.data.badgeColor,
badgeLabel: props.data.badgeLabel
badgeStyle: props.data.badgeStyle,
badgeColor: props.data.badgeColor,
badgeLabel: props.data.badgeLabel,
})
}, [props.data.badgeStyle, props.data.badgeColor, props.data.badgeLabel])
@@ -134,17 +158,18 @@ const Addons = props => {
}
const onCustomizeClick = () => {
setCustomizeOpen(!customizeBadgeOpen);
setCustomizeOpen(!customizeBadgeOpen)
}
const onBadgeUpdate = (option, value) => {
const callback = () => {
let newVal = (option==='badgeLabel' && value==='')?'Profile views':value;
setBadgeOptions({...badgeOptions, [option]: newVal});
props.handleDataChange(option, {target: {value: newVal}})
let newVal =
option === "badgeLabel" && value === "" ? "Profile views" : value
setBadgeOptions({ ...badgeOptions, [option]: newVal })
props.handleDataChange(option, { target: { value: newVal } })
}
clearTimeout(debounce);
setDebounce(setTimeout(callback, 300));
clearTimeout(debounce)
setDebounce(setTimeout(callback, 300))
}
return (
<div className="flex justify-center items-start flex-col w-full px-2 sm:px-6 mb-10">
@@ -155,20 +180,20 @@ const Addons = props => {
inputId="visitors-count"
inputChecked={props.data.visitorsBadge}
onInputChange={() => props.handleCheckChange("visitorsBadge")}
Icon={ customizeBadgeOpen ? XCircleIcon : ToolsIcon }
Icon={customizeBadgeOpen ? XCircleIcon : ToolsIcon}
onIconClick={onCustomizeClick}
>
display visitors count badge
</AddonsItem>
{
customizeBadgeOpen?
<CustomizeBadge
githubName={props.social.github}
badgeOptions={badgeOptions}
onBadgeUpdate={onBadgeUpdate}
/>
: ''
}
{customizeBadgeOpen ? (
<CustomizeBadge
githubName={props.social.github}
badgeOptions={badgeOptions}
onBadgeUpdate={onBadgeUpdate}
/>
) : (
""
)}
<AddonsItem
inputId="github-profile-trophy"
inputChecked={props.data.githubProfileTrophy}
@@ -229,7 +254,7 @@ const Addons = props => {
download
<span
onClick={blogPostPorkflow}
onKeyDown={(e) => e.keyCode === 13 && blogPostPorkflow()}
onKeyDown={e => e.keyCode === 13 && blogPostPorkflow()}
role="button"
tabIndex="0"
style={{ cursor: "pointer", color: "#002ead" }}
+11 -3
View File
@@ -2,8 +2,11 @@ import React from "react"
const Donate = () => {
return (
<>
<div className="text-center text-4xl my-2">Support&nbsp;
<span role="img" aria-label="praying hand emoji">🙏</span>
<div className="text-center text-4xl my-2">
Support&nbsp;
<span role="img" aria-label="praying hand emoji">
🙏
</span>
</div>
<div className="flex flex-col sm:flex-row items-start justify-between">
<div className="w-full sm:w-2/3">
@@ -30,7 +33,12 @@ const Donate = () => {
</div>
</div>
<div className="w-full sm:w-1/3 flex flex-col justify-center items-center">
<span>Tip<span role="img" aria-label="Dollar medal">💰</span></span>
<span>
Tip
<span role="img" aria-label="Dollar medal">
💰
</span>
</span>
{/* Ko-Fi */}
<a
href="https://ko-fi.com/A0A81XXSX"
+11 -7
View File
@@ -80,11 +80,12 @@ const Markdown = props => {
return ""
}
const VisitorsBadge = props => {
let link = "https://komarev.com/ghpvc/?username="
+ props.github
+ `&label=${props.badgeOptions.badgeLabel}`
+ `&color=${props.badgeOptions.badgeColor}`
+ `&style=${props.badgeOptions.badgeStyle}`
let link =
"https://komarev.com/ghpvc/?username=" +
props.github +
`&label=${props.badgeOptions.badgeLabel}` +
`&color=${props.badgeOptions.badgeColor}` +
`&style=${props.badgeOptions.badgeStyle}`
if (props.show) {
return (
<>
@@ -97,7 +98,10 @@ const Markdown = props => {
return ""
}
const TwitterBadge = props => {
let link = "https://img.shields.io/twitter/follow/" + props.twitter + "?logo=twitter&style=for-the-badge"
let link =
"https://img.shields.io/twitter/follow/" +
props.twitter +
"?logo=twitter&style=for-the-badge"
if (props.show) {
return (
<>
@@ -243,7 +247,7 @@ const Markdown = props => {
badgeOptions={{
badgeLabel: encodeURI(props.data.badgeLabel),
badgeColor: props.data.badgeColor,
badgeStyle: props.data.badgeStyle
badgeStyle: props.data.badgeStyle,
}}
/>
</>
+11 -7
View File
@@ -223,11 +223,12 @@ const MarkdownPreview = props => {
)
}
const VisitorsBadgePreview = props => {
let link = "https://komarev.com/ghpvc/?username="
+ props.github
+ `&label=${props.badgeOptions.badgeLabel}`
+ `&color=${props.badgeOptions.badgeColor}`
+ `&style=${props.badgeOptions.badgeStyle}`
let link =
"https://komarev.com/ghpvc/?username=" +
props.github +
`&label=${props.badgeOptions.badgeLabel}` +
`&color=${props.badgeOptions.badgeColor}` +
`&style=${props.badgeOptions.badgeStyle}`
if (props.show) {
return (
<div className="text-left my-2">
@@ -239,7 +240,10 @@ const MarkdownPreview = props => {
return null
}
const TwitterBadgePreview = props => {
let link = "https://img.shields.io/twitter/follow/" + props.twitter + "?logo=twitter&style=for-the-badge"
let link =
"https://img.shields.io/twitter/follow/" +
props.twitter +
"?logo=twitter&style=for-the-badge"
if (props.show) {
return (
<div className="text-left my-2">
@@ -328,7 +332,7 @@ const MarkdownPreview = props => {
badgeOptions={{
badgeLabel: encodeURI(props.data.badgeLabel),
badgeColor: props.data.badgeColor,
badgeStyle: props.data.badgeStyle
badgeStyle: props.data.badgeStyle,
}}
/>
<GithubProfileTrophyPreview
+1 -1
View File
@@ -166,7 +166,7 @@ const Work = props => {
onChange={event => props.handleLinkChange("resume", event)}
/>
</div>
<div className="text-xs sm:text-lg flex flex-col sm:flex-row mb-10 justify-center sm:justify-start items-center sm:items-start w-full px-4 sm:px-0">
<input
id="funFact-prefix"
+21 -28
View File
@@ -120,7 +120,7 @@ const categorizedSkills = {
"bash",
"azure",
"vagrant",
"circleci"
"circleci",
],
},
@@ -140,7 +140,7 @@ const categorizedSkills = {
"codeigniter",
"rails",
"flask",
"quasar"
"quasar",
],
},
@@ -174,21 +174,21 @@ const categorizedSkills = {
static_site_generator: {
title: "Static Site Generators",
skills: [
"gatsby",
"gridsome",
"hugo",
"jekyll",
"nextjs",
"nuxtjs",
"11ty",
"gatsby",
"gridsome",
"hugo",
"jekyll",
"nextjs",
"nuxtjs",
"11ty",
"scully",
"sculpin",
"sapper",
"vuepress"
"vuepress",
],
},
game_engines:{
game_engines: {
title: "Game Engines",
skills: ["unity"],
},
@@ -268,12 +268,10 @@ const icons = {
"https://devicons.github.io/devicon/devicon.git/icons/oracle/oracle-original.svg",
photoshop:
"https://devicons.github.io/devicon/devicon.git/icons/photoshop/photoshop-plain.svg",
xd:
"https://cdn.worldvectorlogo.com/logos/adobe-xd.svg",
xd: "https://cdn.worldvectorlogo.com/logos/adobe-xd.svg",
php:
"https://devicons.github.io/devicon/devicon.git/icons/php/php-original.svg",
perl:
"https://api.iconify.design/logos-perl.svg",
perl: "https://api.iconify.design/logos-perl.svg",
postgresql:
"https://devicons.github.io/devicon/devicon.git/icons/postgresql/postgresql-original-wordmark.svg",
python:
@@ -361,7 +359,7 @@ const icons = {
nextjs: "https://cdn.worldvectorlogo.com/logos/nextjs-3.svg",
reactnative: "https://reactnative.dev/img/header_logo.svg",
mariadb: "https://www.vectorlogo.zone/logos/mariadb/mariadb-icon.svg",
cockroachdb: "https://worldvectorlogo.com/es/download/cockroachdb.svg",
cockroachdb: "https://worldvectorlogo.com/es/download/cockroachdb.svg",
objectivec:
"https://www.vectorlogo.zone/logos/apple_objectivec/apple_objectivec-icon.svg",
clojure:
@@ -389,16 +387,12 @@ const icons = {
"https://devicons.github.io/devicon/devicon.git/icons/ember/ember-original-wordmark.svg",
scikit_learn:
"https://upload.wikimedia.org/wikipedia/commons/0/05/Scikit_learn_logo_small.svg",
quasar:
"https://cdn.quasar.dev/logo/svg/quasar-logo.svg",
quasar: "https://cdn.quasar.dev/logo/svg/quasar-logo.svg",
kibana:
"https://www.vectorlogo.zone/logos/elasticco_kibana/elasticco_kibana-icon.svg",
grafana:
"https://www.vectorlogo.zone/logos/grafana/grafana-icon.svg",
elasticsearch:
"https://www.vectorlogo.zone/logos/elastic/elastic-icon.svg",
circleci:
"https://www.vectorlogo.zone/logos/circleci/circleci-icon.svg",
grafana: "https://www.vectorlogo.zone/logos/grafana/grafana-icon.svg",
elasticsearch: "https://www.vectorlogo.zone/logos/elastic/elastic-icon.svg",
circleci: "https://www.vectorlogo.zone/logos/circleci/circleci-icon.svg",
scully:
"https://raw.githubusercontent.com/scullyio/scully/main/assets/logos/SVG/scullyio-icon.svg",
"11ty":
@@ -406,11 +400,10 @@ const icons = {
sculpin:
"https://gist.githubusercontent.com/vivek32ta/c7f7bf583c1fb1c58d89301ea40f37fd/raw/1782aef8672484698c0dd407f900c4a329ed5bc4/sculpin.svg",
sapper:
"https://raw.githubusercontent.com/bestofjs/bestofjs-webui/master/public/logos/sapper.svg",
"https://raw.githubusercontent.com/bestofjs/bestofjs-webui/master/public/logos/sapper.svg",
vuepress:
"https://raw.githubusercontent.com/AliasIO/wappalyzer/master/src/drivers/webextension/images/icons/VuePress.svg",
unity:
"https://www.vectorlogo.zone/logos/unity3d/unity3d-icon.svg"
unity: "https://www.vectorlogo.zone/logos/unity3d/unity3d-icon.svg",
}
const skillWebsites = {
@@ -542,7 +535,7 @@ const skillWebsites = {
"11ty": "https://www.11ty.dev/",
sapper: "https://sapper.svelte.dev/",
vuepress: "https://vuepress.vuejs.org/",
unity: "https://unity.com/"
unity: "https://unity.com/",
}
const initialSkillState = {}
+38 -14
View File
@@ -42,7 +42,6 @@ const DEFAULT_PREFIX = {
funFact: "⚡ Fun fact",
portfolio: "👨‍💻 All of my projects are available at",
blog: "📝 I regulary write articles on",
}
const DEFAULT_DATA = {
@@ -376,10 +375,14 @@ const IndexPage = () => {
return
}
setPrefix(cache.prefix ? {...DEFAULT_PREFIX, ...cache.prefix} : DEFAULT_PREFIX)
setData(cache.data ? {...DEFAULT_DATA, ...cache.data} : DEFAULT_DATA)
setLink(cache.link ? {...DEFAULT_LINK, ...cache.link} : DEFAULT_LINK)
setSocial(cache.social ? {...DEFAULT_SOCIAL, ...cache.social} : DEFAULT_SOCIAL)
setPrefix(
cache.prefix ? { ...DEFAULT_PREFIX, ...cache.prefix } : DEFAULT_PREFIX
)
setData(cache.data ? { ...DEFAULT_DATA, ...cache.data } : DEFAULT_DATA)
setLink(cache.link ? { ...DEFAULT_LINK, ...cache.link } : DEFAULT_LINK)
setSocial(
cache.social ? { ...DEFAULT_SOCIAL, ...cache.social } : DEFAULT_SOCIAL
)
const cacheSkills = mergeDefaultWithNewDataSkills(
DEFAULT_SKILLS,
@@ -455,6 +458,15 @@ const IndexPage = () => {
}
}
const handleFileInput = e => {
const file = e.target.files[0]
const reader = new FileReader()
reader.readAsText(file, "UTF-8")
reader.onload = () => {
setRestore(reader.result)
}
}
return (
<Layout>
<div className="m-4 sm:p-4">
@@ -532,7 +544,7 @@ const IndexPage = () => {
) : (
""
)}
{(data.twitterBadge && !social.twitter) ? (
{data.twitterBadge && !social.twitter ? (
<div className="warning">
* Please add twitter username to use these add-ons
</div>
@@ -546,7 +558,7 @@ const IndexPage = () => {
tabIndex="0"
role="button"
onClick={handleGenerate}
onKeyDown={(e) => e.keyCode === 13 && handleGenerate()}
onKeyDown={e => e.keyCode === 13 && handleGenerate()}
>
Generate README
</div>
@@ -678,17 +690,29 @@ const IndexPage = () => {
<input
type="text"
className="outline-none w-1/2 mr-6 border-t-0 border-l-0 border-r-0 border solid border-gray-900 py-1 px-2 focus:border-blue-700 prefix"
placeholder="JSON Backup"
placeholder="Paste JSON code or upload file"
value={restore}
onChange={e => setRestore(e.target.value)}
/>
<button
className="text-xxs sm:text-sm border-2 w-32 border-solid border-gray-900 bg-gray-100 flex items-center justify-center py-1"
onClick={handleRestore}
>
Restore
</button>
<div class="overflow-hidden relative w-64 mt-4 mb-4">
<input
class="cursor-pointer absolute block opacity-0 pin-r pin-t before:cursor-pointer"
type="file"
name="vacancyImageFiles"
onChange={handleFileInput}
/>
<button class="text-xxs sm:text-sm border-2 w-40 border-solid border-gray-900 bg-gray-100 flex items-center justify-center py-1">
Upload json file
</button>
</div>
</div>
<button
className="mr-5 mb-10 text-xxs sm:text-sm border-2 w-32 border-solid border-gray-900 bg-gray-100 flex items-center justify-center py-1"
onClick={handleRestore}
>
Restore
</button>
<div className="flex flex-col items-start justify-center">
<div className="text-green-700 font-medium">Tips</div>
<div className="text-sm sm:text-lg text-gray-700">