Initial Commit

This commit is contained in:
Gregory Kenneth Bowne 2024-04-11 22:57:43 -07:00
parent 2ac76a7975
commit 37f0362d41
24 changed files with 19046 additions and 125 deletions

139
.gitignore vendored
View File

@ -1,132 +1,23 @@
# ---> Node # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html) # dependencies
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json /node_modules
/.pnp
.pnp.js
# Runtime data # testing
pids /coverage
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover # production
lib-cov /build
# Coverage directory used by tools like istanbul # misc
coverage .DS_Store
*.lcov .env.local
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional stylelint cache
.stylelintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variable files
.env
.env.development.local .env.development.local
.env.test.local .env.test.local
.env.production.local .env.production.local
.env.local
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Next.js build output
.next
out
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# vuepress v2.x temp and cache directory
.temp
.cache
# Docusaurus cache and generated files
.docusaurus
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*
npm-debug.log*
yarn-debug.log*
yarn-error.log*

View File

@ -1,3 +1,28 @@
# RadioLogger # RadioLogger
Trying to undo the mess created of the RadioLogger app This is a radio hobby logging App in PostgreSQL, ExpressJS, REACT and NodeJS
This can be used for logging Shortwave Radio DX, AM/MW DX, Amateur Radio/Ham Radio contact logging, DX contest logging and Scanner radio logging and possibly for VHF/UHF logging (6m, 2m, etc.)
This will allow you to easily log and store contacts you have made and stations you have heard on the radio.
This is not meant to replace Ham Radio Deluxe or other popular software.
## Languages
<img src="https://img.shields.io/badge/JavaScript-323330?style=for-the-badge&logo=javascript&logoColor=F7DF1E">
## Frameworks & Libraries
<img src="https://img.shields.io/badge/Node.js-43853D?style=for-the-badge&logo=node.js&logoColor=white">
<img src="https://img.shields.io/badge/Express.js-404D59?style=for-the-badge">
<img src="https://img.shields.io/badge/React-20232A?style=for-the-badge&logo=react&logoColor=61DAFB">
## Frontend
<img src="https://img.shields.io/badge/Bootstrap-563D7C?style=for-the-badge&logo=bootstrap&logoColor=white">
<img src="https://img.shields.io/badge/Material--UI-0081CB?style=for-the-badge&logo=material-ui&logoColor=white">
## Backend
<img src="https://img.shields.io/badge/PostgreSQL-316192?style=for-the-badge&logo=postgresql&logoColor=white">

18393
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

43
package.json Normal file
View File

@ -0,0 +1,43 @@
{
"name": "radio-logger",
"version": "0.1.0",
"private": true,
"dependencies": {
"@emotion/react": "^11.11.4",
"@emotion/styled": "^11.11.5",
"@mui/icons-material": "^5.15.15",
"@mui/material": "^5.15.15",
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.22.3",
"react-scripts": "5.0.1",
"web-vitals": "^2.1.4"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

43
public/index.html Normal file
View File

@ -0,0 +1,43 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>

BIN
public/logo192.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

BIN
public/logo512.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

25
public/manifest.json Normal file
View File

@ -0,0 +1,25 @@
{
"short_name": "React App",
"name": "Create React App Sample",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
},
{
"src": "logo192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "logo512.png",
"type": "image/png",
"sizes": "512x512"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}

3
public/robots.txt Normal file
View File

@ -0,0 +1,3 @@
# https://www.robotstxt.org/robotstxt.html
User-agent: *
Disallow:

38
src/App.css Normal file
View File

@ -0,0 +1,38 @@
.App {
text-align: center;
}
.App-logo {
height: 40vmin;
pointer-events: none;
}
@media (prefers-reduced-motion: no-preference) {
.App-logo {
animation: App-logo-spin infinite 20s linear;
}
}
.App-header {
background-color: #282c34;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: white;
}
.App-link {
color: #61dafb;
}
@keyframes App-logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}

23
src/App.js Normal file
View File

@ -0,0 +1,23 @@
import React from 'react';
import { BrowserRouter as Router, Route, Routes } from 'react-router-dom';
import Dashboard from './pages/Register/Register';
import Register from './pages/Dashboard/Dashboard';
import Login from './pages/Login/Login';
import Profile from './pages/Profile/Profile';
import NotFound from './pages/NotFound/NotFound';
const App = () => {
return (
<Router>
<Routes>
<Route path="/" element={<Dashboard />} />
<Route path="/register" element={<Register />} />
<Route path="/login" element={<Login />} />
<Route path="/profile" element={<Profile />} />
<Route path="*" element={<NotFound />} />
</Routes>
</Router>
);
};
export default App;

8
src/App.test.js Normal file
View File

@ -0,0 +1,8 @@
import { render, screen } from '@testing-library/react';
import App from './App';
test('renders learn react link', () => {
render(<App />);
const linkElement = screen.getByText(/learn react/i);
expect(linkElement).toBeInTheDocument();
});

13
src/index.css Normal file
View File

@ -0,0 +1,13 @@
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
}

17
src/index.js Normal file
View File

@ -0,0 +1,17 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

1
src/logo.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 841.9 595.3"><g fill="#61DAFB"><path d="M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50 32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9-22.6 2.4-44 6.1-63.6 11-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4 6.5 3.8 14.1 5.6 22.5 5.6 27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3zm-130.2-66.7c-3.7 12.9-8.3 26.2-13.5 39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4 14.2 2.1 27.9 4.7 41 7.9zm-45.8 106.5c-7.8 13.5-15.8 26.3-24.1 38.2-14.9 1.3-30 2-45.2 2-15.1 0-30.2-.7-45-1.9-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8 6.2-13.4 13.2-26.8 20.7-39.9 7.8-13.5 15.8-26.3 24.1-38.2 14.9-1.3 30-2 45.2-2 15.1 0 30.2.7 45 1.9 8.3 11.9 16.4 24.6 24.2 38 7.6 13.1 14.5 26.4 20.8 39.8-6.3 13.4-13.2 26.8-20.7 39.9zm32.3-13c5.4 13.4 10 26.8 13.8 39.8-13.1 3.2-26.9 5.9-41.2 8 4.9-7.7 9.8-15.6 14.4-23.7 4.6-8 8.9-16.1 13-24.1zM421.2 430c-9.3-9.6-18.6-20.3-27.8-32 9 .4 18.2.7 27.5.7 9.4 0 18.7-.2 27.8-.7-9 11.7-18.3 22.4-27.5 32zm-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9 3.7-12.9 8.3-26.2 13.5-39.5 4.1 8 8.4 16 13.1 24 4.7 8 9.5 15.8 14.4 23.4zM420.7 163c9.3 9.6 18.6 20.3 27.8 32-9-.4-18.2-.7-27.5-.7-9.4 0-18.7.2-27.8.7 9-11.7 18.3-22.4 27.5-32zm-74 58.9c-4.9 7.7-9.8 15.6-14.4 23.7-4.6 8-8.9 16-13 24-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9zm-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6 0-15.7 22.9-35.6 58.3-50.6 8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9-9.2 20.8-16.6 41.1-22.2 60.6-9.9-3.1-19.3-6.5-28-10.2zM310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9 13.5 18.5 27.5 35.3 41.6 50-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7zm237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9 22.6-2.4 44-6.1 63.6-11 2.3 10.1 4.1 19.8 5.2 29.1zm38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9 9.2-20.8 16.6-41.1 22.2-60.6 9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6zM320.8 78.4z"/><circle cx="420.9" cy="296.5" r="45.7"/><path d="M520.5 78.1z"/></g></svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

View File

@ -0,0 +1,69 @@
import React, { useState, useRef, useEffect, useCallback } from 'react';
import { AppBar, Toolbar, IconButton, Typography, Drawer, List, ListItem, ListItemIcon, ListItemText, Button, Box, Container, Grid, Card, CardContent, CardMedia, CardActions } from '@mui/material';
import { useNavigate } from 'react-router-dom';
import MenuIcon from '@mui/icons-material/Menu';
import Brightness4Icon from '@mui/icons-material/Brightness4';
import Brightness7Icon from '@mui/icons-material/Brightness7';
import { useTheme, createTheme, ThemeProvider } from '@mui/material/styles';
import './Dashboard.css'; // Import the CSS file
const Dashboard = () => {
const [drawerOpen, setDrawerOpen] = useState(false);
const [darkMode, setDarkMode] = useState(false);
const theme = useTheme();
const navigate = useNavigate();
const toggleDrawer = (open) => (event) => {
if (event.type === 'keydown' && (event.key === 'Tab' || event.key === 'Shift')) {
return;
}
setDrawerOpen(open);
};
const toggleColorMode = () => {
setDarkMode(!darkMode);
localStorage.setItem('color-mode', darkMode ? 'light' : 'dark');
};
const themeMode = createTheme({
palette: {
mode: darkMode ? 'dark' : 'light',
},
});
return (
<ThemeProvider theme={themeMode}>
<AppBar position="fixed">
<Toolbar>
<IconButton
edge="start"
color="inherit"
aria-label="menu"
onClick={toggleDrawer(true)}
>
<MenuIcon />
</IconButton>
<Typography variant="h6" component="div" sx={{ flexGrow: 1 }}>
Radio Logger
</Typography>
<IconButton color="inherit" onClick={toggleColorMode}>
{darkMode ? <Brightness7Icon /> : <Brightness4Icon />}
</IconButton>
</Toolbar>
</AppBar>
<Drawer anchor="left" open={drawerOpen} onClose={toggleDrawer(false)}>
<List>
{/* Add your menu items here */}
</List>
</Drawer>
<Toolbar />
<Container sx={{ marginTop: 8 }}>
<Grid container spacing={2}>
{/* Add your card elements here */}
</Grid>
</Container>
</ThemeProvider>
);
};
export default Dashboard;

72
src/pages/Login/Login.css Normal file
View File

@ -0,0 +1,72 @@
/* Login.css */
html {
text-align: center;
height: 100%;
}
body {
margin: 0;
padding: 0px;
font-family: sans-serif;
background: linear-gradient(#141e30, #243b55);
}
.login_box {
background-color: rgba(0, 0, 0, 0.5);
position: absolute;
top: 50%;
left: 50%;
width: 400px;
padding: 40px;
transform: translate(-50%, -50%);
box-sizing: border-box;
box-shadow: 0px 15px 25px rgba(0, 0, 0, 0.6);
border-radius: 10px;
}
.login_box h2 {
margin: 0 0 30px;
padding: 0;
color: #fff;
text-align: center;
}
.login_box .user_box {
position: relative;
}
.login_box .user_box input {
width: 100%;
padding: 10px 0;
font-size: 16px;
color: #fff;
margin-bottom: 30px;
border: none;
border-bottom: 1px solid #fff;
outline: none;
background: transparent;
}
.login_box .user_box label {
position: absolute;
top: 0;
left: 0;
padding: 10px 0;
font-size: 16px;
color: #fff;
pointer-events: none;
transition: 0.5s;
}
.login_box .user_box input:focus~label,
.login_box .user_box input:valid~label {
top: -20px;
left: 0;
color: #03e9f4;
font-size: 12px;
}
button {
margin: 0;
background-color: #03e9f4;
}

86
src/pages/Login/Login.jsx Normal file
View File

@ -0,0 +1,86 @@
import React, { useState, useRef, useEffect, useMemo, useCallback } from 'react';
import { Button, TextField, Box, Typography } from '@mui/material';
import { useNavigate } from 'react-router-dom'; // Import useNavigate from react-router-dom
import './Login.css'; // Import the CSS file
const Login = () => {
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const usernameRef = useRef();
const passwordRef = useRef();
const navigate = useNavigate(); // Use useNavigate instead of useHistory
const handleLogin = (e) => {
e.preventDefault();
const API_V = 'http://localhost:3001/api/v1';
const END_POINT = '/login';
const data = {
username: username,
password: password,
};
const headers = {
"Content-type": "application/json; charset=UTF-8",
'Access-Control-Allow-Origin': '*',
};
const options = {
method: 'POST',
headers: headers,
body: JSON.stringify(data),
};
fetch(`${API_V}${END_POINT}`, options)
.then(response => response.json())
.then(data => {
if (data.data) {
console.log('Welcome');
localStorage.setItem('accessToken', data.accessToken);
navigate('/dashboard'); // Use navigate to redirect to dashboard
}
})
.catch(err => console.log(err));
};
return (
<Box className="login_box" sx={{
backgroundColor: 'rgba(0, 0, 0, 0.5)',
position: 'absolute',
top: '50%',
left: '50%',
width: 400,
padding: 4,
transform: 'translate(-50%, -50%)',
boxSizing: 'border-box',
boxShadow: '0px 15px 25px rgba(0, 0, 0, 0.6)',
borderRadius: 10,
}}>
<Typography variant="h4" component="h2" gutterBottom>
Login
</Typography>
<TextField
fullWidth
margin="normal"
label="Username"
variant="outlined"
value={username}
onChange={(e) => setUsername(e.target.value)}
inputRef={usernameRef}
/>
<TextField
fullWidth
margin="normal"
label="Password"
type="password"
variant="outlined"
value={password}
onChange={(e) => setPassword(e.target.value)}
inputRef={passwordRef}
/>
<Button variant="contained" color="primary" onClick={handleLogin}>
Submit
</Button>
</Box>
);
};
export default Login;

View File

@ -0,0 +1,85 @@
import React, { useState, useRef, useEffect, useCallback } from 'react';
import { Box, Card, CardContent, Typography, Button, IconButton, Grid, Carousel, List, ListItem, ListItemIcon, ListItemText, Modal, TextField } from '@mui/material';
import { useNavigate } from 'react-router-dom';
import { Edit as EditIcon, Message as MessageIcon, Follow as FollowIcon, Favorite as LikeIcon, Share as ShareIcon, Email as EmailIcon, Language as WebsiteIcon } from '@mui/icons-material';
const Profile = () => {
const [open, setOpen] = useState(false);
const navigate = useNavigate();
const messageButtonRef = useRef();
const handleOpen = () => setOpen(true);
const handleClose = () => setOpen(false);
const handleMessage = useCallback(async () => {
try {
const response = await fetch('/message', { method: 'GET' });
if (response.ok) {
const data = await response.json();
console.log(data);
} else {
console.error('Error fetching messages:', response.statusText);
}
} catch (error) {
console.error('Error sending message request:', error);
}
}, []);
useEffect(() => {
if (messageButtonRef.current) {
messageButtonRef.current.addEventListener('click', handleMessage);
}
return () => {
if (messageButtonRef.current) {
messageButtonRef.current.removeEventListener('click', handleMessage);
}
};
}, [handleMessage]);
return (
<Box sx={{ padding: 4 }}>
<Grid container spacing={2}>
<Grid item xs={12} md={4}>
<Card>
<CardContent>
<img src="https://via.placeholder.com/150" alt="Profile Picture" style={{ width: '100%', borderRadius: '50%' }} />
<Typography variant="h5" component="h2">Full Name</Typography>
<Typography variant="subtitle1" color="textSecondary">Call Sign</Typography>
<Typography variant="body1">Location</Typography>
<Typography variant="body1">Antenna</Typography>
<Typography variant="body1">Radio Rigs</Typography>
<Typography variant="body1">Transceivers</Typography>
<Typography variant="body1">Receivers</Typography>
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<Button variant="outlined" color="secondary" startIcon={<FollowIcon />}>Follow</Button>
<Button variant="outlined" color="secondary" startIcon={<MessageIcon />} ref={messageButtonRef}>Message</Button>
<Button variant="outlined" color="secondary" startIcon={<LikeIcon />}>Like</Button>
<IconButton color="secondary" onClick={handleOpen}><EditIcon /></IconButton>
<IconButton color="secondary"><ShareIcon /></IconButton>
</Box>
</CardContent>
</Card>
</Grid>
<Grid item xs={12} md={8}>
{/* About Me, Social Media, and Photos sections */}
</Grid>
</Grid>
<Modal
open={open}
onClose={handleClose}
aria-labelledby="edit-profile-modal-label"
aria-describedby="edit-profile-modal-description"
>
<Box sx={{ position: 'absolute', top: '50%', left: '50%', transform: 'translate(-50%, -50%)', width: 400, bgcolor: 'background.paper', boxShadow: 24, p: 4 }}>
<Typography id="edit-profile-modal-label" variant="h6" component="h2">Edit Profile</Typography>
<TextField label="Full Name" fullWidth margin="normal" />
<TextField label="Call Sign" fullWidth margin="normal" />
<TextField label="Location" fullWidth margin="normal" />
<Button variant="contained" color="primary" onClick={handleClose}>Save changes</Button>
</Box>
</Modal>
</Box>
);
};
export default Profile;

View File

@ -0,0 +1,68 @@
import React, { useState } from 'react';
import { Button, TextField, Box, Typography } from '@mui/material';
import { useHistory } from 'react-router-dom';
import '../Login/Login.css'; // Import the CSS file
const Register = () => {
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const [email, setEmail] = useState('');
const history = useHistory();
const handleRegister = (e) => {
e.preventDefault();
// Implement your registration logic here
console.log('Registering user:', username, password, email);
// After successful registration, redirect to login or dashboard
history.push('/login');
};
return (
<Box className="login_box" sx={{
backgroundColor: 'rgba(0, 0, 0, 0.5)',
position: 'absolute',
top: '50%',
left: '50%',
width: 400,
padding: 4,
transform: 'translate(-50%, -50%)',
boxSizing: 'border-box',
boxShadow: '0px 15px 25px rgba(0, 0, 0, 0.6)',
borderRadius: 10,
}}>
<Typography variant="h4" component="h2" gutterBottom>
Register
</Typography>
<TextField
fullWidth
margin="normal"
label="Username"
variant="outlined"
value={username}
onChange={(e) => setUsername(e.target.value)}
/>
<TextField
fullWidth
margin="normal"
label="Email"
variant="outlined"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
<TextField
fullWidth
margin="normal"
label="Password"
type="password"
variant="outlined"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
<Button variant="contained" color="primary" onClick={handleRegister}>
Register
</Button>
</Box>
);
};
export default Register;

13
src/reportWebVitals.js Normal file
View File

@ -0,0 +1,13 @@
const reportWebVitals = onPerfEntry => {
if (onPerfEntry && onPerfEntry instanceof Function) {
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
getCLS(onPerfEntry);
getFID(onPerfEntry);
getFCP(onPerfEntry);
getLCP(onPerfEntry);
getTTFB(onPerfEntry);
});
}
};
export default reportWebVitals;

5
src/setupTests.js Normal file
View File

@ -0,0 +1,5 @@
// jest-dom adds custom jest matchers for asserting on DOM nodes.
// allows you to do things like:
// expect(element).toHaveTextContent(/react/i)
// learn more: https://github.com/testing-library/jest-dom
import '@testing-library/jest-dom';