remove top right buttons, move stars button to top right, add delete account button, use mock data in step 1, change some descriptions

This commit is contained in:
martin-mfg
2025-11-27 14:07:18 +01:00
parent 2376e073d3
commit 02047e75e7
9 changed files with 55 additions and 346 deletions
+3
View File
@@ -60,3 +60,6 @@ See [docs/CONTRIBUTING.md](https://github.com/avgupta456/github-trends/blob/main
## Acknowledgements
Much inspiration was taken from [GitHub Readme Stats](https://github.com/anuraghazra/github-readme-stats). If you haven't already, check it out and give it a star!
## Other
We use your GitHub API access token to make requests on your behalf. All of our code is open-source and visible on our GitHub repository.
+1 -5
View File
@@ -44,12 +44,8 @@ export function setIsAuthenticated(newIsAuthenticated) {
const defaultAdapter = getAdapter(axios.defaults.adapter);
// mock responses to unauthenticated "anuraghazra" requests
// mock responses to "anuraghazra" requests
axios.defaults.adapter = async (config) => {
if (isAuthenticated) {
return defaultAdapter(config);
}
const params = config.data ? JSON.parse(config.data) : {};
if (
@@ -9,12 +9,9 @@ import {
import Header from './Header';
import LandingScreen from '../Landing';
import { SignUpScreen } from '../Auth';
import HomeScreen from '../Home';
import SettingsScreen from '../Settings';
import { NoMatchScreen } from '../Misc';
import { getUserMetadata } from '../../api';
import Footer from './Footer';
import {
useIsAuthenticated,
useUserKey,
@@ -48,16 +45,11 @@ function App() {
<Header mode="trends" />
<section className="bg-white text-gray-700 flex-grow">
<Routes>
{!isAuthenticated && (
<Route path="/signup" element={<SignUpScreen />} />
)}
<Route path="/user/*" element={<HomeScreen />} />
<Route path="/settings" element={<SettingsScreen />} />
<Route exact path="/" element={<LandingScreen />} />
<Route path="*" element={<NoMatchScreen />} />
</Routes>
</section>
<Footer />
</Router>
</div>
);
-28
View File
@@ -1,28 +0,0 @@
import React from 'react';
import { FaGithub as GithubIcon } from 'react-icons/fa';
function Footer() {
return (
<footer className="body-font">
<div className="bg-gray-100 border-t border-gray-300">
<div className="container mx-auto py-4 px-5 flex justify-center">
<a
href="https://www.github.com/avgupta456/github-trends"
target="_blank"
rel="noopener noreferrer"
>
<button
type="button"
className="rounded-sm shadow bg-gray-700 hover:bg-gray-800 text-gray-50 px-3 py-2 flex items-center"
>
Star on
<GithubIcon className="ml-1.5 w-5 h-5" />
</button>
</a>
</div>
</div>
</footer>
);
}
export default Footer;
+15 -29
View File
@@ -5,13 +5,13 @@ import PropTypes from 'prop-types';
import { Link } from 'react-router-dom';
import { GiHamburgerMenu as HamburgerIcon } from 'react-icons/gi';
import { MdSettings as SettingsIcon } from 'react-icons/md';
import { logout as _logout } from '../../redux/actions/userActions';
import { useIsAuthenticated } from '../../redux/selectors/userSelectors';
import appIcon from '../../assets/appLogo64.png';
import { classnames } from '../../utils';
import { GITHUB_PUBLIC_AUTH_URL } from '../../constants';
import { FaGithub as GithubIcon } from 'react-icons/fa';
const propTypes = {
to: PropTypes.string.isRequired,
@@ -80,35 +80,21 @@ const Header = ({ mode }) => {
<span className="ml-2 text-xl">GitHub Trends</span>
)}
</Link>
{/* Auth Pages: Sign Up, Log In, Log Out */}
{/* Star on GitHub */}
<div className="hidden md:flex ml-auto items-center text-base justify-center">
{isAuthenticated ? (
<>
{mode === 'trends' && (
<Link to="/settings" className="mr-3 px-1 py-1">
<SettingsIcon className="h-6 w-6 text-gray-700" />
</Link>
)}
<StandardLink to="/" onClick={logout}>
Sign Out
</StandardLink>
</>
) : (
<>
<a
className="px-4 py-1 mr-3 rounded-sm bg-gray-200 hover:bg-gray-300 text-gray-700"
href={GITHUB_PUBLIC_AUTH_URL}
>
Login
</a>
<Link
to="/signup"
className="px-4 py-1 mr-3 rounded-sm bg-blue-500 hover:bg-blue-700 hover:text-gray-300"
>
Sign Up
</Link>
</>
)}
<a
href="https://www.github.com/avgupta456/github-trends"
target="_blank"
rel="noopener noreferrer"
>
<button
type="button"
className="rounded-sm shadow bg-gray-700 hover:bg-gray-800 text-gray-50 px-3 py-2 flex items-center"
>
Star on
<GithubIcon className="ml-1.5 w-5 h-5" />
</button>
</a>
</div>
{/* Hamburger Menu */}
<div className="md:hidden flex ml-auto items-center">
+4 -3
View File
@@ -280,12 +280,13 @@ const HomeScreen = () => {
<p>
{privateAccess ? (
<>
You have granted access to both{' '}
<b>public and private</b> repositories.
You granted GitHub Trends access to both your{' '}
<b>public and private</b> contributions.
</>
) : (
<>
You have granted access to <b>public</b> repositories.
You granted GitHub Trends access to your <b>public</b>{' '}
contributions.
</>
)}
</p>
@@ -7,6 +7,7 @@ import { useDispatch } from 'react-redux';
import { Button, Image } from '../../../components';
import { classnames } from '../../../utils';
import {
CLIENT_ID,
GITHUB_PRIVATE_AUTH_URL,
GITHUB_PUBLIC_AUTH_URL,
HOST,
@@ -16,10 +17,13 @@ import { logout as _logout } from '../../../redux/actions/userActions';
import {
useIsAuthenticated,
usePrivateAccess,
useUserId,
useUserKey,
} from '../../../redux/selectors/userSelectors';
import { deleteAccount } from '../../../api';
const LoginStage = ({ setCurrItem }) => {
const userId = useUserId();
const userKey = useUserKey();
const privateAccess = usePrivateAccess();
const isAuthenticated = useIsAuthenticated();
@@ -28,6 +32,13 @@ const LoginStage = ({ setCurrItem }) => {
const logout = () => {
dispatch(_logout());
};
const deleteAccountHandler = async (myUserId, myUserKey) => {
const success = await deleteAccount(myUserId, myUserKey);
if (success) {
logout();
window.location = `https://github.com/settings/connections/applications/${CLIENT_ID}`;
}
};
// Card data
const cards = [
@@ -81,7 +92,7 @@ const LoginStage = ({ setCurrItem }) => {
</a>
<p className="text-sm text-gray-600 flex-1">
Switch to public access if you prefer not to share private
repository data.
contributions.
</p>
</div>
) : (
@@ -95,8 +106,8 @@ const LoginStage = ({ setCurrItem }) => {
</Button>
</a>
<p className="text-sm text-gray-600 flex-1">
Upgrade to include contributions from your private
repositories for more complete stats.
Upgrade to include contributions in private repositories
for more complete and accurate stats.
</p>
</div>
)}
@@ -111,7 +122,21 @@ const LoginStage = ({ setCurrItem }) => {
<span className="xl:text-lg">Log Out</span>
</Button>
<p className="text-sm text-gray-600 flex-1">
Log out from your GitHub account.
Log out from GitHub Trends.
</p>
</div>
{/* Delete Account Button */}
<div className="mt-6 flex items-center gap-4">
<Button
className="h-12 flex justify-center items-center w-[320px] text-black border border-black bg-white hover:bg-gray-100"
onClick={deleteAccountHandler(userId, userKey)}
>
<span className="xl:text-lg">Delete Account</span>
</Button>
<p className="text-sm text-gray-600 flex-1">
This will delete your GitHub Trends account and then redirect
you to a GitHub screen where you can revoke your access token.
</p>
</div>
</>
@@ -128,7 +153,8 @@ const LoginStage = ({ setCurrItem }) => {
</Button>
</a>
<p className="text-sm text-gray-600 flex-1">
Generate stats based on your public repositories.
Generate stats based on your contributions in public
repositories.
</p>
</div>
@@ -142,7 +168,7 @@ const LoginStage = ({ setCurrItem }) => {
</Button>
</a>
<p className="text-sm text-gray-600 flex-1">
Includes contributions in your private repositories for more
Includes contributions from private repositories for more
complete and accurate stats.
</p>
</div>
@@ -1,264 +0,0 @@
import React, { useEffect, useRef, useState } from 'react';
import PropTypes from 'prop-types';
import { useDispatch } from 'react-redux';
import { Button } from '../../components';
import { logout as _logout } from '../../redux/actions/userActions';
import {
useUserId,
useUserKey,
useIsAuthenticated,
usePrivateAccess,
} from '../../redux/selectors/userSelectors';
import { deleteAccount } from '../../api';
import { classnames } from '../../utils';
import { CLIENT_ID, GITHUB_PRIVATE_AUTH_URL, HOST } from '../../constants';
const SectionButton = ({ name, implemented, isSelected, setSelected }) => {
return (
<div
className={classnames(
'w-full py-3 px-3 hover:bg-gray-100 text-gray-700 text-lg cursor-pointer',
isSelected &&
'pl-2 bg-gray-100 cursor-default border-l-4 border-blue-500',
!implemented && 'opacity-50 cursor-not-allowed',
)}
onClick={() => setSelected('accountTier')}
>
{name}
</div>
);
};
SectionButton.propTypes = {
name: PropTypes.string.isRequired,
implemented: PropTypes.bool,
isSelected: PropTypes.bool.isRequired,
setSelected: PropTypes.func.isRequired,
};
SectionButton.defaultProps = {
implemented: true,
};
function useOutsideAlerter(ref, action) {
useEffect(() => {
/**
* Alert if clicked on outside of element
*/
function handleClickOutside(event) {
if (ref.current && !ref.current.contains(event.target)) {
action();
}
}
// Bind the event listener
document.addEventListener('mousedown', handleClickOutside);
return () => {
// Unbind the event listener on clean up
document.removeEventListener('mousedown', handleClickOutside);
};
}, [ref]);
}
const SettingsScreen = () => {
const [selected, setSelected] = useState('accountTier');
const [deleteModal, setDeleteModal] = useState(false);
const openDeleteModal = () => {
setDeleteModal(true);
};
const closeDeleteModal = () => {
setDeleteModal(false);
};
const wrapperRef = useRef(null);
useOutsideAlerter(wrapperRef, closeDeleteModal);
const userId = useUserId();
const userKey = useUserKey();
const isAuthenticated = useIsAuthenticated();
const privateAccess = usePrivateAccess();
const accountTier = privateAccess ? 'Private Workflow' : 'Public Workflow';
const dispatch = useDispatch();
const logout = () => dispatch(_logout());
console.log(isAuthenticated, userId, userKey, privateAccess, accountTier);
if (!isAuthenticated) {
return (
<div className="h-full py-8 flex justify-center items-center">
<div className="text-center">
<h1 className="text-3xl font-bold">
Please sign in to access this page
</h1>
</div>
</div>
);
}
return (
<div className="h-full w-full">
<div className={classnames('h-full flex', deleteModal && 'opacity-25')}>
<div className="h-full w-1/3 md:w-80 p-4 flex flex-col border-r">
<p className="p-2 text-gray-700 text-lg font-bold">
Account Settings
</p>
<hr />
<SectionButton
name="Account Tier"
isSelected={selected === 'accountTier'}
setSelected={() => setSelected('accountTier')}
/>
<SectionButton
name="Personalization"
implemented={false}
isSelected={selected === 'personalization'}
setSelected={() => setSelected('personalization')}
/>
<SectionButton
name="Delete Account"
isSelected={selected === 'deleteAccount'}
setSelected={() => setSelected('deleteAccount')}
/>
</div>
<div className="h-full w-full">
<div className="container mx-auto p-8">
{selected === 'accountTier' && (
<div>
<p className="mb-1 text-2xl text-gray-700">Account Tier</p>
<hr />
<br />
<p className="flex text-lg">
Current Tier:
<strong className="ml-1">{accountTier}</strong>
</p>
<br />
{privateAccess ? (
<p>
You have given GitHub Trends (read and write) access to all
public and private code contributions. We use your GitHub
API access token to make requests on your behalf. All of our
code is open-source and visible on our
<a
className="ml-1 text-blue-500 underline"
href="https://github.com/avgupta456/github-trends"
rel="noopener noreferrer"
target="_blank"
>
GitHub repository
</a>
</p>
) : (
<p>
You have given GitHub Trends read access to your public
repositories. Upgrading to the Private Workflow will allow
us to better represent your code contributions. We use your
GitHub API access token to make requests on your behalf. All
of our code is open-source and visible on our
<a
className="ml-1 text-blue-500 underline"
href="https://github.com/avgupta456/github-trends"
rel="noopener noreferrer"
target="_blank"
>
GitHub repository
</a>
</p>
)}
<br />
{privateAccess ? (
<a href={`https://${HOST}/api/downgrade?user_key=${userKey}`}>
<Button className="bg-blue-500 text-white rounded-sm">
Downgrade to Public Access
</Button>
</a>
) : (
<a href={GITHUB_PRIVATE_AUTH_URL}>
<Button className="bg-blue-500 text-white rounded-sm">
Upgrade to Private Access
</Button>
</a>
)}
</div>
)}
{selected === 'personalization' && (
<div>
<p className="mb-1 text-2xl text-gray-700">Personalization</p>
<hr />
<br />
<p>Coming soon!</p>
</div>
)}
{selected === 'deleteAccount' && (
<div>
<p className="mb-1 text-2xl text-gray-700">Delete Account</p>
<hr />
<br />
<p>
Deleting your account is permanent and cannot be undone. If
you are sure you want to delete your account, click the button
below to remove your statistics from GitHub Trends. This will
redirect you to a GitHub screen where you can revoke your
access token.
</p>
<br />
<Button
className="bg-gray-200 rounded-sm text-red-600 border-2"
onClick={openDeleteModal}
>
Permanently Delete Account
</Button>
</div>
)}
</div>
</div>
</div>
{deleteModal && (
<div>
<div className="fixed left-0 top-0 w-full h-full">
<div className="w-full h-full flex justify-center items-center">
<div
className="w-96 p-4 bg-white rounded-sm border-2 border-gray-200"
ref={wrapperRef}
>
<p className="mb-1 text-2xl text-gray-700">Delete Account</p>
<hr />
<br />
<p>
Are you sure you want to continue? This action cannot be
undone.
</p>
<br />
<div className="flex flex-wrap">
<Button
className="bg-blue-500 text-white rounded-sm"
onClick={() => setDeleteModal(false)}
>
Cancel
</Button>
<Button
className="bg-gray-200 ml-auto rounded-sm text-red-600 border-2"
onClick={async () => {
const success = await deleteAccount(userId, userKey);
if (success) {
logout();
window.location = `https://github.com/settings/connections/applications/${CLIENT_ID}`;
}
}}
>
Delete Account
</Button>
</div>
</div>
</div>
</div>
</div>
)}
</div>
);
};
export default SettingsScreen;
@@ -1,3 +0,0 @@
import SettingsScreen from './Settings';
export default SettingsScreen;