├── src ├── App.scss ├── components │ ├── info │ │ ├── Info.js │ │ └── info.scss │ ├── themeswitch │ │ ├── themeswitch.scss │ │ └── ThemeSwitch.js │ ├── header │ │ ├── Header.js │ │ └── header.scss │ ├── input │ │ ├── InputBox.js │ │ └── input.scss │ └── content │ │ ├── contentbox.scss │ │ └── ContentBox.js ├── assets │ ├── map.png │ └── ipwire-logo.svg ├── setupTests.js ├── App.test.js ├── index.js ├── reportWebVitals.js ├── App.js ├── logo.svg └── index.scss ├── README.md ├── .env ├── public ├── favicon.ico ├── robots.txt ├── manifest.json └── index.html ├── .gitignore ├── package.json └── .eslintcache /src/App.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | FAST_REFRESH=false -------------------------------------------------------------------------------- /src/components/info/Info.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/info/info.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DavidHDev/ipwire/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /src/assets/map.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DavidHDev/ipwire/HEAD/src/assets/map.png -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "IP TrackTool", 3 | "name": "IP TrackTool", 4 | "start_url": ".", 5 | "display": "standalone", 6 | "theme_color": "#000000", 7 | "background_color": "#ffffff" 8 | } 9 | -------------------------------------------------------------------------------- /src/setupTests.js: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom'; 6 | -------------------------------------------------------------------------------- /src/App.test.js: -------------------------------------------------------------------------------- 1 | import { render, screen } from '@testing-library/react'; 2 | import App from './App'; 3 | 4 | test('renders learn react link', () => { 5 | render(); 6 | const linkElement = screen.getByText(/learn react/i); 7 | expect(linkElement).toBeInTheDocument(); 8 | }); 9 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.scss'; 4 | import App from './App'; 5 | import { ChakraProvider } from "@chakra-ui/react"; 6 | 7 | ReactDOM.render( 8 | 9 | 10 | , 11 | document.getElementById('root') 12 | ); 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | 13 | # misc 14 | .DS_Store 15 | .env.local 16 | .env.development.local 17 | .env.test.local 18 | .env.production.local 19 | 20 | npm-debug.log* 21 | yarn-debug.log* 22 | yarn-error.log* 23 | -------------------------------------------------------------------------------- /src/reportWebVitals.js: -------------------------------------------------------------------------------- 1 | const reportWebVitals = onPerfEntry => { 2 | if (onPerfEntry && onPerfEntry instanceof Function) { 3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 4 | getCLS(onPerfEntry); 5 | getFID(onPerfEntry); 6 | getFCP(onPerfEntry); 7 | getLCP(onPerfEntry); 8 | getTTFB(onPerfEntry); 9 | }); 10 | } 11 | }; 12 | 13 | export default reportWebVitals; 14 | -------------------------------------------------------------------------------- /src/components/themeswitch/themeswitch.scss: -------------------------------------------------------------------------------- 1 | .app-theme-switcher { 2 | display: flex; 3 | align-items: center; 4 | justify-content: space-between; 5 | z-index: 10; 6 | 7 | .theme-switch-text { 8 | opacity: 0.8; 9 | color: #fff; 10 | font-weight: 500; 11 | margin: 0; 12 | position: relative; 13 | top: -1px; 14 | } 15 | 16 | .theme-icon { 17 | margin-right: 10px; 18 | } 19 | 20 | .app-switch { 21 | margin-left: .5em; 22 | } 23 | } 24 | 25 | @media only screen and (max-width: 500px) { 26 | .app-theme-switcher { 27 | font-size: 12px; 28 | } 29 | } -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import './App.scss'; 2 | import React, { useState } from 'react'; 3 | import { Header } from './components/header/Header'; 4 | import { InputBox } from './components/input/InputBox'; 5 | import { ContentBox } from './components/content/ContentBox'; 6 | 7 | function App() { 8 | 9 | const [content, setContent] = useState([]) 10 | const [country, setCountry] = useState([]) 11 | 12 | return ( 13 |
14 |
15 | 16 | 17 |
18 | ); 19 | } 20 | 21 | export default App; 22 | -------------------------------------------------------------------------------- /src/components/themeswitch/ThemeSwitch.js: -------------------------------------------------------------------------------- 1 | import './themeswitch.scss'; 2 | import React from 'react' 3 | import { Switch } from "@chakra-ui/react" 4 | import useDarkMode from 'use-dark-mode'; 5 | 6 | export const ThemeSwitch = () => { 7 | 8 | const darkMode = useDarkMode(false); 9 | 10 | return ( 11 |
12 | { 13 | darkMode.value === true 14 | ? 15 | : 16 | } 17 | 18 |
19 | ) 20 | } 21 | -------------------------------------------------------------------------------- /src/components/header/Header.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react'; 2 | import { ThemeSwitch } from '../themeswitch/ThemeSwitch'; 3 | import logo from '../../assets/ipwire-logo.svg'; 4 | import './header.scss'; 5 | 6 | export const Header = () => { 7 | 8 | const [scroll, setScroll] = useState(true) 9 | 10 | useEffect(() => { 11 | document.addEventListener("scroll", () => { 12 | const scrollCheck = window.scrollY < 50 13 | if (scrollCheck !== scroll) { 14 | setScroll(scrollCheck) 15 | } 16 | }) 17 | }) 18 | 19 | return ( 20 |
21 | 27 |

{scroll ? 'Track any IP and discover useful information.' : ''}

28 |
29 | ) 30 | } 31 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | 10 | 14 | 15 | 16 | ipwire - IP Tracker 17 | 18 | 19 | 20 |
21 | 22 | 23 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "iptracker", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@chakra-ui/react": "^1.0.0", 7 | "@emotion/react": "^11.1.4", 8 | "@emotion/styled": "^11.0.0", 9 | "@testing-library/jest-dom": "^5.11.8", 10 | "@testing-library/react": "^11.2.3", 11 | "@testing-library/user-event": "^12.6.0", 12 | "axios": "^0.21.4", 13 | "eva-icons": "^1.1.3", 14 | "framer-motion": "^3.2.0", 15 | "leaflet": "^1.7.1", 16 | "pdfmake": "^0.2.4", 17 | "public-ip": "^4.0.3", 18 | "react": "^17.0.1", 19 | "react-dom": "^17.0.1", 20 | "react-hot-loader": "^4.13.0", 21 | "react-image-fallback": "^8.0.0", 22 | "react-leaflet": "^3.0.5", 23 | "react-pdfmake": "^0.3.0", 24 | "react-scripts": "4.0.1", 25 | "sass": "^1.45.1", 26 | "use-dark-mode": "^2.3.1", 27 | "web-vitals": "^0.2.4" 28 | }, 29 | "scripts": { 30 | "start": "react-scripts start", 31 | "build": "react-scripts build && echo ‘/* /index.html 200’ | cat >build/_redirects", 32 | "test": "react-scripts test", 33 | "eject": "react-scripts eject" 34 | }, 35 | "eslintConfig": { 36 | "extends": [ 37 | "react-app", 38 | "react-app/jest" 39 | ] 40 | }, 41 | "browserslist": { 42 | "production": [ 43 | ">0.2%", 44 | "not dead", 45 | "not op_mini all" 46 | ], 47 | "development": [ 48 | "last 1 chrome version", 49 | "last 1 firefox version", 50 | "last 1 safari version" 51 | ] 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/components/header/header.scss: -------------------------------------------------------------------------------- 1 | .app-header-small { 2 | height: 110px !important; 3 | transition: .3s ease !important; 4 | } 5 | 6 | .app-header { 7 | position: fixed; 8 | height: 280px; 9 | top: 0; 10 | width: 100vw; 11 | box-shadow: 0 0 50px rgba(17, 17, 17, 0.35); 12 | background-color: #fff; 13 | z-index: 3; 14 | transition: .3s ease !important; 15 | 16 | .header-title { 17 | font-family: 'Sora', sans-serif; 18 | position: absolute; 19 | z-index: 4; 20 | font-weight: 500; 21 | width: 20ch; 22 | letter-spacing: -1px; 23 | left: 50%; 24 | line-height: 120%; 25 | top: 50%; 26 | transform: translate(-50%, -50%); 27 | font-size: 35px; 28 | color: #f5f5f5; 29 | text-align: center; 30 | } 31 | 32 | .app-navigation { 33 | width: 80%; 34 | margin: 0 auto; 35 | height: 100px; 36 | display: flex; 37 | flex-direction: row-reverse; 38 | align-items: center; 39 | justify-content: space-between; 40 | position: fixed; 41 | left: 50%; 42 | transform: translateX(-50%); 43 | z-index: 999; 44 | 45 | .app-logo { 46 | cursor: pointer; 47 | } 48 | } 49 | } 50 | 51 | @media only screen and (max-width: 1200px) { 52 | .header-title { 53 | width: 80%; 54 | font-size: 30px !important; 55 | } 56 | } 57 | 58 | 59 | @media only screen and (max-width: 500px) { 60 | .header-title { 61 | font-size: 22px !important; 62 | } 63 | 64 | .app-navigation { 65 | width: 95% !important; 66 | } 67 | 68 | .app-logo { 69 | font-size: 12px; 70 | z-index: 99; 71 | left: 1em !important; 72 | top: 1em !important; 73 | } 74 | } -------------------------------------------------------------------------------- /.eslintcache: -------------------------------------------------------------------------------- 1 | [{"/Users/david/Documents/Code/ipwire/src/index.js":"1","/Users/david/Documents/Code/ipwire/src/App.js":"2","/Users/david/Documents/Code/ipwire/src/components/input/InputBox.js":"3","/Users/david/Documents/Code/ipwire/src/components/header/Header.js":"4","/Users/david/Documents/Code/ipwire/src/components/content/ContentBox.js":"5","/Users/david/Documents/Code/ipwire/src/components/themeswitch/ThemeSwitch.js":"6"},{"size":283,"mtime":1640679182578,"results":"7","hashOfConfig":"8"},{"size":587,"mtime":1640679182576,"results":"9","hashOfConfig":"8"},{"size":2199,"mtime":1640688435459,"results":"10","hashOfConfig":"8"},{"size":1000,"mtime":1640685520778,"results":"11","hashOfConfig":"8"},{"size":6796,"mtime":1640695378971,"results":"12","hashOfConfig":"8"},{"size":635,"mtime":1640684113749,"results":"13","hashOfConfig":"8"},{"filePath":"14","messages":"15","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"16"},"1v03f2b",{"filePath":"17","messages":"18","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"16"},{"filePath":"19","messages":"20","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"16"},{"filePath":"21","messages":"22","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"16"},{"filePath":"23","messages":"24","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"25","messages":"26","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"16"},"/Users/david/Documents/Code/ipwire/src/index.js",[],["27","28"],"/Users/david/Documents/Code/ipwire/src/App.js",[],"/Users/david/Documents/Code/ipwire/src/components/input/InputBox.js",[],"/Users/david/Documents/Code/ipwire/src/components/header/Header.js",[],"/Users/david/Documents/Code/ipwire/src/components/content/ContentBox.js",[],"/Users/david/Documents/Code/ipwire/src/components/themeswitch/ThemeSwitch.js",[],{"ruleId":"29","replacedBy":"30"},{"ruleId":"31","replacedBy":"32"},"no-native-reassign",["33"],"no-negated-in-lhs",["34"],"no-global-assign","no-unsafe-negation"] -------------------------------------------------------------------------------- /src/components/input/InputBox.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import publicIp from 'public-ip'; 3 | import axios from 'axios'; 4 | import './input.scss'; 5 | import { Button, Input } from "@chakra-ui/react"; 6 | 7 | export const InputBox = ({ setContent, setCountry }) => { 8 | 9 | const [input, setInput] = useState('') 10 | 11 | const [loader, setLoader] = useState(false); 12 | 13 | const [scroll, setScroll] = useState(true) 14 | 15 | const getContent = (ip) => { 16 | setLoader(true); 17 | axios.get(`https://ipapi.co/${ip}/json/`) 18 | .then(res => { 19 | getCountry(res.data.country_name) 20 | setContent(res.data) 21 | }) 22 | .finally(() => { 23 | setLoader(false); 24 | }) 25 | } 26 | 27 | const getCountry = (countryName) => { 28 | axios.get(`https://restcountries.com/v3.1/name/${countryName}?fullText=true`) 29 | .then(res => { 30 | setCountry(res.data[0]); 31 | }) 32 | } 33 | 34 | const getPublicIp = () => { 35 | return publicIp.v4(); 36 | } 37 | 38 | const fetchData = () => { 39 | getPublicIp() 40 | .then(res => { 41 | setInput(res); 42 | getContent(res); 43 | }) 44 | } 45 | 46 | useEffect(() => { 47 | fetchData(); 48 | // eslint-disable-next-line react-hooks/exhaustive-deps 49 | }, []) 50 | 51 | useEffect(() => { 52 | document.addEventListener("scroll", () => { 53 | const scrollCheck = window.scrollY < 50 54 | if (scrollCheck !== scroll) { 55 | setScroll(scrollCheck) 56 | } 57 | }) 58 | }) 59 | 60 | return ( 61 |
62 | setInput(e.target.value)} 65 | className={"main-input"} 66 | placeholder='IP Address'> 67 | 68 | 69 | 78 |
79 | ) 80 | } 81 | -------------------------------------------------------------------------------- /src/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/index.scss: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css2?family=Sora:wght@300;400;500;600;700&display=swap'); 2 | @import url('/node_modules/eva-icons/style/eva-icons.css'); 3 | 4 | /* width */ 5 | ::-webkit-scrollbar { 6 | width: 10px; 7 | } 8 | 9 | /* Track */ 10 | ::-webkit-scrollbar-track { 11 | background: #fff; 12 | } 13 | 14 | /* Handle */ 15 | ::-webkit-scrollbar-thumb { 16 | background: #cfcfcf; 17 | } 18 | 19 | html { 20 | scroll-behavior: smooth; 21 | } 22 | 23 | body { 24 | overflow-x: hidden; 25 | color: #333 !important; 26 | margin: 0; 27 | font-family: 'Sora', sans-serif; 28 | -webkit-font-smoothing: antialiased; 29 | -moz-osx-font-smoothing: grayscale; 30 | } 31 | 32 | .leaflet-container { 33 | min-height: 600px; 34 | position: relative; 35 | top: -80px; 36 | z-index: 2; 37 | opacity: 1; 38 | } 39 | 40 | .chakra-switch__track { 41 | background: #4B3CFF !important; 42 | background-color: #4B3CFF !important; 43 | } 44 | 45 | body.dark-mode .leaflet-container { 46 | opacity: 0.85; 47 | } 48 | 49 | body.dark-mode .section-title { 50 | color: #f5f5f5; 51 | } 52 | 53 | body.dark-mode .app-header { 54 | background-color: #2F2D41; 55 | } 56 | 57 | body.light-mode { 58 | background-color: #fff; 59 | color: #333; 60 | transition: background-color 0.3s ease; 61 | } 62 | 63 | body.light-mode .header-title { 64 | color: #333; 65 | } 66 | 67 | body.light-mode .app-logo { 68 | filter: invert(95%); 69 | } 70 | 71 | body.dark-mode { 72 | background-color: #2F2D41; 73 | color: #f5f5f5; 74 | } 75 | 76 | body.dark-mode .main-input { 77 | background-color: #201E2D; 78 | color: #f5f5f5; 79 | } 80 | 81 | span.chakra-switch__track { 82 | &:focus { 83 | box-shadow: 0 0 0 3px rgba(66, 201, 225, 0.6) !important; 84 | } 85 | } 86 | 87 | body.dark-mode .chakra-stat__label { 88 | font-family: 'Sora', sans-serif; 89 | color: #f5f5f5; 90 | } 91 | 92 | body.dark-mode .general-content { 93 | background-color: #201E2D; 94 | } 95 | 96 | body.dark-mode .list-item-descrip { 97 | color: #f5f5f5; 98 | 99 | } 100 | 101 | body.dark-mode .content-flag { 102 | background-color: #2F2D41; 103 | } 104 | 105 | body.dark-mode .theme-icon { 106 | color: #fff; 107 | } 108 | 109 | @media only screen and (max-width: 1200px) { 110 | .chakra-stat { 111 | margin-bottom: 1em; 112 | } 113 | } 114 | 115 | .chakra-stat { 116 | font-family: 'Sora', sans-serif; 117 | display: flex !important; 118 | justify-content: center !important; 119 | } 120 | 121 | .chakra-stat__number { 122 | font-family: 'Sora', sans-serif; 123 | color: #4B3CFF; 124 | } 125 | 126 | 127 | body.dark-mode .main-button { 128 | border: none; 129 | background: linear-gradient(-45deg, #514A9D, #24C6DC); 130 | color: #f5f5f5; 131 | 132 | } -------------------------------------------------------------------------------- /src/components/input/input.scss: -------------------------------------------------------------------------------- 1 | .input-box-small { 2 | top: 70px !important; 3 | transition: .3s ease; 4 | } 5 | 6 | .app-inputbox { 7 | position: fixed; 8 | top: 240px; 9 | left: 50%; 10 | transform: translatex(-50%); 11 | z-index: 3; 12 | width: 35vw; 13 | transition: .3s ease; 14 | 15 | .main-input { 16 | font-family: 'Sora', sans-serif; 17 | position: relative; 18 | z-index: 3; 19 | height: 80px; 20 | border: 2px solid transparent; 21 | background-color: #fff; 22 | padding-left: 1.5em; 23 | font-size: 20px; 24 | font-weight: 500; 25 | color: #333; 26 | 27 | &:hover { 28 | border: 2px solid transparent; 29 | } 30 | 31 | &:focus { 32 | border: 2px solid transparent; 33 | box-shadow: 0 0 0 2px rgba(109, 107, 228, 0.2); 34 | } 35 | } 36 | 37 | .dummy-input { 38 | position: absolute; 39 | left: 0; 40 | z-index: 0; 41 | box-shadow: 0 10px 50px rgba(17, 17, 17, 0.35); 42 | } 43 | 44 | 45 | &:hover .dummy-input { 46 | box-shadow: 0 10px 25px rgba(17, 17, 17, 0.35) !important; 47 | 48 | } 49 | 50 | 51 | .main-button { 52 | font-family: 'Sora', sans-serif; 53 | font-weight: 500; 54 | border: none; 55 | background: #4B3CFF !important; 56 | color: #f5f5f5; 57 | min-width: 100px; 58 | right: 1.5em; 59 | height: 40px; 60 | z-index: 4; 61 | position: absolute; 62 | top: 50%; 63 | transition: .3s ease; 64 | transform: translatey(-50%); 65 | 66 | &:hover { 67 | background: #24C6DC; 68 | opacity: 0.8; 69 | transition: .3s ease; 70 | } 71 | 72 | &:focus { 73 | border: none; 74 | box-shadow: 0 0 0 3px rgba(109, 107, 228, 0.5); 75 | } 76 | } 77 | } 78 | 79 | @media only screen and (max-width: 500px) { 80 | .app-inputbox { 81 | width: 95vw; 82 | } 83 | 84 | .input-box-small { 85 | top: 90px !important; 86 | transition: .3s ease; 87 | } 88 | } 89 | 90 | @media only screen and (max-width: 1200px) { 91 | .app-inputbox { 92 | width: 80%; 93 | } 94 | 95 | .input-box-small { 96 | top: 90px !important; 97 | transition: .3s ease; 98 | } 99 | } 100 | 101 | @media only screen and (max-width: 500px) { 102 | .app-inputbox { 103 | width: 95%; 104 | 105 | .main-input { 106 | font-size: 14px; 107 | } 108 | } 109 | 110 | .input-box-small { 111 | top: 90px !important; 112 | transition: .3s ease; 113 | } 114 | } -------------------------------------------------------------------------------- /src/assets/ipwire-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/components/content/contentbox.scss: -------------------------------------------------------------------------------- 1 | .content-wrapper { 2 | margin-top: 360px; 3 | } 4 | 5 | .loader { 6 | width: 100vw; 7 | height: 100vh; 8 | position: absolute; 9 | background: linear-gradient(45deg, #514a9d, #4b3cff); 10 | z-index: 99; 11 | display: flex; 12 | flex-direction: column; 13 | justify-content: center; 14 | align-items: center; 15 | 16 | p { 17 | color: #fff; 18 | margin-top: 1em; 19 | text-align: center; 20 | max-width: 30ch; 21 | } 22 | } 23 | 24 | .placeholder-map { 25 | margin-top: 360px; 26 | height: 600px; 27 | width: 100vw; 28 | background-image: url("../../assets/map.png"); 29 | background-repeat: no-repeat; 30 | background-size: cover; 31 | opacity: 0.2; 32 | z-index: 2; 33 | position: relative; 34 | top: -80px; 35 | 36 | .map-overlay { 37 | position: absolute; 38 | width: 100%; 39 | height: 100%; 40 | opacity: 0.5; 41 | top: 0; 42 | left: 0; 43 | background: linear-gradient(45deg, #514a9d, #24c6dc); 44 | } 45 | } 46 | 47 | .general-content { 48 | position: relative; 49 | top: -140px; 50 | box-shadow: 0 0 50px rgba(17, 17, 17, 0.12); 51 | border-radius: 5px; 52 | padding: 1.5em; 53 | justify-content: space-between; 54 | display: flex; 55 | width: 60%; 56 | margin: 0 auto; 57 | 58 | .details-list { 59 | display: flex; 60 | width: 100%; 61 | flex-direction: column; 62 | 63 | .details-list-item { 64 | display: flex; 65 | justify-content: space-between; 66 | width: 100%; 67 | border-bottom: 1px solid #2f2d41; 68 | padding: 20px 0; 69 | margin-bottom: 15px; 70 | 71 | &:last-child { 72 | border: none; 73 | } 74 | 75 | .list-item-title { 76 | color: #4b3cff; 77 | font-family: "Sora", sans-serif; 78 | margin-right: 2em; 79 | font-weight: 700; 80 | } 81 | 82 | .list-item-descrip { 83 | font-family: "Sora", sans-serif; 84 | } 85 | } 86 | } 87 | } 88 | 89 | .content-section-title { 90 | width: 60%; 91 | margin: 0 auto; 92 | color: #4b3cff; 93 | z-index: 2; 94 | position: relative; 95 | top: -100px; 96 | 97 | .section-title { 98 | margin: 2em 0; 99 | font-size: 30px; 100 | font-family: "Sora", sans-serif; 101 | font-weight: 700; 102 | } 103 | } 104 | 105 | .content-flag { 106 | width: 250px; 107 | position: absolute; 108 | justify-content: space-between; 109 | height: 80px; 110 | background-color: #fff; 111 | box-shadow: 0 0 50px rgba(17, 17, 17, 0.35); 112 | border-radius: 5px; 113 | display: flex; 114 | align-items: center; 115 | padding: 0 2em; 116 | bottom: 2em; 117 | left: 50%; 118 | transform: translatex(-50%); 119 | z-index: 999; 120 | 121 | .flag-img { 122 | border-radius: 5px; 123 | } 124 | 125 | .street-link { 126 | display: flex; 127 | align-items: center; 128 | font-family: "Sora", sans-serif; 129 | font-weight: 600; 130 | font-size: 14px; 131 | color: #fff; 132 | background: #4b3cff !important; 133 | border-radius: 5px; 134 | padding: 7px 15px; 135 | 136 | i { 137 | font-size: 15px; 138 | margin-right: 5px; 139 | } 140 | } 141 | } 142 | 143 | .download-container { 144 | width: 60%; 145 | margin: 0 auto; 146 | height: 100px; 147 | position: relative; 148 | top: -50px; 149 | display: flex; 150 | align-items: flex-start; 151 | 152 | .download-button { 153 | font-family: "Sora", sans-serif; 154 | font-weight: 500; 155 | border: none; 156 | background: #4b3cff !important; 157 | color: #f5f5f5; 158 | padding: 15px 25px; 159 | z-index: 4; 160 | border-radius: 5px; 161 | transition: 0.3s ease; 162 | margin-right: .5em; 163 | transform: translatey(-50%); 164 | 165 | span { 166 | font-size: 14px; 167 | opacity: 0.5; 168 | } 169 | 170 | &:hover { 171 | opacity: 0.8 !important; 172 | transition: 0.3s ease; 173 | } 174 | 175 | &:focus { 176 | border: none; 177 | box-shadow: 0 0 0 3px rgba(109, 107, 228, 0.5); 178 | } 179 | } 180 | } 181 | 182 | .pdf-title { 183 | margin-bottom: 2em; 184 | } 185 | 186 | @media only screen and (max-width: 1200px) { 187 | .content-section-title { 188 | width: 80%; 189 | } 190 | 191 | .download-container { 192 | width: 80%; 193 | } 194 | 195 | .general-content { 196 | width: 80%; 197 | flex-direction: column; 198 | justify-content: flex-start; 199 | align-items: flex-start; 200 | } 201 | } 202 | 203 | @media only screen and (max-width: 500px) { 204 | .content-section-title { 205 | width: 95%; 206 | } 207 | 208 | .download-container { 209 | width: 95%; 210 | } 211 | 212 | .general-content { 213 | width: 95%; 214 | } 215 | } 216 | -------------------------------------------------------------------------------- /src/components/content/ContentBox.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/style-prop-object */ 2 | import { Spinner, Stat, StatLabel, StatNumber } from "@chakra-ui/react"; 3 | import React, { useMemo } from "react"; 4 | import { MapContainer, Marker, TileLayer } from "react-leaflet"; 5 | import pdf, { PDFDocument,PDFText } from 'react-pdfmake'; 6 | import "./contentbox.scss"; 7 | 8 | export const ContentBox = ({ content, country }) => { 9 | 10 | const pdfFile = useMemo((pdfcontent, pdfcountry) => { 11 | if (content.ip) { 12 | pdfcontent = content; 13 | pdfcountry = country; 14 | } 15 | return pdf( 16 | { 20 | return ( 21 | currentNode.headlineLevel === 1 && followingNodesOnPage.length === 0 22 | ); 23 | }} 24 | styles={{ 25 | header: { 26 | fontSize: 18, 27 | margin: [0, 20, 0, 10], 28 | } 29 | }} 30 | > 31 | 32 | {`IP Address: ${pdfcontent?.ip}`} 33 | 34 | 35 | {`General`} 36 | 37 | {`Continent: ${pdfcontent?.region}`} 38 | {`Country: ${pdfcontent?.country_name} (${content?.country_code})`} 39 | {`Region: ${pdfcontent?.region} (${content?.region_code})`} 40 | {`City: ${pdfcontent?.city}`} 41 | 42 | 43 | {`Details`} 44 | 45 | {`EU Member: ${pdfcontent?.in_eu ? 'Yes' : 'No'}`} 46 | {`Population: ${pdfcountry?.population}`} 47 | {`Subregion: ${pdfcountry?.subregion}`} 48 | {`Currency: ${pdfcontent?.currency_name} (${pdfcontent?.currency})`} 49 | {`Zip Code: ${pdfcontent?.postal}`} 50 | {`Capital: ${pdfcontent?.country_capital}`} 51 | {`Timezone: ${pdfcountry?.timezones}`} 52 | {`Coordinates: ${content?.latitude}, ${content?.longitude}`} 53 | 54 | ) 55 | }, [content, country]); 56 | 57 | return ( 58 | <> 59 | {content.ip && ( 60 |
61 | 62 | 66 | 67 |
68 | country-flag 75 | 81 | 82 | Street View 83 | 84 |
85 |
86 |
87 |

General

88 |
89 |
90 | 91 | Continent 92 | {`${country.region} (${content.continent_code})`} 93 | 94 | 95 | Country 96 | {`${content.country_name} (${content.country_code})`} 97 | 98 | 99 | Region 100 | {`${content.region} (${content.region_code})`} 101 | 102 | 103 | City 104 | {`${content.city}`} 105 | 106 |
107 | 108 |
109 |

Details

110 |
111 | 112 |
113 |
114 |
115 |

EU Member

116 |

{content.in_eu ? 'Yes' : 'No'}

117 |
118 |
119 |

Population

120 |

{country.population}

121 |
122 |
123 |

Subregion

124 |

{country.subregion}

125 |
126 |
127 |

Currency

128 |

{`${content.currency_name} (${content.currency})`}

129 |
130 |
131 |

Zip Code

132 |

{content.postal}

133 |
134 |
135 |

Capital

136 |

{content.country_capital}

137 |
138 |
139 |

Timezone

140 |

{country.timezones}

141 |
142 |
143 |

Coordinates

144 |

{`${content.latitude.toFixed(2)}, ${content.longitude.toFixed(2)}`}

145 |
146 |
147 |
148 |
149 | 152 | 155 |
156 |
157 | )} 158 | {!content.ip && ( 159 |
160 | 161 |

Please enable trackers in your browser for this application to work.

162 |
163 | )} 164 | 165 | ); 166 | }; 167 | --------------------------------------------------------------------------------