├── src
├── services
│ └── api.js
├── index.js
├── routes
│ └── index.jsx
├── App.js
├── pages
│ └── Dashboard
│ │ ├── styles.js
│ │ └── index.jsx
├── styles
│ └── global.js
├── components
│ ├── Header
│ │ ├── index.jsx
│ │ └── styles.js
│ ├── ModalAddFood
│ │ ├── styles.js
│ │ └── index.jsx
│ ├── ModalEditFood
│ │ ├── styles.js
│ │ └── index.jsx
│ ├── Input
│ │ ├── styles.js
│ │ └── index.jsx
│ ├── Modal
│ │ └── index.jsx
│ └── Food
│ │ ├── index.jsx
│ │ └── styles.js
└── assets
│ └── logo.svg
├── .gitignore
├── public
└── index.html
├── server.json
├── README.md
└── package.json
/src/services/api.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 |
3 | const api = axios.create({
4 | baseURL: 'http://localhost:3333',
5 | });
6 |
7 | export default api;
8 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | ReactDOM.render(
6 |
7 |
8 | ,
9 | document.getElementById('root')
10 | );
11 |
--------------------------------------------------------------------------------
/src/routes/index.jsx:
--------------------------------------------------------------------------------
1 | import { Switch, Route } from 'react-router-dom';
2 |
3 | import Dashboard from '../pages/Dashboard';
4 |
5 | const Routes = () => (
6 |
7 |
8 |
9 | );
10 |
11 | export default Routes;
12 |
--------------------------------------------------------------------------------
/src/App.js:
--------------------------------------------------------------------------------
1 | import { BrowserRouter as Router } from 'react-router-dom';
2 |
3 | import Routes from './routes';
4 |
5 | import GlobalStyle from './styles/global';
6 |
7 | const App = () => (
8 | <>
9 |
10 |
11 |
12 |
13 | >
14 | );
15 |
16 | export default App;
17 |
--------------------------------------------------------------------------------
/src/pages/Dashboard/styles.js:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components';
2 |
3 | export const FoodsContainer = styled.div`
4 | width: 100%;
5 | max-width: 1280px;
6 | margin: 0 auto;
7 | padding: 40px 0;
8 | margin-top: -140px;
9 |
10 | display: grid;
11 |
12 | grid-template-columns: repeat(3, 1fr);
13 | grid-gap: 32px;
14 | `;
15 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/src/styles/global.js:
--------------------------------------------------------------------------------
1 | import { createGlobalStyle } from 'styled-components';
2 |
3 | export default createGlobalStyle`
4 | * {
5 | margin: 0;
6 | padding: 0;
7 | box-sizing: border-box;
8 | outline: 0;
9 | }
10 |
11 | body {
12 | background: #fff;
13 | color: #FFF;
14 | -webkit-font-smoothing: antialiased;
15 | }
16 |
17 | body, input, button {
18 | font-family: 'Poppins', sans-serif;
19 | font-size: 16px;
20 | }
21 |
22 | h1, h2, h3, h4, h5, h6, strong {
23 | font-weight: 500;
24 | }
25 |
26 | button {
27 | cursor: pointer;
28 | }
29 | `;
30 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | GoRestaurant
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/src/components/Header/index.jsx:
--------------------------------------------------------------------------------
1 | import { Component } from 'react';
2 | import { FiPlusSquare } from 'react-icons/fi';
3 |
4 | import { Container } from './styles';
5 | import Logo from '../../assets/logo.svg';
6 |
7 | class Header extends Component {
8 | render() {
9 | const { openModal } = this.props;
10 |
11 | return (
12 |
13 |
14 |
15 |
28 |
29 |
30 | )
31 | }
32 | };
33 |
34 | export default Header;
35 |
--------------------------------------------------------------------------------
/src/components/ModalAddFood/styles.js:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components';
2 | import { Form as Unform } from '@unform/web';
3 |
4 | export const Form = styled(Unform)`
5 | padding: 48px 40px;
6 | display: flex;
7 | flex-direction: column;
8 |
9 | h1 {
10 | font-weight: 600;
11 | font-size: 36px;
12 | line-height: 36px;
13 | margin-bottom: 40px;
14 | }
15 |
16 | button {
17 | margin-top: 48px;
18 | align-self: flex-end;
19 | }
20 |
21 | button {
22 | font-weight: 600;
23 | border-radius: 8px;
24 | border: 0;
25 | background: #39b100;
26 | color: #fff;
27 |
28 | display: flex;
29 | flex-direction: row;
30 | align-items: center;
31 |
32 | .text {
33 | padding: 16px 24px;
34 | }
35 |
36 | .icon {
37 | display: flex;
38 | padding: 16px 16px;
39 | background: #41c900;
40 | border-radius: 0 8px 8px 0;
41 | margin: 0 auto;
42 | }
43 | }
44 | `;
45 |
--------------------------------------------------------------------------------
/src/components/ModalEditFood/styles.js:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components';
2 | import { Form as Unform } from '@unform/web';
3 |
4 | export const Form = styled(Unform)`
5 | padding: 48px 40px;
6 | display: flex;
7 | flex-direction: column;
8 |
9 | h1 {
10 | font-weight: 600;
11 | font-size: 36px;
12 | line-height: 36px;
13 | margin-bottom: 40px;
14 | }
15 |
16 | button {
17 | margin-top: 48px;
18 | align-self: flex-end;
19 | }
20 |
21 | button {
22 | font-weight: 600;
23 | border-radius: 8px;
24 | border: 0;
25 | background: #39b100;
26 | color: #fff;
27 |
28 | display: flex;
29 | flex-direction: row;
30 | align-items: center;
31 |
32 | .text {
33 | padding: 16px 24px;
34 | }
35 |
36 | .icon {
37 | display: flex;
38 | padding: 16px 16px;
39 | background: #41c900;
40 | border-radius: 0 8px 8px 0;
41 | margin: 0 auto;
42 | }
43 | }
44 | `;
45 |
--------------------------------------------------------------------------------
/src/components/Input/styles.js:
--------------------------------------------------------------------------------
1 | import styled, { css } from 'styled-components';
2 |
3 | export const Container = styled.div`
4 | display: flex;
5 | align-items: center;
6 |
7 | background: #fff;
8 | border-radius: 8px;
9 | padding: 18px 24px;
10 | width: 100%;
11 | font-size: 16px;
12 |
13 | & + div {
14 | margin-top: 24px;
15 | }
16 |
17 | h1 {
18 | margin-bottom: 40px;
19 | font-weight: 600;
20 | font-size: 36px;
21 | line-height: 36px;
22 | }
23 |
24 | ${props =>
25 | props.isFocused &&
26 | css`
27 | color: #ff9000;
28 | border-color: #ff9000;
29 | `}
30 |
31 | ${props =>
32 | props.isFilled &&
33 | css`
34 | color: #ff9000;
35 | `}
36 |
37 | input {
38 | flex: 1;
39 | background: transparent;
40 | border: 0;
41 | color: #b7b7cc;
42 |
43 | &::placeholder {
44 | color: #b7b7cc;
45 | }
46 | }
47 |
48 | svg {
49 | margin-right: 16px;
50 | }
51 | `;
52 |
--------------------------------------------------------------------------------
/server.json:
--------------------------------------------------------------------------------
1 | {
2 | "foods": [
3 | {
4 | "id": 1,
5 | "name": "Ao molho",
6 | "description": "Macarrão ao molho branco, fughi e cheiro verde das montanhas",
7 | "price": "19.90",
8 | "available": true,
9 | "image": "https://storage.googleapis.com/golden-wind/bootcamp-gostack/desafio-food/food1.png"
10 | },
11 | {
12 | "id": 2,
13 | "name": "Veggie",
14 | "description": "Macarrão com pimentão, ervilha e ervas finas colhidas no himalaia.",
15 | "price": "21.90",
16 | "available": true,
17 | "image": "https://storage.googleapis.com/golden-wind/bootcamp-gostack/desafio-food/food2.png"
18 | },
19 | {
20 | "id": 3,
21 | "name": "A la Camarón",
22 | "description": "Macarrão com vegetais de primeira linha e camarão dos 7 mares.",
23 | "price": "25.90",
24 | "available": false,
25 | "image": "https://storage.googleapis.com/golden-wind/bootcamp-gostack/desafio-food/food3.png"
26 | }
27 | ]
28 | }
--------------------------------------------------------------------------------
/src/components/Header/styles.js:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components';
2 |
3 | export const Container = styled.div`
4 | background: #c72828;
5 | padding: 30px 0;
6 |
7 | header {
8 | width: 1280px;
9 | margin: 0 auto;
10 | padding: 0 0 160px;
11 | display: flex;
12 | align-items: center;
13 | justify-content: space-between;
14 |
15 | nav {
16 | div {
17 | button {
18 | font-weight: 600;
19 | border-radius: 8px;
20 | border: 0;
21 | background: #39b100;
22 | color: #fff;
23 |
24 | display: flex;
25 | flex-direction: row;
26 | align-items: center;
27 |
28 | .text {
29 | padding: 16px 24px;
30 | }
31 |
32 | .icon {
33 | display: flex;
34 | padding: 16px 16px;
35 | background: #41c900;
36 | border-radius: 0 8px 8px 0;
37 | margin: 0 auto;
38 | }
39 | }
40 | }
41 | }
42 | }
43 | `;
44 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | ## 💻 Projeto
12 |
13 | ignite-template-reactjs-refactoring-classes-ts
14 |
15 | ## 📝 Licença
16 |
17 | Esse projeto está sob a licença MIT. Veja o arquivo [LICENSE](LICENSE) para mais detalhes.
18 |
19 | ---
20 |
21 |
22 | Feito com 💜 by Rocketseat
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "refactoring-classes-ts",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.11.4",
7 | "@testing-library/react": "^11.1.0",
8 | "@testing-library/user-event": "^12.1.10",
9 | "@unform/core": "^2.1.6",
10 | "@unform/web": "^2.1.6",
11 | "axios": "^0.21.1",
12 | "react": "^17.0.1",
13 | "react-dom": "^17.0.1",
14 | "react-icons": "^4.2.0",
15 | "react-modal": "^3.12.1",
16 | "react-router-dom": "^5.2.0",
17 | "react-scripts": "4.0.3",
18 | "styled-components": "^5.2.1",
19 | "yup": "^0.32.9"
20 | },
21 | "scripts": {
22 | "start": "react-scripts start",
23 | "build": "react-scripts build",
24 | "test": "react-scripts test",
25 | "eject": "react-scripts eject",
26 | "server": "json-server server.json -p 3333"
27 | },
28 | "eslintConfig": {
29 | "extends": [
30 | "react-app",
31 | "react-app/jest"
32 | ]
33 | },
34 | "browserslist": {
35 | "production": [
36 | ">0.2%",
37 | "not dead",
38 | "not op_mini all"
39 | ],
40 | "development": [
41 | "last 1 chrome version",
42 | "last 1 firefox version",
43 | "last 1 safari version"
44 | ]
45 | },
46 | "devDependencies": {
47 | "json-server": "^0.16.3"
48 | }
49 | }
--------------------------------------------------------------------------------
/src/components/Input/index.jsx:
--------------------------------------------------------------------------------
1 | import {
2 | useEffect,
3 | useRef,
4 | useState,
5 | useCallback,
6 | } from 'react';
7 |
8 | import { useField } from '@unform/core';
9 |
10 | import { Container } from './styles';
11 |
12 | const Input = ({ name, icon: Icon, ...rest }) => {
13 | const inputRef = useRef(null);
14 |
15 | const [isFocused, setIsFocused] = useState(false);
16 | const [isFilled, setIsFilled] = useState(false);
17 |
18 | const { fieldName, defaultValue, registerField } = useField(name);
19 |
20 | const handleInputFocus = useCallback(() => {
21 | setIsFocused(true);
22 | }, []);
23 |
24 | const handleInputBlur = useCallback(() => {
25 | setIsFocused(false);
26 |
27 | setIsFilled(!!inputRef.current?.value);
28 | }, []);
29 |
30 | useEffect(() => {
31 | registerField({
32 | name: fieldName,
33 | ref: inputRef.current,
34 | path: 'value',
35 | });
36 | }, [fieldName, registerField]);
37 |
38 | return (
39 |
40 | {Icon && }
41 |
42 |
49 |
50 | );
51 | };
52 |
53 | export default Input;
54 |
--------------------------------------------------------------------------------
/src/components/ModalAddFood/index.jsx:
--------------------------------------------------------------------------------
1 | import { Component, createRef } from 'react';
2 | import { FiCheckSquare } from 'react-icons/fi';
3 |
4 | import { Form } from './styles';
5 | import Modal from '../Modal';
6 | import Input from '../Input';
7 |
8 | class ModalAddFood extends Component {
9 | constructor(props) {
10 | super(props);
11 |
12 | this.formRef = createRef();
13 | }
14 |
15 | handleSubmit = async data => {
16 | const { setIsOpen, handleAddFood } = this.props;
17 |
18 | handleAddFood(data);
19 | setIsOpen();
20 | };
21 |
22 | render() {
23 | const { isOpen, setIsOpen } = this.props;
24 |
25 | return (
26 |
27 |
42 |
43 | );
44 | }
45 | };
46 |
47 | export default ModalAddFood;
48 |
--------------------------------------------------------------------------------
/src/components/ModalEditFood/index.jsx:
--------------------------------------------------------------------------------
1 | import { Component, createRef } from 'react';
2 | import { FiCheckSquare } from 'react-icons/fi';
3 |
4 | import { Form } from './styles';
5 | import Modal from '../Modal';
6 | import Input from '../Input';
7 |
8 | class ModalEditFood extends Component {
9 | constructor(props) {
10 | super(props);
11 |
12 | this.formRef = createRef()
13 | }
14 |
15 | handleSubmit = async (data) => {
16 | const { setIsOpen, handleUpdateFood } = this.props;
17 |
18 | handleUpdateFood(data);
19 | setIsOpen();
20 | };
21 |
22 | render() {
23 | const { isOpen, setIsOpen, editingFood } = this.props;
24 |
25 | return (
26 |
27 |
43 |
44 | );
45 | }
46 | };
47 |
48 | export default ModalEditFood;
49 |
--------------------------------------------------------------------------------
/src/components/Modal/index.jsx:
--------------------------------------------------------------------------------
1 | import { Component } from 'react';
2 | import ReactModal from 'react-modal';
3 |
4 | class Modal extends Component {
5 | constructor(props) {
6 | super(props);
7 |
8 | const { isOpen } = this.props;
9 | this.state = {
10 | modalStatus: isOpen
11 | }
12 | }
13 |
14 | componentDidUpdate(prevProps) {
15 | const { isOpen } = this.props;
16 |
17 | if (prevProps.isOpen !== isOpen) {
18 | console.log(this.props)
19 | this.setState({ modalStatus: isOpen })
20 | }
21 | }
22 |
23 | render() {
24 | const { children, setIsOpen } = this.props;
25 | const { modalStatus } = this.state;
26 |
27 | return (
28 |
52 | {children}
53 |
54 | );
55 | }
56 | };
57 |
58 | export default Modal;
59 |
--------------------------------------------------------------------------------
/src/components/Food/index.jsx:
--------------------------------------------------------------------------------
1 | import { Component } from 'react';
2 | import { FiEdit3, FiTrash } from 'react-icons/fi';
3 |
4 | import { Container } from './styles';
5 | import api from '../../services/api';
6 |
7 | class Food extends Component {
8 | constructor(props) {
9 | super(props);
10 |
11 | const { available } = this.props.food;
12 | this.state = {
13 | isAvailable: available
14 | };
15 | }
16 |
17 | toggleAvailable = async () => {
18 | const { food } = this.props;
19 | const { isAvailable } = this.state;
20 |
21 | await api.put(`/foods/${food.id}`, {
22 | ...food,
23 | available: !isAvailable,
24 | });
25 |
26 | this.setState({ isAvailable: !isAvailable });
27 | }
28 |
29 | setEditingFood = () => {
30 | const { food, handleEditFood } = this.props;
31 |
32 | handleEditFood(food);
33 | }
34 |
35 | render() {
36 | const { isAvailable } = this.state;
37 | const { food, handleDelete } = this.props;
38 |
39 | return (
40 |
41 |
42 |
43 |
44 |
45 | {food.name}
46 | {food.description}
47 |
48 | R$ {food.price}
49 |
50 |
51 |
52 |
53 |
61 |
62 |
70 |
71 |
72 |
73 |
{isAvailable ? 'Disponível' : 'Indisponível'}
74 |
75 |
85 |
86 |
87 |
88 | );
89 | }
90 | };
91 |
92 | export default Food;
93 |
--------------------------------------------------------------------------------
/src/components/Food/styles.js:
--------------------------------------------------------------------------------
1 | import styled, { css } from 'styled-components';
2 |
3 | export const Container = styled.div`
4 | background: #f0f0f5;
5 | border-radius: 8px;
6 |
7 | header {
8 | background: #ffb84d;
9 | border-radius: 8px 8px 0px 0px;
10 | height: 192px;
11 | overflow: hidden;
12 | transition: 0.3s opacity;
13 | text-align: center;
14 |
15 | ${props =>
16 | !props.available &&
17 | css`
18 | opacity: 0.3;
19 | `};
20 |
21 | img {
22 | pointer-events: none;
23 | user-select: none;
24 | }
25 | }
26 |
27 | section.body {
28 | padding: 30px;
29 |
30 | h2 {
31 | color: #3d3d4d;
32 | }
33 |
34 | p {
35 | color: #3d3d4d;
36 |
37 | margin-top: 16px;
38 | }
39 |
40 | .price {
41 | font-style: normal;
42 | font-size: 24px;
43 | line-height: 34px;
44 | color: #39b100;
45 |
46 | b {
47 | font-weight: 600;
48 | }
49 | }
50 | }
51 |
52 | section.footer {
53 | display: flex;
54 | justify-content: space-between;
55 | align-items: center;
56 |
57 | padding: 20px 30px;
58 | background: #e4e4eb;
59 | border-radius: 0px 0px 8px 8px;
60 |
61 | div.icon-container {
62 | display: flex;
63 |
64 | button {
65 | background: #fff;
66 | padding: 10px;
67 | border-radius: 8px;
68 | display: flex;
69 | border: none;
70 | transition: 0.1s;
71 |
72 | svg {
73 | color: #3d3d4d;
74 | }
75 |
76 | & + button {
77 | margin-left: 6px;
78 | }
79 | }
80 | }
81 |
82 | div.availability-container {
83 | display: flex;
84 | align-items: center;
85 |
86 | p {
87 | color: #3d3d4d;
88 | }
89 |
90 | .switch {
91 | position: relative;
92 | display: inline-block;
93 | width: 88px;
94 | height: 32px;
95 | margin-left: 12px;
96 |
97 | & input {
98 | opacity: 0;
99 | width: 0;
100 | height: 0;
101 | }
102 |
103 | .slider {
104 | position: absolute;
105 | cursor: pointer;
106 | top: 0;
107 | left: 0;
108 | right: 0;
109 | bottom: 0;
110 | background-color: #c72828;
111 | -webkit-transition: 0.4s;
112 | transition: 0.4s;
113 | border-radius: 16px;
114 |
115 | &:before {
116 | position: absolute;
117 | content: '';
118 | height: 20px;
119 | width: 40px;
120 | left: 8px;
121 | bottom: 6px;
122 | background-color: white;
123 | -webkit-transition: 0.4s;
124 | transition: 0.4s;
125 | border-radius: 10px;
126 | }
127 | }
128 |
129 | input:checked + .slider {
130 | background-color: #39b100;
131 | }
132 |
133 | input:focus + .slider {
134 | box-shadow: 0 0 1px #2196f3;
135 | }
136 |
137 | input:checked + .slider:before {
138 | -webkit-transform: translateX(32px);
139 | -ms-transform: translateX(32px);
140 | transform: translateX(32px);
141 | }
142 | }
143 | }
144 | }
145 | `;
146 |
--------------------------------------------------------------------------------
/src/pages/Dashboard/index.jsx:
--------------------------------------------------------------------------------
1 | import { Component } from 'react';
2 |
3 | import Header from '../../components/Header';
4 | import api from '../../services/api';
5 | import Food from '../../components/Food';
6 | import ModalAddFood from '../../components/ModalAddFood';
7 | import ModalEditFood from '../../components/ModalEditFood';
8 | import { FoodsContainer } from './styles';
9 |
10 | class Dashboard extends Component {
11 | constructor(props) {
12 | super(props);
13 | this.state = {
14 | foods: [],
15 | editingFood: {},
16 | modalOpen: false,
17 | editModalOpen: false,
18 | }
19 | }
20 |
21 | async componentDidMount() {
22 | const response = await api.get('/foods');
23 |
24 | this.setState({ foods: response.data });
25 | }
26 |
27 | handleAddFood = async food => {
28 | const { foods } = this.state;
29 |
30 | try {
31 | const response = await api.post('/foods', {
32 | ...food,
33 | available: true,
34 | });
35 |
36 | this.setState({ foods: [...foods, response.data] });
37 | } catch (err) {
38 | console.log(err);
39 | }
40 | }
41 |
42 | handleUpdateFood = async food => {
43 | const { foods, editingFood } = this.state;
44 |
45 | try {
46 | const foodUpdated = await api.put(
47 | `/foods/${editingFood.id}`,
48 | { ...editingFood, ...food },
49 | );
50 |
51 | const foodsUpdated = foods.map(f =>
52 | f.id !== foodUpdated.data.id ? f : foodUpdated.data,
53 | );
54 |
55 | this.setState({ foods: foodsUpdated });
56 | } catch (err) {
57 | console.log(err);
58 | }
59 | }
60 |
61 | handleDeleteFood = async id => {
62 | const { foods } = this.state;
63 |
64 | await api.delete(`/foods/${id}`);
65 |
66 | const foodsFiltered = foods.filter(food => food.id !== id);
67 |
68 | this.setState({ foods: foodsFiltered });
69 | }
70 |
71 | toggleModal = () => {
72 | const { modalOpen } = this.state;
73 |
74 | this.setState({ modalOpen: !modalOpen });
75 | }
76 |
77 | toggleEditModal = () => {
78 | const { editModalOpen } = this.state;
79 |
80 | this.setState({ editModalOpen: !editModalOpen });
81 | }
82 |
83 | handleEditFood = food => {
84 | this.setState({ editingFood: food, editModalOpen: true });
85 | }
86 |
87 | render() {
88 | const { modalOpen, editModalOpen, editingFood, foods } = this.state;
89 |
90 | return (
91 | <>
92 |
93 |
98 |
104 |
105 |
106 | {foods &&
107 | foods.map(food => (
108 |
114 | ))}
115 |
116 | >
117 | );
118 | }
119 | };
120 |
121 | export default Dashboard;
122 |
--------------------------------------------------------------------------------
/src/assets/logo.svg:
--------------------------------------------------------------------------------
1 |
35 |
--------------------------------------------------------------------------------