├── .gitignore
├── README.md
├── package.json
├── public
├── favicon.ico
├── index.html
└── manifest.json
├── src
├── App.css
├── App.js
├── App.test.js
├── assets
│ └── crown.svg
├── components
│ ├── collection-item
│ │ ├── collection-item.component.jsx
│ │ └── collection-item.styles.scss
│ ├── collection-preview
│ │ ├── collection-preview.jsx
│ │ └── collection-preview.styles.scss
│ ├── custom-button
│ │ ├── custom-button.component.jsx
│ │ └── custom-button.styles.scss
│ ├── directory
│ │ ├── directory.component.jsx
│ │ └── directory.styles.scss
│ ├── form-input
│ │ ├── form-input.component.jsx
│ │ └── form-input.styles.scss
│ ├── header
│ │ ├── header.component.jsx
│ │ └── header.styles.scss
│ ├── menu-item
│ │ ├── menu-item.component.jsx
│ │ └── menu-item.styles.scss
│ └── sign-in
│ │ ├── sign-in.component.jsx
│ │ └── sign-in.styles.scss
├── firebase
│ └── firebase.utils.js
├── index.css
├── index.js
├── logo.svg
├── pages
│ ├── homepage
│ │ ├── homepage.component.jsx
│ │ └── homepage.styles.scss
│ ├── shop
│ │ ├── shop.component.jsx
│ │ └── shop.data.js
│ └── sign-in-and-sign-up
│ │ ├── sign-in-and-sign-up.component.jsx
│ │ └── sign-in-and-sign-up.styles.scss
└── serviceWorker.js
└── yarn.lock
/.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 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## Lesson-9
2 |
3 | Creating our firebase database and adding the firebase library into our code. Add google sign in and authentication into our application.
4 |
5 | # How to fork and clone
6 |
7 | One quick note about cloning this project. If you wish to make commits and push the code up after cloning this repo, you should fork the project first. In order to own your own copy of this repository, you have to fork it so you get your own copy on your own profile!
8 |
9 | You can see the fork button in the top right corner of every GitHub project; click it and a copy of the project will be added to your GitHub profile under the same name as the original project.
10 |
11 | 
12 |
13 | After forking the project, simply clone it the way you would from the new forked project in your own GitHub repository and you can commit and push to it freely!
14 |
15 |
16 | # After you fork and clone:
17 |
18 | ## Install dependencies
19 |
20 | In your terminal after you clone your project down, remember to run either `yarn` or `npm install` to build all the dependencies in the project.
21 |
22 | ## Set your firebase config
23 |
24 | Remember to replace the `config` variable in your `firebase.utils.js` with your own config object from the firebase dashboard! Navigate to the project settings and scroll down to the config code. Copy the object in the code and replace the variable in your cloned code.
25 |
26 | 
27 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "crwn-clothing",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "firebase": "6.0.2",
7 | "node-sass": "4.12.0",
8 | "react": "^17.0.1",
9 | "react-dom": "^16.8.6",
10 | "react-router-dom": "5.0.0"
11 | },
12 | "devDependencies": {
13 | "react-scripts": "3.0.0"
14 | },
15 | "resolutions": {
16 | "babel-jest": "24.7.1"
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": "react-app"
26 | },
27 | "browserslist": {
28 | "production": [
29 | ">0.2%",
30 | "not dead",
31 | "not op_mini all"
32 | ],
33 | "development": [
34 | "last 1 chrome version",
35 | "last 1 firefox version",
36 | "last 1 safari version"
37 | ]
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZhangMYihua/lesson-9/783fd72e3f6290c9c4efcd072d805f210b40efd0/public/favicon.ico
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
26 | React App
27 |
28 |
29 | You need to enable JavaScript to run this app.
30 |
31 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/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 | "start_url": ".",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/src/App.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-family: 'Open Sans Condensed';
3 | padding: 20px 40px;
4 | }
5 |
6 | a {
7 | text-decoration: none;
8 | color: black;
9 | }
10 |
11 | * {
12 | box-sizing: border-box;
13 | }
14 |
--------------------------------------------------------------------------------
/src/App.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Switch, Route } from 'react-router-dom';
3 |
4 | import './App.css';
5 |
6 | import HomePage from './pages/homepage/homepage.component';
7 | import ShopPage from './pages/shop/shop.component';
8 | import SignInAndSignUpPage from './pages/sign-in-and-sign-up/sign-in-and-sign-up.component';
9 | import Header from './components/header/header.component';
10 | import { auth } from './firebase/firebase.utils';
11 |
12 | class App extends React.Component {
13 | constructor() {
14 | super();
15 |
16 | this.state = {
17 | currentUser: null
18 | };
19 | }
20 |
21 | unsubscribeFromAuth = null;
22 |
23 | componentDidMount() {
24 | this.unsubscribeFromAuth = auth.onAuthStateChanged(user => {
25 | this.setState({ currentUser: user });
26 |
27 | console.log(user);
28 | });
29 | }
30 |
31 | componentWillUnmount() {
32 | this.unsubscribeFromAuth();
33 | }
34 |
35 | render() {
36 | return (
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 | );
46 | }
47 | }
48 |
49 | export default App;
50 |
--------------------------------------------------------------------------------
/src/App.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | it('renders without crashing', () => {
6 | const div = document.createElement('div');
7 | ReactDOM.render( , div);
8 | ReactDOM.unmountComponentAtNode(div);
9 | });
10 |
--------------------------------------------------------------------------------
/src/assets/crown.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Group
5 | Created with Sketch.
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/src/components/collection-item/collection-item.component.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import './collection-item.styles.scss';
4 |
5 | const CollectionItem = ({ id, name, price, imageUrl }) => (
6 |
7 |
13 |
14 | {name}
15 | {price}
16 |
17 |
18 | );
19 |
20 | export default CollectionItem;
21 |
--------------------------------------------------------------------------------
/src/components/collection-item/collection-item.styles.scss:
--------------------------------------------------------------------------------
1 | .collection-item {
2 | width: 22%;
3 | display: flex;
4 | flex-direction: column;
5 | height: 350px;
6 | align-items: center;
7 |
8 | .image {
9 | width: 100%;
10 | height: 95%;
11 | background-size: cover;
12 | background-position: center;
13 | margin-bottom: 5px;
14 | }
15 |
16 | .collection-footer {
17 | width: 100%;
18 | height: 5%;
19 | display: flex;
20 | justify-content: space-between;
21 | font-size: 18px;
22 |
23 | .name {
24 | width: 90%;
25 | margin-bottom: 15px;
26 | }
27 |
28 | .price {
29 | width: 10%;
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/components/collection-preview/collection-preview.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import CollectionItem from '../collection-item/collection-item.component';
4 |
5 | import './collection-preview.styles.scss';
6 |
7 | const CollectionPreview = ({ title, items }) => (
8 |
9 |
{title.toUpperCase()}
10 |
11 | {items
12 | .filter((item, idx) => idx < 4)
13 | .map(({ id, ...otherItemProps }) => (
14 |
15 | ))}
16 |
17 |
18 | );
19 |
20 | export default CollectionPreview;
21 |
--------------------------------------------------------------------------------
/src/components/collection-preview/collection-preview.styles.scss:
--------------------------------------------------------------------------------
1 | .collection-preview {
2 | display: flex;
3 | flex-direction: column;
4 | margin-bottom: 30px;
5 |
6 | .title {
7 | font-size: 28px;
8 | margin-bottom: 25px;
9 | }
10 |
11 | .preview {
12 | display: flex;
13 | justify-content: space-between;
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/components/custom-button/custom-button.component.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import './custom-button.styles.scss';
4 |
5 | const CustomButton = ({ children, isGoogleSignIn, ...otherProps }) => (
6 |
10 | {children}
11 |
12 | );
13 |
14 | export default CustomButton;
15 |
--------------------------------------------------------------------------------
/src/components/custom-button/custom-button.styles.scss:
--------------------------------------------------------------------------------
1 | .custom-button {
2 | min-width: 165px;
3 | width: auto;
4 | height: 50px;
5 | letter-spacing: 0.5px;
6 | line-height: 50px;
7 | padding: 0 35px 0 35px;
8 | font-size: 15px;
9 | background-color: black;
10 | color: white;
11 | text-transform: uppercase;
12 | font-family: 'Open Sans Condensed';
13 | font-weight: bolder;
14 | border: none;
15 | cursor: pointer;
16 |
17 | &:hover {
18 | background-color: white;
19 | color: black;
20 | border: 1px solid black;
21 | }
22 |
23 | &.google-sign-in {
24 | background-color: #4285f4;
25 | color: white;
26 |
27 | &:hover {
28 | background-color: #357ae8;
29 | border: none;
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/components/directory/directory.component.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import MenuItem from '../menu-item/menu-item.component';
4 |
5 | import './directory.styles.scss';
6 |
7 | class Directory extends React.Component {
8 | constructor() {
9 | super();
10 |
11 | this.state = {
12 | sections: [
13 | {
14 | title: 'hats',
15 | imageUrl: 'https://i.ibb.co/cvpntL1/hats.png',
16 | id: 1,
17 | linkUrl: 'hats'
18 | },
19 | {
20 | title: 'jackets',
21 | imageUrl: 'https://i.ibb.co/px2tCc3/jackets.png',
22 | id: 2,
23 | linkUrl: ''
24 | },
25 | {
26 | title: 'sneakers',
27 | imageUrl: 'https://i.ibb.co/0jqHpnp/sneakers.png',
28 | id: 3,
29 | linkUrl: ''
30 | },
31 | {
32 | title: 'womens',
33 | imageUrl: 'https://i.ibb.co/GCCdy8t/womens.png',
34 | size: 'large',
35 | id: 4,
36 | linkUrl: ''
37 | },
38 | {
39 | title: 'mens',
40 | imageUrl: 'https://i.ibb.co/R70vBrQ/men.png',
41 | size: 'large',
42 | id: 5,
43 | linkUrl: ''
44 | }
45 | ]
46 | };
47 | }
48 |
49 | render() {
50 | return (
51 |
52 | {this.state.sections.map(({ id, ...otherSectionProps }) => (
53 |
54 | ))}
55 |
56 | );
57 | }
58 | }
59 |
60 | export default Directory;
61 |
--------------------------------------------------------------------------------
/src/components/directory/directory.styles.scss:
--------------------------------------------------------------------------------
1 | .directory-menu {
2 | width: 100%;
3 | display: flex;
4 | flex-wrap: wrap;
5 | justify-content: space-between;
6 | }
7 |
--------------------------------------------------------------------------------
/src/components/form-input/form-input.component.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import './form-input.styles.scss';
4 |
5 | const FormInput = ({ handleChange, label, ...otherProps }) => (
6 |
7 |
8 | {label ? (
9 |
14 | {label}
15 |
16 | ) : null}
17 |
18 | );
19 |
20 | export default FormInput;
21 |
--------------------------------------------------------------------------------
/src/components/form-input/form-input.styles.scss:
--------------------------------------------------------------------------------
1 | $sub-color: grey;
2 | $main-color: black;
3 |
4 | @mixin shrinkLabel {
5 | top: -14px;
6 | font-size: 12px;
7 | color: $main-color;
8 | }
9 |
10 | .group {
11 | position: relative;
12 | margin: 45px 0;
13 |
14 | .form-input {
15 | background: none;
16 | background-color: white;
17 | color: $sub-color;
18 | font-size: 18px;
19 | padding: 10px 10px 10px 5px;
20 | display: block;
21 | width: 100%;
22 | border: none;
23 | border-radius: 0;
24 | border-bottom: 1px solid $sub-color;
25 | margin: 25px 0;
26 |
27 | &:focus {
28 | outline: none;
29 | }
30 |
31 | &:focus ~ .form-input-label {
32 | @include shrinkLabel();
33 | }
34 | }
35 |
36 | input[type='password'] {
37 | letter-spacing: 0.3em;
38 | }
39 |
40 | .form-input-label {
41 | color: $sub-color;
42 | font-size: 16px;
43 | font-weight: normal;
44 | position: absolute;
45 | pointer-events: none;
46 | left: 5px;
47 | top: 10px;
48 | transition: 300ms ease all;
49 |
50 | &.shrink {
51 | @include shrinkLabel();
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/components/header/header.component.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Link } from 'react-router-dom';
3 |
4 | import { auth } from '../../firebase/firebase.utils';
5 |
6 | import { ReactComponent as Logo } from '../../assets/crown.svg';
7 |
8 | import './header.styles.scss';
9 |
10 | const Header = ({ currentUser }) => (
11 |
12 |
13 |
14 |
15 |
16 |
17 | SHOP
18 |
19 |
20 | CONTACT
21 |
22 | {currentUser ? (
23 |
auth.signOut()}>
24 | SIGN OUT
25 |
26 | ) : (
27 |
28 | SIGN IN
29 |
30 | )}
31 |
32 |
33 | );
34 |
35 | export default Header;
36 |
--------------------------------------------------------------------------------
/src/components/header/header.styles.scss:
--------------------------------------------------------------------------------
1 | .header {
2 | height: 70px;
3 | width: 100%;
4 | display: flex;
5 | justify-content: space-between;
6 | margin-bottom: 25px;
7 |
8 | .logo-container {
9 | height: 100%;
10 | width: 70px;
11 | padding: 25px;
12 | }
13 |
14 | .options {
15 | width: 50%;
16 | height: 100%;
17 | display: flex;
18 | align-items: center;
19 | justify-content: flex-end;
20 |
21 | .option {
22 | padding: 10px 15px;
23 | cursor: pointer;
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/components/menu-item/menu-item.component.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { withRouter } from 'react-router-dom';
3 |
4 | import './menu-item.styles.scss';
5 |
6 | const MenuItem = ({ title, imageUrl, size, history, linkUrl, match }) => (
7 | history.push(`${match.url}${linkUrl}`)}
10 | >
11 |
17 |
18 |
{title.toUpperCase()}
19 | SHOP NOW
20 |
21 |
22 | );
23 |
24 | export default withRouter(MenuItem);
25 |
--------------------------------------------------------------------------------
/src/components/menu-item/menu-item.styles.scss:
--------------------------------------------------------------------------------
1 | .menu-item {
2 | min-width: 30%;
3 | height: 240px;
4 | flex: 1 1 auto;
5 | display: flex;
6 | align-items: center;
7 | justify-content: center;
8 | border: 1px solid black;
9 | margin: 0 7.5px 15px;
10 | overflow: hidden;
11 |
12 | &:hover {
13 | cursor: pointer;
14 |
15 | & .background-image {
16 | transform: scale(1.1);
17 | transition: transform 6s cubic-bezier(0.25, 0.45, 0.45, 0.95);
18 | }
19 |
20 | & .content {
21 | opacity: 0.9;
22 | }
23 | }
24 |
25 | &.large {
26 | height: 380px;
27 | }
28 |
29 | &:first-child {
30 | margin-right: 7.5px;
31 | }
32 |
33 | &:last-child {
34 | margin-left: 7.5px;
35 | }
36 |
37 | .background-image {
38 | width: 100%;
39 | height: 100%;
40 | background-size: cover;
41 | background-position: center;
42 | }
43 |
44 | .content {
45 | height: 90px;
46 | padding: 0 25px;
47 | display: flex;
48 | flex-direction: column;
49 | align-items: center;
50 | justify-content: center;
51 | border: 1px solid black;
52 | background-color: white;
53 | opacity: 0.7;
54 | position: absolute;
55 |
56 | .title {
57 | font-weight: bold;
58 | margin: 0 6px 0;
59 | font-size: 22px;
60 | color: #4a4a4a;
61 | }
62 |
63 | .subtitle {
64 | font-weight: lighter;
65 | font-size: 16px;
66 | }
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/src/components/sign-in/sign-in.component.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import FormInput from '../form-input/form-input.component';
4 | import CustomButton from '../custom-button/custom-button.component';
5 |
6 | import { signInWithGoogle } from '../../firebase/firebase.utils';
7 |
8 | import './sign-in.styles.scss';
9 |
10 | class SignIn extends React.Component {
11 | constructor(props) {
12 | super(props);
13 |
14 | this.state = {
15 | email: '',
16 | password: ''
17 | };
18 | }
19 |
20 | handleSubmit = event => {
21 | event.preventDefault();
22 |
23 | this.setState({ email: '', password: '' });
24 | };
25 |
26 | handleChange = event => {
27 | const { value, name } = event.target;
28 |
29 | this.setState({ [name]: value });
30 | };
31 |
32 | render() {
33 | return (
34 |
35 |
I already have an account
36 |
Sign in with your email and password
37 |
38 |
62 |
63 | );
64 | }
65 | }
66 |
67 | export default SignIn;
68 |
--------------------------------------------------------------------------------
/src/components/sign-in/sign-in.styles.scss:
--------------------------------------------------------------------------------
1 | .sign-in {
2 | width: 380px;
3 | display: flex;
4 | flex-direction: column;
5 |
6 | .title {
7 | margin: 10px 0;
8 | }
9 |
10 | .buttons {
11 | display: flex;
12 | justify-content: space-between;
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/firebase/firebase.utils.js:
--------------------------------------------------------------------------------
1 | import firebase from 'firebase/app';
2 | import 'firebase/firestore';
3 | import 'firebase/auth';
4 |
5 | const config = {
6 | apiKey: 'AIzaSyCdHT-AYHXjF7wOrfAchX4PIm3cSj5tn14',
7 | authDomain: 'crwn-db.firebaseapp.com',
8 | databaseURL: 'https://crwn-db.firebaseio.com',
9 | projectId: 'crwn-db',
10 | storageBucket: 'crwn-db.appspot.com',
11 | messagingSenderId: '850995411664',
12 | appId: '1:850995411664:web:7ddc01d597846f65'
13 | };
14 |
15 | firebase.initializeApp(config);
16 |
17 | export const auth = firebase.auth();
18 | export const firestore = firebase.firestore();
19 |
20 | const provider = new firebase.auth.GoogleAuthProvider();
21 | provider.setCustomParameters({ prompt: 'select_account' });
22 | export const signInWithGoogle = () => auth.signInWithPopup(provider);
23 |
24 | export default firebase;
25 |
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | padding: 0;
4 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
5 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
6 | sans-serif;
7 | -webkit-font-smoothing: antialiased;
8 | -moz-osx-font-smoothing: grayscale;
9 | }
10 |
11 | code {
12 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
13 | monospace;
14 | }
15 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import { BrowserRouter } from 'react-router-dom';
4 |
5 | import './index.css';
6 | import App from './App';
7 |
8 | ReactDOM.render(
9 |
10 |
11 | ,
12 | document.getElementById('root')
13 | );
14 |
--------------------------------------------------------------------------------
/src/logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/pages/homepage/homepage.component.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import Directory from '../../components/directory/directory.component';
4 |
5 | import './homepage.styles.scss';
6 |
7 | const HomePage = () => (
8 |
9 |
10 |
11 | );
12 |
13 | export default HomePage;
14 |
--------------------------------------------------------------------------------
/src/pages/homepage/homepage.styles.scss:
--------------------------------------------------------------------------------
1 | .homepage {
2 | display: flex;
3 | flex-direction: column;
4 | align-items: center;
5 | }
6 |
--------------------------------------------------------------------------------
/src/pages/shop/shop.component.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import SHOP_DATA from './shop.data.js';
4 |
5 | import CollectionPreview from '../../components/collection-preview/collection-preview';
6 |
7 | class ShopPage extends React.Component {
8 | constructor(props) {
9 | super(props);
10 |
11 | this.state = {
12 | collections: SHOP_DATA
13 | };
14 | }
15 |
16 | render() {
17 | const { collections } = this.state;
18 | return (
19 |
20 | {collections.map(({ id, ...otherCollectionProps }) => (
21 |
22 | ))}
23 |
24 | );
25 | }
26 | }
27 |
28 | export default ShopPage;
29 |
--------------------------------------------------------------------------------
/src/pages/shop/shop.data.js:
--------------------------------------------------------------------------------
1 | const SHOP_DATA = [
2 | {
3 | id: 1,
4 | title: 'Hats',
5 | routeName: 'hats',
6 | items: [
7 | {
8 | id: 1,
9 | name: 'Brown Brim',
10 | imageUrl: 'https://i.ibb.co/ZYW3VTp/brown-brim.png',
11 | price: 25
12 | },
13 | {
14 | id: 2,
15 | name: 'Blue Beanie',
16 | imageUrl: 'https://i.ibb.co/ypkgK0X/blue-beanie.png',
17 | price: 18
18 | },
19 | {
20 | id: 3,
21 | name: 'Brown Cowboy',
22 | imageUrl: 'https://i.ibb.co/QdJwgmp/brown-cowboy.png',
23 | price: 35
24 | },
25 | {
26 | id: 4,
27 | name: 'Grey Brim',
28 | imageUrl: 'https://i.ibb.co/RjBLWxB/grey-brim.png',
29 | price: 25
30 | },
31 | {
32 | id: 5,
33 | name: 'Green Beanie',
34 | imageUrl: 'https://i.ibb.co/YTjW3vF/green-beanie.png',
35 | price: 18
36 | },
37 | {
38 | id: 6,
39 | name: 'Palm Tree Cap',
40 | imageUrl: 'https://i.ibb.co/rKBDvJX/palm-tree-cap.png',
41 | price: 14
42 | },
43 | {
44 | id: 7,
45 | name: 'Red Beanie',
46 | imageUrl: 'https://i.ibb.co/bLB646Z/red-beanie.png',
47 | price: 18
48 | },
49 | {
50 | id: 8,
51 | name: 'Wolf Cap',
52 | imageUrl: 'https://i.ibb.co/1f2nWMM/wolf-cap.png',
53 | price: 14
54 | },
55 | {
56 | id: 9,
57 | name: 'Blue Snapback',
58 | imageUrl: 'https://i.ibb.co/X2VJP2W/blue-snapback.png',
59 | price: 16
60 | }
61 | ]
62 | },
63 | {
64 | id: 2,
65 | title: 'Sneakers',
66 | routeName: 'sneakers',
67 | items: [
68 | {
69 | id: 10,
70 | name: 'Adidas NMD',
71 | imageUrl: 'https://i.ibb.co/0s3pdnc/adidas-nmd.png',
72 | price: 220
73 | },
74 | {
75 | id: 11,
76 | name: 'Adidas Yeezy',
77 | imageUrl: 'https://i.ibb.co/dJbG1cT/yeezy.png',
78 | price: 280
79 | },
80 | {
81 | id: 12,
82 | name: 'Black Converse',
83 | imageUrl: 'https://i.ibb.co/bPmVXyP/black-converse.png',
84 | price: 110
85 | },
86 | {
87 | id: 13,
88 | name: 'Nike White AirForce',
89 | imageUrl: 'https://i.ibb.co/1RcFPk0/white-nike-high-tops.png',
90 | price: 160
91 | },
92 | {
93 | id: 14,
94 | name: 'Nike Red High Tops',
95 | imageUrl: 'https://i.ibb.co/QcvzydB/nikes-red.png',
96 | price: 160
97 | },
98 | {
99 | id: 15,
100 | name: 'Nike Brown High Tops',
101 | imageUrl: 'https://i.ibb.co/fMTV342/nike-brown.png',
102 | price: 160
103 | },
104 | {
105 | id: 16,
106 | name: 'Air Jordan Limited',
107 | imageUrl: 'https://i.ibb.co/w4k6Ws9/nike-funky.png',
108 | price: 190
109 | },
110 | {
111 | id: 17,
112 | name: 'Timberlands',
113 | imageUrl: 'https://i.ibb.co/Mhh6wBg/timberlands.png',
114 | price: 200
115 | }
116 | ]
117 | },
118 | {
119 | id: 3,
120 | title: 'Jackets',
121 | routeName: 'jackets',
122 | items: [
123 | {
124 | id: 18,
125 | name: 'Black Jean Shearling',
126 | imageUrl: 'https://i.ibb.co/XzcwL5s/black-shearling.png',
127 | price: 125
128 | },
129 | {
130 | id: 19,
131 | name: 'Blue Jean Jacket',
132 | imageUrl: 'https://i.ibb.co/mJS6vz0/blue-jean-jacket.png',
133 | price: 90
134 | },
135 | {
136 | id: 20,
137 | name: 'Grey Jean Jacket',
138 | imageUrl: 'https://i.ibb.co/N71k1ML/grey-jean-jacket.png',
139 | price: 90
140 | },
141 | {
142 | id: 21,
143 | name: 'Brown Shearling',
144 | imageUrl: 'https://i.ibb.co/s96FpdP/brown-shearling.png',
145 | price: 165
146 | },
147 | {
148 | id: 22,
149 | name: 'Tan Trench',
150 | imageUrl: 'https://i.ibb.co/M6hHc3F/brown-trench.png',
151 | price: 185
152 | }
153 | ]
154 | },
155 | {
156 | id: 4,
157 | title: 'Womens',
158 | routeName: 'womens',
159 | items: [
160 | {
161 | id: 23,
162 | name: 'Blue Tanktop',
163 | imageUrl: 'https://i.ibb.co/7CQVJNm/blue-tank.png',
164 | price: 25
165 | },
166 | {
167 | id: 24,
168 | name: 'Floral Blouse',
169 | imageUrl: 'https://i.ibb.co/4W2DGKm/floral-blouse.png',
170 | price: 20
171 | },
172 | {
173 | id: 25,
174 | name: 'Floral Dress',
175 | imageUrl: 'https://i.ibb.co/KV18Ysr/floral-skirt.png',
176 | price: 80
177 | },
178 | {
179 | id: 26,
180 | name: 'Red Dots Dress',
181 | imageUrl: 'https://i.ibb.co/N3BN1bh/red-polka-dot-dress.png',
182 | price: 80
183 | },
184 | {
185 | id: 27,
186 | name: 'Striped Sweater',
187 | imageUrl: 'https://i.ibb.co/KmSkMbH/striped-sweater.png',
188 | price: 45
189 | },
190 | {
191 | id: 28,
192 | name: 'Yellow Track Suit',
193 | imageUrl: 'https://i.ibb.co/v1cvwNf/yellow-track-suit.png',
194 | price: 135
195 | },
196 | {
197 | id: 29,
198 | name: 'White Blouse',
199 | imageUrl: 'https://i.ibb.co/qBcrsJg/white-vest.png',
200 | price: 20
201 | }
202 | ]
203 | },
204 | {
205 | id: 5,
206 | title: 'Mens',
207 | routeName: 'mens',
208 | items: [
209 | {
210 | id: 30,
211 | name: 'Camo Down Vest',
212 | imageUrl: 'https://i.ibb.co/xJS0T3Y/camo-vest.png',
213 | price: 325
214 | },
215 | {
216 | id: 31,
217 | name: 'Floral T-shirt',
218 | imageUrl: 'https://i.ibb.co/qMQ75QZ/floral-shirt.png',
219 | price: 20
220 | },
221 | {
222 | id: 32,
223 | name: 'Black & White Longsleeve',
224 | imageUrl: 'https://i.ibb.co/55z32tw/long-sleeve.png',
225 | price: 25
226 | },
227 | {
228 | id: 33,
229 | name: 'Pink T-shirt',
230 | imageUrl: 'https://i.ibb.co/RvwnBL8/pink-shirt.png',
231 | price: 25
232 | },
233 | {
234 | id: 34,
235 | name: 'Jean Long Sleeve',
236 | imageUrl: 'https://i.ibb.co/VpW4x5t/roll-up-jean-shirt.png',
237 | price: 40
238 | },
239 | {
240 | id: 35,
241 | name: 'Burgundy T-shirt',
242 | imageUrl: 'https://i.ibb.co/mh3VM1f/polka-dot-shirt.png',
243 | price: 25
244 | }
245 | ]
246 | }
247 | ];
248 |
249 | export default SHOP_DATA;
250 |
--------------------------------------------------------------------------------
/src/pages/sign-in-and-sign-up/sign-in-and-sign-up.component.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import SignIn from '../../components/sign-in/sign-in.component';
4 |
5 | import './sign-in-and-sign-up.styles.scss';
6 |
7 | const SignInAndSignUpPage = () => (
8 |
9 |
10 |
11 | );
12 |
13 | export default SignInAndSignUpPage;
14 |
--------------------------------------------------------------------------------
/src/pages/sign-in-and-sign-up/sign-in-and-sign-up.styles.scss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZhangMYihua/lesson-9/783fd72e3f6290c9c4efcd072d805f210b40efd0/src/pages/sign-in-and-sign-up/sign-in-and-sign-up.styles.scss
--------------------------------------------------------------------------------
/src/serviceWorker.js:
--------------------------------------------------------------------------------
1 | // This optional code is used to register a service worker.
2 | // register() is not called by default.
3 |
4 | // This lets the app load faster on subsequent visits in production, and gives
5 | // it offline capabilities. However, it also means that developers (and users)
6 | // will only see deployed updates on subsequent visits to a page, after all the
7 | // existing tabs open on the page have been closed, since previously cached
8 | // resources are updated in the background.
9 |
10 | // To learn more about the benefits of this model and instructions on how to
11 | // opt-in, read https://bit.ly/CRA-PWA
12 |
13 | const isLocalhost = Boolean(
14 | window.location.hostname === 'localhost' ||
15 | // [::1] is the IPv6 localhost address.
16 | window.location.hostname === '[::1]' ||
17 | // 127.0.0.1/8 is considered localhost for IPv4.
18 | window.location.hostname.match(
19 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
20 | )
21 | );
22 |
23 | export function register(config) {
24 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
25 | // The URL constructor is available in all browsers that support SW.
26 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);
27 | if (publicUrl.origin !== window.location.origin) {
28 | // Our service worker won't work if PUBLIC_URL is on a different origin
29 | // from what our page is served on. This might happen if a CDN is used to
30 | // serve assets; see https://github.com/facebook/create-react-app/issues/2374
31 | return;
32 | }
33 |
34 | window.addEventListener('load', () => {
35 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
36 |
37 | if (isLocalhost) {
38 | // This is running on localhost. Let's check if a service worker still exists or not.
39 | checkValidServiceWorker(swUrl, config);
40 |
41 | // Add some additional logging to localhost, pointing developers to the
42 | // service worker/PWA documentation.
43 | navigator.serviceWorker.ready.then(() => {
44 | console.log(
45 | 'This web app is being served cache-first by a service ' +
46 | 'worker. To learn more, visit https://bit.ly/CRA-PWA'
47 | );
48 | });
49 | } else {
50 | // Is not localhost. Just register service worker
51 | registerValidSW(swUrl, config);
52 | }
53 | });
54 | }
55 | }
56 |
57 | function registerValidSW(swUrl, config) {
58 | navigator.serviceWorker
59 | .register(swUrl)
60 | .then(registration => {
61 | registration.onupdatefound = () => {
62 | const installingWorker = registration.installing;
63 | if (installingWorker == null) {
64 | return;
65 | }
66 | installingWorker.onstatechange = () => {
67 | if (installingWorker.state === 'installed') {
68 | if (navigator.serviceWorker.controller) {
69 | // At this point, the updated precached content has been fetched,
70 | // but the previous service worker will still serve the older
71 | // content until all client tabs are closed.
72 | console.log(
73 | 'New content is available and will be used when all ' +
74 | 'tabs for this page are closed. See https://bit.ly/CRA-PWA.'
75 | );
76 |
77 | // Execute callback
78 | if (config && config.onUpdate) {
79 | config.onUpdate(registration);
80 | }
81 | } else {
82 | // At this point, everything has been precached.
83 | // It's the perfect time to display a
84 | // "Content is cached for offline use." message.
85 | console.log('Content is cached for offline use.');
86 |
87 | // Execute callback
88 | if (config && config.onSuccess) {
89 | config.onSuccess(registration);
90 | }
91 | }
92 | }
93 | };
94 | };
95 | })
96 | .catch(error => {
97 | console.error('Error during service worker registration:', error);
98 | });
99 | }
100 |
101 | function checkValidServiceWorker(swUrl, config) {
102 | // Check if the service worker can be found. If it can't reload the page.
103 | fetch(swUrl)
104 | .then(response => {
105 | // Ensure service worker exists, and that we really are getting a JS file.
106 | const contentType = response.headers.get('content-type');
107 | if (
108 | response.status === 404 ||
109 | (contentType != null && contentType.indexOf('javascript') === -1)
110 | ) {
111 | // No service worker found. Probably a different app. Reload the page.
112 | navigator.serviceWorker.ready.then(registration => {
113 | registration.unregister().then(() => {
114 | window.location.reload();
115 | });
116 | });
117 | } else {
118 | // Service worker found. Proceed as normal.
119 | registerValidSW(swUrl, config);
120 | }
121 | })
122 | .catch(() => {
123 | console.log(
124 | 'No internet connection found. App is running in offline mode.'
125 | );
126 | });
127 | }
128 |
129 | export function unregister() {
130 | if ('serviceWorker' in navigator) {
131 | navigator.serviceWorker.ready.then(registration => {
132 | registration.unregister();
133 | });
134 | }
135 | }
136 |
--------------------------------------------------------------------------------