├── src
├── api
│ └── agent.js
├── features
│ ├── Profile
│ │ ├── profileSlice.js
│ │ ├── Profile.css
│ │ └── Profile.jsx
│ ├── Missions
│ │ ├── Missions.css
│ │ ├── missionSlice.js
│ │ └── Missions.jsx
│ └── Rockets
│ │ ├── Rockets.css
│ │ ├── Rockets.jsx
│ │ └── rocketSlice.js
├── App.css
├── setupTests.js
├── App.js
├── store
│ └── store.js
├── reportWebVitals.js
├── index.css
├── index.js
└── components
│ ├── Navbar.css
│ └── Navbar.jsx
├── README.old.md
├── public
├── robots.txt
├── favicon.ico
├── logo192.png
├── logo512.png
├── planet.png
├── manifest.json
└── index.html
├── .babelrc
├── .stylelintrc.json
├── .eslintrc.json
├── LICENSE
├── package.json
├── .github
└── workflows
│ └── linters.yml
├── .gitignore
└── README.md
/src/api/agent.js:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/README.old.md:
--------------------------------------------------------------------------------
1 | # space-traveller-hub
--------------------------------------------------------------------------------
/src/features/Profile/profileSlice.js:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/App.css:
--------------------------------------------------------------------------------
1 | .home {
2 | padding: 10px;
3 | }
4 |
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Naledi-Dikgale/space-traveller-hub/HEAD/public/favicon.ico
--------------------------------------------------------------------------------
/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Naledi-Dikgale/space-traveller-hub/HEAD/public/logo192.png
--------------------------------------------------------------------------------
/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Naledi-Dikgale/space-traveller-hub/HEAD/public/logo512.png
--------------------------------------------------------------------------------
/public/planet.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Naledi-Dikgale/space-traveller-hub/HEAD/public/planet.png
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | "@babel/preset-react"
4 | ],
5 | "plugins": ["@babel/plugin-syntax-jsx"]
6 | }
--------------------------------------------------------------------------------
/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.js:
--------------------------------------------------------------------------------
1 | import './App.css';
2 | import React from 'react';
3 | import Navbar from './components/Navbar';
4 |
5 | function App() {
6 | return (
7 |
8 |
11 |
12 | );
13 | }
14 |
15 | export default App;
16 |
--------------------------------------------------------------------------------
/src/store/store.js:
--------------------------------------------------------------------------------
1 | import { configureStore } from '@reduxjs/toolkit';
2 | import { rocketSlice } from '../features/Rockets/rocketSlice';
3 | import { missionSlice } from '../features/Missions/missionSlice';
4 |
5 | export default configureStore({
6 | reducer: {
7 | rockets: rocketSlice.reducer,
8 | missions: missionSlice.reducer,
9 | },
10 | });
11 |
--------------------------------------------------------------------------------
/src/reportWebVitals.js:
--------------------------------------------------------------------------------
1 | const reportWebVitals = (onPerfEntry) => {
2 | if (onPerfEntry && onPerfEntry instanceof Function) {
3 | import('web-vitals').then(({
4 | getCLS, getFID, getFCP, getLCP, getTTFB,
5 | }) => {
6 | getCLS(onPerfEntry);
7 | getFID(onPerfEntry);
8 | getFCP(onPerfEntry);
9 | getLCP(onPerfEntry);
10 | getTTFB(onPerfEntry);
11 | });
12 | }
13 | };
14 |
15 | export default reportWebVitals;
16 |
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | font-family:
4 | -apple-system,
5 | BlinkMacSystemFont,
6 | 'Segoe UI',
7 | 'Roboto',
8 | 'Oxygen',
9 | 'Ubuntu',
10 | 'Cantarell',
11 | 'Fira Sans',
12 | 'Droid Sans',
13 | 'Helvetica Neue',
14 | sans-serif;
15 | -webkit-font-smoothing: antialiased;
16 | -moz-osx-font-smoothing: grayscale;
17 | }
18 |
19 | code {
20 | font-family:
21 | source-code-pro,
22 | Menlo,
23 | Monaco,
24 | Consolas,
25 | 'Courier New',
26 | monospace;
27 | }
28 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom/client';
3 | import { BrowserRouter } from 'react-router-dom';
4 | import { Provider } from 'react-redux';
5 | import store from './store/store';
6 |
7 | import './index.css';
8 | import App from './App';
9 |
10 | const root = ReactDOM.createRoot(document.getElementById('root'));
11 | root.render(
12 |
13 |
14 |
15 |
16 |
17 |
18 | ,
19 | );
20 |
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/src/features/Profile/Profile.css:
--------------------------------------------------------------------------------
1 | .profile {
2 | display: flex;
3 | justify-content: space-around;
4 | }
5 |
6 | .listtitle {
7 | margin-bottom: 8px;
8 | padding: 0;
9 | font-size: xx-large;
10 | }
11 |
12 | .listbox {
13 | display: flex;
14 | flex-direction: column;
15 | text-align: start;
16 | width: 45%;
17 | height: auto;
18 | }
19 |
20 | .itemlist {
21 | all: unset;
22 | display: flex;
23 | flex-direction: column;
24 | justify-content: flex-end;
25 | }
26 |
27 | .listitems {
28 | border: 1px solid black;
29 | list-style: none;
30 | font-size: larger;
31 | height: auto;
32 | padding: 20px;
33 | }
34 |
--------------------------------------------------------------------------------
/.stylelintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["stylelint-config-standard"],
3 | "plugins": ["stylelint-scss", "stylelint-csstree-validator"],
4 | "rules": {
5 | "at-rule-no-unknown": [
6 | true,
7 | {
8 | "ignoreAtRules": ["tailwind", "apply", "variants", "responsive", "screen"]
9 | }
10 | ],
11 | "scss/at-rule-no-unknown": [
12 | true,
13 | {
14 | "ignoreAtRules": ["tailwind", "apply", "variants", "responsive", "screen"]
15 | }
16 | ],
17 | "csstree/validator": true
18 | },
19 | "ignoreFiles": ["build/**", "dist/**", "**/reset*.css", "**/bootstrap*.css", "**/*.js", "**/*.jsx"]
20 | }
21 |
--------------------------------------------------------------------------------
/src/components/Navbar.css:
--------------------------------------------------------------------------------
1 | * {
2 | font-family: 'Lucida Sans', 'Lucida Sans Regular', 'Lucida Grande', 'Lucida Sans Unicode', Geneva, Verdana, sans-serif;
3 | font-size: large;
4 | }
5 |
6 | .navbar {
7 | display: flex;
8 | flex-direction: column;
9 | height: 100px;
10 | }
11 |
12 | .navbar a {
13 | all: unset;
14 | }
15 |
16 | .navlist {
17 | display: flex;
18 | align-items: center;
19 | justify-content: space-between;
20 | }
21 |
22 | .navtitle {
23 | display: flex;
24 | align-items: center;
25 | }
26 |
27 | .logo {
28 | height: 80px;
29 | }
30 |
31 | .menu {
32 | padding-right: 80px;
33 | color: aqua;
34 | }
35 |
36 | .navitems {
37 | all: unset;
38 | padding: 6px;
39 | }
40 |
41 | .navitems:hover {
42 | text-align: center;
43 | cursor: pointer;
44 | text-decoration: underline;
45 | }
46 |
47 | .navitems a.active {
48 | text-decoration: underline;
49 | }
50 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "browser": true,
4 | "es6": true,
5 | "jest": true
6 | },
7 | "parser": "@babel/eslint-parser",
8 | "parserOptions": {
9 | "ecmaFeatures": {
10 | "jsx": true
11 | },
12 | "ecmaVersion": 2018,
13 | "sourceType": "module"
14 | },
15 | "extends": ["airbnb", "plugin:react/recommended", "plugin:react-hooks/recommended"],
16 | "plugins": ["react"],
17 | "rules": {
18 | "react/jsx-filename-extension": ["warn", { "extensions": [".js", ".jsx"] }],
19 | "react/react-in-jsx-scope": "off",
20 | "import/no-unresolved": "off",
21 | "no-shadow": "off"
22 | },
23 | "overrides": [
24 | {
25 | // feel free to replace with your preferred file pattern - eg. 'src/**/*Slice.js' or 'redux/**/*Slice.js'
26 | "files": ["src/**/*Slice.js"],
27 | // avoid state param assignment
28 | "rules": { "no-param-reassign": ["error", { "props": false }] }
29 | }
30 | ],
31 | "ignorePatterns": [
32 | "dist/",
33 | "build/"
34 | ]
35 | }
36 |
--------------------------------------------------------------------------------
/src/features/Profile/Profile.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import './Profile.css';
3 | import { useSelector } from 'react-redux';
4 |
5 | function Profile() {
6 | const rockets = useSelector((state) => state.rockets.rockets.filter((elem) => elem.reserved));
7 | const missions = useSelector((state) => state.missions.missions.filter((elem) => elem.joined));
8 | return (
9 |
10 |
11 |
My Missions
12 |
13 | {
14 | missions.map((elem) => ({elem.mission_name} ))
15 | }
16 |
17 |
18 |
19 |
My Rockets
20 |
21 | {
22 | rockets.map((elem) => {elem.rocket_name} )
23 | }
24 |
25 |
26 |
27 | );
28 | }
29 |
30 | export default Profile;
31 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 Naledi
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/features/Missions/Missions.css:
--------------------------------------------------------------------------------
1 | .missions {
2 | padding: 25px;
3 | display: flex;
4 | justify-content: center;
5 | }
6 |
7 | table {
8 | width: 90%;
9 | border-right: 1px solid black;
10 | border-top: 1px solid black;
11 | border-bottom: 1px solid black;
12 | }
13 |
14 | tr:nth-child(odd) {
15 | background-color: #f1f2f2;
16 | }
17 |
18 | th {
19 | background-color: white !important;
20 | text-align: center;
21 | padding: 18px;
22 | border-left: 1px solid black;
23 | }
24 |
25 | td {
26 | text-align: center;
27 | padding: 18px;
28 | border-left: 1px solid black;
29 | }
30 |
31 | .mstatus {
32 | width: max-content;
33 | }
34 |
35 | td button {
36 | width: max-content;
37 | padding: 8px;
38 | border-radius: 5px;
39 | }
40 |
41 | .mstat {
42 | border: 1px solid black;
43 | border-radius: 5px;
44 | padding: 5px;
45 | width: max-content;
46 | color: white;
47 | font-weight: bolder;
48 | }
49 |
50 | .member {
51 | background-color: rgb(15, 112, 157);
52 | }
53 |
54 | .notmember {
55 | background-color: rgb(70, 72, 72);
56 | }
57 |
58 | .leavebtn {
59 | border: 2px solid red;
60 | }
61 |
--------------------------------------------------------------------------------
/src/components/Navbar.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {
3 | NavLink,
4 | Routes,
5 | Route,
6 | } from 'react-router-dom';
7 | import Profile from '../features/Profile/Profile';
8 | import Missions from '../features/Missions/Missions';
9 | import Rockets from '../features/Rockets/Rockets';
10 | import './Navbar.css';
11 |
12 | function Navbar() {
13 | return (
14 |
15 |
16 |
17 |
32 |
33 |
34 | } />
35 | } />
36 | } />
37 |
38 |
39 |
40 |
41 |
42 | );
43 | }
44 |
45 | export default Navbar;
46 |
--------------------------------------------------------------------------------
/src/features/Rockets/Rockets.css:
--------------------------------------------------------------------------------
1 | .rockets {
2 | display: flex;
3 | flex-direction: column;
4 | }
5 |
6 | .rocketimg {
7 | height: 150px;
8 | aspect-ratio: 4 / 3;
9 | }
10 |
11 | .rocketTile {
12 | display: flex;
13 | align-items: center;
14 | padding: 10px;
15 | margin-bottom: 8px;
16 | }
17 |
18 | .reserved {
19 | margin-right: 10px;
20 | font-size: medium;
21 | background-color: rgb(51, 87, 219);
22 | color: white;
23 | width: fit-content;
24 | width: -moz-fit-content;
25 | padding: 6px;
26 | border-radius: 8px;
27 | font-weight: bolder;
28 | }
29 |
30 | .notReserved {
31 | display: none;
32 | }
33 |
34 | .rocketDetails {
35 | display: flex;
36 | flex-direction: column;
37 | justify-content: flex-start;
38 | align-items: flex-start;
39 | margin-left: 20px;
40 | }
41 |
42 | .rocketName {
43 | font-size: larger !important;
44 | margin: 0;
45 | }
46 |
47 | .rocketDescription {
48 | max-width: 800px;
49 | margin-top: 10px;
50 | margin-bottom: 10px;
51 | line-height: 28px;
52 | }
53 |
54 | .reserveButton {
55 | all: unset;
56 | padding: 8px;
57 | text-align: center;
58 | border-radius: 8px;
59 | color: white;
60 | width: fit-content;
61 | width: -moz-fit-content;
62 | }
63 |
64 | .reserveButton:hover {
65 | cursor: pointer;
66 | }
67 |
68 | .cancel {
69 | cursor: pointer;
70 | background-color: rgb(220, 224, 227);
71 | color: black;
72 | }
73 |
74 | .reserve {
75 | background-color: rgb(16, 77, 168);
76 | }
77 |
--------------------------------------------------------------------------------
/src/features/Missions/missionSlice.js:
--------------------------------------------------------------------------------
1 | import { createAsyncThunk, createSlice } from '@reduxjs/toolkit';
2 | import axios from 'axios';
3 |
4 | const initialState = {
5 | missions: [],
6 | loading: false,
7 | error: '',
8 | };
9 |
10 | export const fetchMissions = createAsyncThunk('missions/fetchmissions', () => axios.get('https://api.spacexdata.com/v3/missions')
11 | .then((response) => response.data.map((mission) => (
12 | {
13 | mission_id: mission.mission_id,
14 | mission_name: mission.mission_name,
15 | mission_description: mission.description,
16 | joined: false,
17 | }
18 | ))));
19 |
20 | export const missionSlice = createSlice({
21 | name: 'missions',
22 | initialState,
23 | reducers: {
24 | join: (state, action) => {
25 | const temp = [];
26 | state.missions.forEach((elem) => {
27 | if (elem.mission_id === action.payload) {
28 | temp.push({ ...elem, joined: !elem.joined });
29 | } else {
30 | temp.push(elem);
31 | }
32 | });
33 | return { ...state, missions: temp };
34 | },
35 | },
36 | extraReducers: (builder) => {
37 | builder.addCase(fetchMissions.pending, (state) => ({ ...state, loading: true }));
38 | builder.addCase(fetchMissions.fulfilled, (state, action) => ({
39 | ...state, loading: false, missions: action.payload, error: '',
40 | }));
41 | },
42 | });
43 |
44 | export const { join } = missionSlice.actions;
45 | export default missionSlice.reducer;
46 |
--------------------------------------------------------------------------------
/src/features/Rockets/Rockets.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from 'react';
2 | import './Rockets.css';
3 | import { useSelector, useDispatch } from 'react-redux';
4 | import { fetchRockets, reserve } from './rocketSlice';
5 |
6 | function Rockets() {
7 | const rockets = useSelector((state) => state.rockets.rockets);
8 | const loading = useSelector((state) => state.rockets.loading);
9 | const dispatch = useDispatch();
10 | useEffect(() => {
11 | if (rockets.length === 0) dispatch(fetchRockets());
12 | }, []);
13 | if (loading) {
14 | return Loading...
;
15 | } return (
16 |
17 | {
18 | rockets.map((elem) => (
19 |
20 |
21 |
22 |
{elem.rocket_name}
23 |
24 | Reserved
25 | {elem.rocket_description}
26 |
27 |
28 |
{ dispatch(reserve(elem.rocket_id)); }} type="button" className={elem.reserved ? 'reserveButton cancel' : 'reserveButton reserve'}>
29 | {elem.reserved ? 'Cancel Reservation' : 'Reserve Rocket'}
30 |
31 |
32 |
33 | ))
34 | }
35 |
36 | );
37 | }
38 |
39 | export default Rockets;
40 |
--------------------------------------------------------------------------------
/src/features/Rockets/rocketSlice.js:
--------------------------------------------------------------------------------
1 | import { createAsyncThunk, createSlice } from '@reduxjs/toolkit';
2 | import axios from 'axios';
3 |
4 | const initialState = {
5 | rockets: [],
6 | loading: false,
7 | error: '',
8 | };
9 |
10 | export const fetchRockets = createAsyncThunk('rockets/fetchRockets', () => axios.get('https://api.spacexdata.com/v3/rockets')
11 | .then((response) => response.data.map((rocket) => (
12 | {
13 | rocket_id: rocket.rocket_id,
14 | rocket_img: rocket.flickr_images[0],
15 | rocket_name: rocket.rocket_name,
16 | rocket_description: rocket.description,
17 | reserved: false,
18 | }
19 | ))));
20 |
21 | export const rocketSlice = createSlice({
22 | name: 'rockets',
23 | initialState,
24 | reducers: {
25 | reserve: (state, action) => {
26 | const temp = [];
27 | state.rockets.forEach((rocket) => {
28 | if (rocket.rocket_id === action.payload) {
29 | temp.push({ ...rocket, reserved: !rocket.reserved });
30 | // return { ...rocket, reserved: true };
31 | } else {
32 | temp.push(rocket);
33 | }
34 | // return rocket;
35 | });
36 | return { ...state, rockets: temp };
37 | },
38 |
39 | },
40 | extraReducers: (builder) => {
41 | builder.addCase(fetchRockets.pending, (state) => ({ ...state, loading: true }));
42 | builder.addCase(fetchRockets.fulfilled, (state, action) => ({
43 | ...state, loading: false, rockets: action.payload, error: '',
44 | }));
45 | },
46 | });
47 |
48 | export const { reserve } = rocketSlice.actions;
49 | export default rocketSlice.reducer;
50 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "space-traveller-hub",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@reduxjs/toolkit": "^1.9.5",
7 | "@testing-library/jest-dom": "^5.16.5",
8 | "@testing-library/react": "^13.4.0",
9 | "@testing-library/user-event": "^13.5.0",
10 | "axios": "^1.4.0",
11 | "react": "^18.2.0",
12 | "react-dom": "^18.2.0",
13 | "react-redux": "^8.0.5",
14 | "react-router-dom": "^6.11.1",
15 | "react-scripts": "5.0.1",
16 | "web-vitals": "^2.1.4"
17 | },
18 | "scripts": {
19 | "start": "react-scripts start",
20 | "build": "react-scripts build",
21 | "test": "react-scripts test",
22 | "eject": "react-scripts eject"
23 | },
24 | "eslintConfig": {
25 | "extends": [
26 | "react-app",
27 | "react-app/jest"
28 | ]
29 | },
30 | "browserslist": {
31 | "production": [
32 | ">0.2%",
33 | "not dead",
34 | "not op_mini all"
35 | ],
36 | "development": [
37 | "last 1 chrome version",
38 | "last 1 firefox version",
39 | "last 1 safari version"
40 | ]
41 | },
42 | "devDependencies": {
43 | "@babel/core": "^7.21.8",
44 | "@babel/eslint-parser": "^7.21.8",
45 | "@babel/plugin-syntax-jsx": "^7.21.4",
46 | "@babel/preset-env": "^7.21.5",
47 | "@babel/preset-react": "^7.18.6",
48 | "eslint": "^7.32.0",
49 | "eslint-config-airbnb": "^18.2.1",
50 | "eslint-plugin-import": "^2.27.5",
51 | "eslint-plugin-jsx-a11y": "^6.7.1",
52 | "eslint-plugin-react": "^7.32.2",
53 | "eslint-plugin-react-hooks": "^4.6.0",
54 | "stylelint": "^13.13.1",
55 | "stylelint-config-standard": "^21.0.0",
56 | "stylelint-csstree-validator": "^1.9.0",
57 | "stylelint-scss": "^3.21.0"
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
18 |
27 | React App
28 |
29 |
30 | You need to enable JavaScript to run this app.
31 |
32 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/src/features/Missions/Missions.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from 'react';
2 | import './Missions.css';
3 | import { useSelector, useDispatch } from 'react-redux';
4 | import { fetchMissions, join } from './missionSlice';
5 |
6 | function Missions() {
7 | const missions = useSelector((state) => state.missions.missions);
8 | const loading = useSelector((state) => state.missions.loading);
9 | const dispatch = useDispatch();
10 | useEffect(() => {
11 | if (missions.length === 0) dispatch(fetchMissions());
12 | }, []);
13 | if (loading) {
14 | return Loading...
;
15 | } return (
16 |
17 |
18 |
19 |
20 | Mission
21 | Description
22 | Status
23 |
24 |
25 |
26 |
27 | {
28 | missions.map((elem) => (
29 |
30 | {elem.mission_name}
31 |
32 | {' '}
33 |
34 | {' '}
35 | {elem.mission_description}
36 | {' '}
37 |
38 |
39 |
40 | {
41 | elem.joined ? A member
: Not a member
42 | }
43 |
44 |
45 |
46 | dispatch(join(elem.mission_id))}>
47 | {elem.joined ? 'Leave Mission' : 'Join Mission'}
48 |
49 |
50 |
51 |
52 | ))
53 | }
54 |
55 |
56 |
57 |
58 | );
59 | }
60 |
61 | export default Missions;
62 |
--------------------------------------------------------------------------------
/.github/workflows/linters.yml:
--------------------------------------------------------------------------------
1 | name: Linters
2 |
3 | on: pull_request
4 |
5 | env:
6 | FORCE_COLOR: 1
7 |
8 | jobs:
9 | eslint:
10 | name: ESLint
11 | runs-on: ubuntu-22.04
12 | steps:
13 | - uses: actions/checkout@v3
14 | - uses: actions/setup-node@v3
15 | with:
16 | node-version: "18.x"
17 | - name: Setup ESLint
18 | run: |
19 | npm install --save-dev eslint@7.x eslint-config-airbnb@18.x eslint-plugin-import@2.x eslint-plugin-jsx-a11y@6.x eslint-plugin-react@7.x eslint-plugin-react-hooks@4.x @babel/eslint-parser@7.x @babel/core@7.x @babel/plugin-syntax-jsx@7.x @babel/preset-env@7.x @babel/preset-react@7.x
20 | [ -f .eslintrc.json ] || wget https://raw.githubusercontent.com/microverseinc/linters-config/master/react-redux/.eslintrc.json
21 | [ -f .babelrc ] || wget https://raw.githubusercontent.com/microverseinc/linters-config/master/react-redux/.babelrc
22 | - name: ESLint Report
23 | run: npx eslint "**/*.{js,jsx}"
24 | stylelint:
25 | name: Stylelint
26 | runs-on: ubuntu-22.04
27 | steps:
28 | - uses: actions/checkout@v3
29 | - uses: actions/setup-node@v3
30 | with:
31 | node-version: "18.x"
32 | - name: Setup Stylelint
33 | run: |
34 | npm install --save-dev stylelint@13.x stylelint-scss@3.x stylelint-config-standard@21.x stylelint-csstree-validator@1.x
35 | [ -f .stylelintrc.json ] || wget https://raw.githubusercontent.com/microverseinc/linters-config/master/react-redux/.stylelintrc.json
36 | - name: Stylelint Report
37 | run: npx stylelint "**/*.{css,scss}"
38 | nodechecker:
39 | name: node_modules checker
40 | runs-on: ubuntu-22.04
41 | steps:
42 | - uses: actions/checkout@v3
43 | - name: Check node_modules existence
44 | run: |
45 | if [ -d "node_modules/" ]; then echo -e "\e[1;31mThe node_modules/ folder was pushed to the repo. Please remove it from the GitHub repository and try again."; echo -e "\e[1;32mYou can set up a .gitignore file with this folder included on it to prevent this from happening in the future." && exit 1; fi
46 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | lerna-debug.log*
8 | .pnpm-debug.log*
9 |
10 | # Diagnostic reports (https://nodejs.org/api/report.html)
11 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
12 |
13 | # Runtime data
14 | pids
15 | *.pid
16 | *.seed
17 | *.pid.lock
18 |
19 | # Directory for instrumented libs generated by jscoverage/JSCover
20 | lib-cov
21 |
22 | # Coverage directory used by tools like istanbul
23 | coverage
24 | *.lcov
25 |
26 | # nyc test coverage
27 | .nyc_output
28 |
29 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
30 | .grunt
31 |
32 | # Bower dependency directory (https://bower.io/)
33 | bower_components
34 |
35 | # node-waf configuration
36 | .lock-wscript
37 |
38 | # Compiled binary addons (https://nodejs.org/api/addons.html)
39 | build/Release
40 |
41 | # Dependency directories
42 | node_modules/
43 | jspm_packages/
44 |
45 | # Snowpack dependency directory (https://snowpack.dev/)
46 | web_modules/
47 |
48 | # TypeScript cache
49 | *.tsbuildinfo
50 |
51 | # Optional npm cache directory
52 | .npm
53 |
54 | # Optional eslint cache
55 | .eslintcache
56 |
57 | # Optional stylelint cache
58 | .stylelintcache
59 |
60 | # Microbundle cache
61 | .rpt2_cache/
62 | .rts2_cache_cjs/
63 | .rts2_cache_es/
64 | .rts2_cache_umd/
65 |
66 | # Optional REPL history
67 | .node_repl_history
68 |
69 | # Output of 'npm pack'
70 | *.tgz
71 |
72 | # Yarn Integrity file
73 | .yarn-integrity
74 |
75 | # dotenv environment variable files
76 | .env
77 | .env.development.local
78 | .env.test.local
79 | .env.production.local
80 | .env.local
81 |
82 | # parcel-bundler cache (https://parceljs.org/)
83 | .cache
84 | .parcel-cache
85 |
86 | # Next.js build output
87 | .next
88 | out
89 |
90 | # Nuxt.js build / generate output
91 | .nuxt
92 | dist
93 |
94 | # Gatsby files
95 | .cache/
96 | # Comment in the public line in if your project uses Gatsby and not Next.js
97 | # https://nextjs.org/blog/next-9-1#public-directory-support
98 | # public
99 |
100 | # vuepress build output
101 | .vuepress/dist
102 |
103 | # vuepress v2.x temp and cache directory
104 | .temp
105 | .cache
106 |
107 | # Docusaurus cache and generated files
108 | .docusaurus
109 |
110 | # Serverless directories
111 | .serverless/
112 |
113 | # FuseBox cache
114 | .fusebox/
115 |
116 | # DynamoDB Local files
117 | .dynamodb/
118 |
119 | # TernJS port file
120 | .tern-port
121 |
122 | # Stores VSCode versions used for testing VSCode extensions
123 | .vscode-test
124 |
125 | # yarn v2
126 | .yarn/cache
127 | .yarn/unplugged
128 | .yarn/build-state.yml
129 | .yarn/install-state.gz
130 | .pnp.*
131 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
132 |
133 | # dependencies
134 | /node_modules
135 | /.pnp
136 | .pnp.js
137 |
138 | # testing
139 | /coverage
140 |
141 | # production
142 | /build
143 |
144 | # misc
145 | .DS_Store
146 | .env.local
147 | .env.development.local
148 | .env.test.local
149 | .env.production.local
150 |
151 | npm-debug.log*
152 | yarn-debug.log*
153 | yarn-error.log*
154 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # 📗 Table of Contents
4 |
5 | - [📖 About the Project](#about-project)
6 | - [🛠 Built With](#built-with)
7 | - [Video Walkthrough](#video-walkthrough)
8 | - [Tech Stack](#tech-stack)
9 | - [Key Features](#key-features)
10 | - [💻 Getting Started](#getting-started)
11 | - [Setup](#setup)
12 | - [Prerequisites](#prerequisites)
13 | - [Install](#install)
14 | - [Usage](#usage)
15 | - [Run tests](#run-tests)
16 | - [Deployment](#triangular_flag_on_post-deployment)
17 | - [👥 Authors](#authors)
18 | - [🔭 Future Features](#future-features)
19 | - [🤝 Contributing](#contributing)
20 | - [⭐️ Show your support](#support)
21 | - [🙏 Acknowledgements](#acknowledgements)
22 | - [❓ FAQ (OPTIONAL)](#faq)
23 | - [📝 License](#license)
24 |
25 |
26 |
27 | # 📖 Space Travelers' Hub
28 |
29 |
30 | **Space Travelers' Hub** "Space Travelers hub" is aa web application for a company that provides commercial and scientific space travel services. The application will allow users to book rockets and join selected space missions.
31 |
32 |
33 | ## 🛠 Built With Create React App
34 |
35 |
36 | ### Key Features
37 |
38 | - **Project setup with Create React App**
39 |
40 | (back to top )
41 |
42 | ## 💻 Getting Started
43 |
44 | Try to have these in your local machine
45 |
46 | - Git and GitHub
47 | - VS Code or any code editor
48 |
49 | ### Prerequisites
50 |
51 | In order to run this project you need
52 | - A web browser
53 | - Code editor
54 |
55 | ### Setup
56 |
57 | Clone this repository to your desired folder:
58 |
59 | git clone (https://github.com/Amazinggracee/rocket-mission)
60 |
61 | ### Install
62 |
63 | Install this project using the cloned repo or download and use the files to your local machine.
64 |
65 | ### Usage
66 |
67 | - Clone the repository first
68 | - Commnads to Run:
69 | - npm install
70 | - npm run build
71 | - npm start
72 |
73 | ### Run tests
74 |
75 | - tests are included using Jest framework
76 | - commands to run: npm run test
77 |
78 | ### Deployment
79 |
80 | You can deploy this project through Github Pages
81 |
82 | (back to top )
83 |
84 |
85 |
86 | ## 👥 Authors
87 |
88 | 👤 **Naledi Dikgale**
89 |
90 | - GitHub: [@githubhandle](https://github.com/Naledi-Dikgale)
91 | - Twitter: [@twitterhandle](https://twitter.com/ChichiTheStar)
92 | - LinkedIn: [LinkedIn](https://www.linkedin.com/in/naledi-dikgale-068423159/)
93 | - Website: [@naledi_dikgale](https://nalediportfolio.netlify.app/)
94 |
95 |
96 | (back to top )
97 |
98 | ## 🔭 Future Features
99 |
100 | - [ ] **Changes will be made as comming project requirement.**
101 |
102 | (back to top )
103 |
104 |
105 |
106 | ## 🤝 Contributing
107 |
108 | Contributions, issues, and feature requests are welcome!
109 |
110 | Feel free to check the [issues page](../../issues/).
111 |
112 | (back to top )
113 |
114 | ## ⭐️ Show your support
115 |
116 | If you like this project give a star to this repositiory.
117 |
118 | (back to top )
119 |
120 | ## 🙏 Acknowledgments
121 |
122 | - I would also like to thank Microverse for the guidance they made all the way up to this point.
123 |
124 | (back to top )
125 |
126 | ## ❓ FAQ
127 |
128 | - **Is it allowed to copy the contents of this project and use it for personal use?**
129 |
130 | - This project is free for copying and reusing in any way you like.
131 |
132 | ## 📝 License
133 |
134 | This project is [MIT](/LICENSE.md) licensed.
135 |
136 | (back to top )
137 |
--------------------------------------------------------------------------------