├── 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 |
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 |
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 |
--------------------------------------------------------------------------------