Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Base Structure and GitHub OAuth Login #97

Merged
merged 6 commits into from
May 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions ui/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
VITE_GITHUB_CLIENT_ID=your_client_id
VITE_GITHUB_REDIRECT_URI=http://localhost:3000/oauth/callback
VITE_GITHUB_CLIENT_SECRET=your_client_secret
VITE_GITHUB_CLIENT_USER_IDENTITY_URL=https://github.com/login/oauth/authorize
VITE_GITHUB_LOGIN_URL=https://github.com/login/oauth/access_token
VITE_GITHUB_CORS_LOGIN_URL=https://cors-anywhere.herokuapp.com/https://github.com/login/oauth/authorize
VITE_GITHUB_SCOPES=repo
VITE_STORAGE_EXPIRY_DURATION=21600000
2 changes: 1 addition & 1 deletion ui/.eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@
*.mjs
*.d.ts
*.d.mts
vite.config.ts
vite.config.ts!/coverage/
99 changes: 99 additions & 0 deletions ui/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,102 @@ yarn-debug.log*
yarn-error.log*
/.stylelintcache
/.eslintcache
!/coverage/
!/.eslintcache
/.eslintcache
/coverage/ui/src/api/api-client.ts.html
/coverage/ui/src/App.tsx.html
/coverage/ui/src/providers/app-provider.tsx.html
/coverage/ui/src/layouts/AppLayout.tsx.html
/coverage/ui/src/providers/auth-provider/auth-provider.tsx.html
/coverage/ui/src/store/auth-store/auth-store.ts.html
/coverage/base.css
/coverage/block-navigation.js
/coverage/clover.xml
/coverage/coverage-final.json
/coverage/favicon.png
/coverage/ui/src/api/github-client.ts.html
/coverage/ui/src/components/Header/Header.tsx.html
/coverage/ui/src/api/repos/github/index.html
/coverage/ui/src/api/repos/index.html
/coverage/ui/src/api/user/github/index.html
/coverage/ui/src/api/user/index.html
/coverage/ui/src/api/index.html
/coverage/ui/src/components/Header/ReposList/index.html
/coverage/ui/src/components/Header/UserMenu/index.html
/coverage/ui/src/components/Header/index.html
/coverage/ui/src/components/Loading/index.html
/coverage/ui/src/components/NavBar/index.html
/coverage/ui/src/components/index.html
/coverage/ui/src/layouts/index.html
/coverage/ui/src/providers/auth-provider/index.html
/coverage/ui/src/providers/index.html
/coverage/ui/src/store/auth-store/index.html
/coverage/ui/src/store/index.html
/coverage/ui/src/utils/index.html
/coverage/ui/src/index.html
/coverage/ui/test-utils/index.html
/coverage/ui/index.html
/coverage/index.html
/coverage/ui/src/api/repos/github/index.ts.html
/coverage/ui/src/api/repos/index.ts.html
/coverage/ui/src/api/user/github/index.ts.html
/coverage/ui/src/api/user/index.ts.html
/coverage/ui/src/api/index.ts.html
/coverage/ui/src/components/Header/ReposList/index.ts.html
/coverage/ui/src/components/Header/UserMenu/index.ts.html
/coverage/ui/src/components/Header/index.ts.html
/coverage/ui/src/components/Loading/index.ts.html
/coverage/ui/src/components/NavBar/index.ts.html
/coverage/ui/src/components/index.ts.html
/coverage/ui/src/layouts/index.ts.html
/coverage/ui/src/providers/auth-provider/index.ts.html
/coverage/ui/src/providers/index.ts.html
/coverage/ui/src/store/auth-store/index.ts.html
/coverage/ui/src/store/index.ts.html
/coverage/ui/src/utils/index.ts.html
/coverage/ui/test-utils/index.ts.html
/coverage/ui/src/components/Loading/Loading.tsx.html
/coverage/ui/src/main.tsx.html
/coverage/ui/src/providers/mantine-provider.tsx.html
/coverage/ui/src/components/NavBar/NavBar.tsx.html
/coverage/ui/postcss.config.cjs.html
/coverage/prettify.css
/coverage/prettify.js
/coverage/ui/src/providers/react-query-provider.tsx.html
/coverage/ui/test-utils/render.tsx.html
/coverage/ui/src/api/repos/repo-query-keys.ts.html
/coverage/ui/src/components/Header/ReposList/ReposList.tsx.html
/coverage/sort-arrow-sprite.png
/coverage/sorter.js
/coverage/ui/src/theme.ts.html
/coverage/ui/src/api/repos/github/use-repos.ts.html
/coverage/ui/src/api/user/github/use-user.ts.html
/coverage/ui/src/api/user/user-query-keys.ts.html
/coverage/ui/src/components/Header/UserMenu/UserMenu.tsx.html
/coverage/ui/src/utils/yaml_to_json.ts.html
/coverage/ui/test-utils/__mocks__/dynamic-store.mock.ts.html
/coverage/ui/test-utils/__mocks__/index.html
/coverage/ui/test-utils/__mocks__/index.ts.html
/coverage/ui/test-utils/__mocks__/repo.mock.ts.html
/coverage/ui/test-utils/__mocks__/repos.mock.ts.html
/coverage/ui/test-utils/__mocks__/user.mock.ts.html
/coverage/.tmp/coverage-0.json
/coverage/.tmp/coverage-1.json
/coverage/.tmp/coverage-2.json
/coverage/.tmp/coverage-3.json
/coverage/.tmp/coverage-4.json
/coverage/.tmp/coverage-5.json
/coverage/.tmp/coverage-6.json
/coverage/.tmp/coverage-7.json
/coverage/ui/src/pages/Login/index.html
/coverage/ui/src/pages/index.html
/coverage/ui/src/routes/index.html
/coverage/ui/src/pages/Login/index.ts.html
/coverage/ui/src/pages/index.ts.html
/coverage/ui/src/routes/index.ts.html
/coverage/ui/src/utils/isAuthenticated.ts.html
/coverage/ui/src/pages/Login/Login.tsx.html
/coverage/ui/src/routes/protected.tsx.html
/coverage/ui/src/routes/router.tsx.html
/coverage/ui/src/providers/router-provider.tsx.html
8 changes: 6 additions & 2 deletions ui/.stylelintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,9 @@
"ignorePseudoClasses": ["global"]
}
]
}
}
},
"ignoreFiles": [
"coverage/**",
"node_modules/**"
]
}
47 changes: 47 additions & 0 deletions ui/DEVELOPER.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Developer Setup Guide

## Setting Up GitHub OAuth

To set up GitHub OAuth for this application, you will need to configure several environment variables in a `.env.local` file. Below are the steps to get you started.

1. **Create a GitHub OAuth App**:
- Go to [GitHub Developer Settings](https://github.com/settings/developers).
- Click on "New OAuth App".
- Fill in the details with your application's information:
- **Application name**: Your app's name
- **Homepage URL**: `http://localhost:3000`
- **Authorization callback URL**: `http://localhost:3000/oauth/callback`
- After creating the app, you will get a **Client ID** and **Client Secret**.

2. **Set Environment Variables**:
- Create a file named `.env.local` in the root directory of your project.
- Copy and paste the following template into `.env.local` and fill in the values with your GitHub OAuth credentials and URLs.

```plaintext
VITE_GITHUB_CLIENT_ID=your_client_id
VITE_GITHUB_REDIRECT_URI=http://localhost:3000/oauth/callback
VITE_GITHUB_CLIENT_SECRET=your_client_secret
VITE_GITHUB_CLIENT_USER_IDENTITY_URL=https://github.com/login/oauth/authorize
VITE_GITHUB_LOGIN_URL=https://github.com/login/oauth/access_token
VITE_GITHUB_CORS_LOGIN_URL=https://cors-anywhere.herokuapp.com/https://github.com/login/oauth/authorize
VITE_GITHUB_SCOPES=repo
VITE_STORAGE_EXPIRY_DURATION=21600000
```

3. **Run the Application**:
- Make sure you have all dependencies installed.
- Start the development server:

```sh
npm install
npm run dev
```

## Additional Notes

- The `VITE_GITHUB_CORS_LOGIN_URL` is used to handle CORS issues during development. You might need to adjust this depending on your deployment environment.
- The `VITE_STORAGE_EXPIRY_DURATION` is set to 6 hours (21600000 milliseconds).

By following these steps, you should be able to set up and run the application with GitHub OAuth configured.

Happy coding!
9 changes: 8 additions & 1 deletion ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
"build": "tsc && vite build",
"preview": "vite preview",
"typecheck": "tsc --noEmit",
"clean": "rm -rf node_modules && rm -rf coverage && yarn cache clean",
"ci": "npm run clean && yarn install",
"code:fix": "npm run lint:fix && npm run prettier:write && npm run typecheck",
"lint": "npm run lint:eslint && npm run lint:stylelint",
"lint:fix": "npm run lint:eslint:fix && npm run lint:stylelint:fix",
Expand All @@ -17,13 +19,15 @@
"prettier": "prettier --check \"**/*.{ts,tsx}\"",
"prettier:write": "prettier --write \"**/*.{ts,tsx}\"",
"vitest": "vitest run",
"vitest:cover": "vitest run --coverage",
"vitest:watch": "vitest",
"test": "npm run typecheck && npm run prettier && npm run lint && npm run vitest"
"test": "npm run typecheck && npm run prettier && npm run lint && npm run vitest:cover"
},
"dependencies": {
"@mantine/core": "^7.9.0",
"@mantine/form": "^7.9.0",
"@mantine/hooks": "^7.9.0",
"@mantine/notifications": "^7.9.2",
"@tabler/icons-react": "^3.3.0",
"@tanstack/react-query": "^5.34.1",
"@testing-library/dom": "^10.1.0",
Expand All @@ -37,6 +41,7 @@
"zustand": "^4.5.2"
},
"devDependencies": {
"@faker-js/faker": "^8.4.1",
"@tanstack/react-query-devtools": "^5.34.1",
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
Expand All @@ -50,6 +55,7 @@
"@typescript-eslint/eslint-plugin": "^7.2.0",
"@typescript-eslint/parser": "^7.2.0",
"@vitejs/plugin-react": "^4.2.1",
"@vitest/coverage-v8": "^1.6.0",
"eslint": "^8.57.0",
"eslint-config-airbnb": "19.0.4",
"eslint-config-airbnb-typescript": "^18.0.0",
Expand All @@ -69,6 +75,7 @@
"stylelint-config-standard-scss": "^13.0.0",
"typescript": "^5.4.5",
"vite": "^5.2.10",
"vite-plugin-istanbul": "^6.0.2",
"vite-tsconfig-paths": "^4.3.1",
"vitest": "^1.5.2"
}
Expand Down
8 changes: 2 additions & 6 deletions ui/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
import '@mantine/core/styles.css';
import '@mantine/notifications/styles.css';
import React, { FC } from 'react';
import './App.css';
import { AppLayout } from '@/layouts';
import { AppProvider } from '@/providers';

export const App: FC = () => (
<AppProvider>
<AppLayout />
</AppProvider>
);
export const App: FC = () => <AppProvider />;
Empty file.
38 changes: 0 additions & 38 deletions ui/src/HOC/WithLoadingAndError/WithLoadingAndError.tsx

This file was deleted.

Empty file.
Empty file removed ui/src/HOC/index.ts
Empty file.
35 changes: 35 additions & 0 deletions ui/src/__tests__/theme.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { theme } from '@/theme';

describe('Theme Configuration', () => {
describe('Colors', () => {
it('should have deepBlue and blue color palettes defined', () => {
expect(theme.colors?.deepBlue).toBeDefined();
expect(theme.colors?.blue).toBeDefined();
});

it('should have 10 shades for deepBlue', () => {
expect(theme.colors?.deepBlue?.length).toBe(10);
});
});

describe('Shadows', () => {
it('should define medium and extra-large shadows', () => {
expect(theme.shadows?.md).toBeDefined();
expect(theme.shadows?.xl).toBeDefined();
});

it('should have correct shadow value for md', () => {
expect(theme.shadows?.md).toBe('1px 1px 3px rgba(0, 0, 0, .25)');
});
});

describe('Typography', () => {
it('should have a Roboto font family for headings', () => {
expect(theme.headings?.fontFamily).toBe('Roboto, sans-serif');
});

it('should define font size for h1', () => {
expect(theme.headings?.sizes?.h1?.fontSize).toBeDefined();
});
});
});
20 changes: 13 additions & 7 deletions ui/src/api/github-client.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
import axios from 'axios';
import { useAuthStore } from '@/store';

export const gitHubClient = () => {
//TODO: Is this a good practice?
const { token } = useAuthStore.getState();

return axios.create({
export const gitHubClient = () =>
axios.create({
baseURL: 'https://api.github.com/',
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json',
},
});

const GithubLoginCredentials = {
clientId: import.meta.env.VITE_GITHUB_CLIENT_ID,
loginUrl: import.meta.env.VITE_GITHUB_CLIENT_USER_IDENTITY_URL,
redirectUri: import.meta.env.VITE_GITHUB_REDIRECT_URI,
scopes: import.meta.env.VITE_GITHUB_SCOPES,
};

export const githubLogin = () => {
const { loginUrl, clientId, redirectUri, scopes } = GithubLoginCredentials;
window.location.href = `${loginUrl}?client_id=${clientId}&redirect_uri=${redirectUri}&scope=${scopes}`;
};
2 changes: 2 additions & 0 deletions ui/src/api/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
export { apiClient } from './api-client';
export { githubLogin } from './github-client';

export * from './user';
export * from './repos';
export * from './oauth';
1 change: 1 addition & 0 deletions ui/src/api/oauth/github/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './use-oauth';
24 changes: 24 additions & 0 deletions ui/src/api/oauth/github/use-oauth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import axios from 'axios';
import { useQuery } from '@tanstack/react-query';

export const exchangeGithubCodeForToken = async (code: string | null) => {
const data = {
client_id: import.meta.env.VITE_GITHUB_CLIENT_ID,
client_secret: import.meta.env.VITE_GITHUB_CLIENT_SECRET,
code,
redirectUri: import.meta.env.VITE_GITHUB_REDIRECT_URI,
};
const response = await axios.post('/login/oauth/access_token', data, {
headers: {
Accept: 'application/json',
},
});
return response.data as OAuth;
};

export const useOAuth = ({ code }: { code: string | null }) =>
useQuery({
queryKey: ['Github', 'oauth', code],
queryFn: () => exchangeGithubCodeForToken(code),
retry: 1,
});
1 change: 1 addition & 0 deletions ui/src/api/oauth/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './github';
5 changes: 5 additions & 0 deletions ui/src/api/oauth/oauth.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
type OAuth = {
access_token: string;
scope: string;
token_type: string;
};
Loading
Loading