diff --git a/.gitignore b/.gitignore
index 315cb60..ac07e0b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,6 +13,7 @@
# misc
.DS_Store
+.env
.env.local
.env.development.local
.env.test.local
diff --git a/package.json b/package.json
index 8d3b02c..4c2f9ab 100644
--- a/package.json
+++ b/package.json
@@ -9,6 +9,7 @@
"@testing-library/user-event": "^12.1.10",
"add": "^2.0.6",
"aos": "^3.0.0-beta.6",
+ "axios": "^0.23.0",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-multi-carousel": "^2.6.5",
diff --git a/public/manifest.json b/public/manifest.json
index 468bae1..080d6c7 100644
--- a/public/manifest.json
+++ b/public/manifest.json
@@ -1,25 +1,25 @@
{
- 'short_name': 'React App',
- 'name': 'Create React App Sample',
- 'icons': [
+ "short_name": "React App",
+ "name": "Create React App Sample",
+ "icons": [
{
- 'src': 'favicon.ico',
- 'sizes': '64x64 32x32 24x24 16x16',
- 'type': 'image/x-icon'
+ "src": "favicon.ico",
+ "sizes": "64x64 32x32 24x24 16x16",
+ "type": "image/x-icon"
},
{
- 'src': 'logo192.png',
- 'type': 'image/png',
- 'sizes': '192x192'
+ "src": "logo192.png",
+ "type": "image/png",
+ "sizes": "192x192"
},
{
- 'src': 'logo512.png',
- 'type': 'image/png',
- 'sizes': '512x512'
+ "src": "logo512.png",
+ "type": "image/png",
+ "sizes": "512x512"
}
],
- 'start_url': '.',
- 'display': 'standalone',
- 'theme_color': '#000000',
- 'background_color': '#ffffff'
+ "start_url": ".",
+ "display": "standalone",
+ "theme_color": "#000000",
+ "background_color": "#ffffff"
}
diff --git a/src/App.js b/src/App.js
index 9db09df..4504864 100644
--- a/src/App.js
+++ b/src/App.js
@@ -1,4 +1,4 @@
-import React, { useEffect } from 'react';
+import React from 'react';
import Home from './container';
diff --git a/src/assets/form/Frame.svg b/src/assets/form/Frame.svg
new file mode 100644
index 0000000..5e1c91a
--- /dev/null
+++ b/src/assets/form/Frame.svg
@@ -0,0 +1,779 @@
+
diff --git a/src/assets/form/bg.png b/src/assets/form/bg.png
new file mode 100644
index 0000000..de4478e
Binary files /dev/null and b/src/assets/form/bg.png differ
diff --git a/src/config/index.js b/src/config/index.js
new file mode 100644
index 0000000..9f849bc
--- /dev/null
+++ b/src/config/index.js
@@ -0,0 +1,9 @@
+const publicRuntimeConfig = {
+ NODE_ENV: process.env.NODE_ENV || 'production',
+ API_URL: process.env.REACT_APP_API_URL,
+ LOCAL_STORAGE_TOKEN_NAME: 'token',
+};
+
+export const { NODE_ENV, API_URL, LOCAL_STORAGE_TOKEN_NAME } = publicRuntimeConfig;
+
+export default publicRuntimeConfig.NODE_ENV;
diff --git a/src/container/FAQ/Question/question.style.js b/src/container/FAQ/Question/question.style.js
index eb06455..a8ed338 100644
--- a/src/container/FAQ/Question/question.style.js
+++ b/src/container/FAQ/Question/question.style.js
@@ -1,3 +1,5 @@
+import styled from 'styled-components';
+
const StyledContainer = styled.div`
display: flex;
flex-direction: column;
diff --git a/src/container/Header/NavBar/index.js b/src/container/Header/NavBar/index.js
index 7cb11d6..a4221fd 100644
--- a/src/container/Header/NavBar/index.js
+++ b/src/container/Header/NavBar/index.js
@@ -13,13 +13,15 @@ const NavBar = () => {
Hoạt động
-
+
Timeline
FAQ
- ĐĂNG KÍ
+
+ ĐĂNG KÍ
+
);
};
diff --git a/src/container/activities/index.js b/src/container/activities/index.js
index 107ced7..3dfcf55 100644
--- a/src/container/activities/index.js
+++ b/src/container/activities/index.js
@@ -77,7 +77,9 @@ const Activities = () => {
Đến với FCode các bạn sẽ được tham gia rất nhiều hoạt động bổ ích,
cũng như trau dồi thêm được nhiều kinh nghiệp quý giá
- ĐĂNG KÍ
+
+ ĐĂNG KÍ
+
diff --git a/src/container/activities/style.js b/src/container/activities/style.js
index 2cf0c52..c61ced7 100644
--- a/src/container/activities/style.js
+++ b/src/container/activities/style.js
@@ -60,15 +60,14 @@ export const Detail = styled.p`
export const RegisButton = styled.button`
width: 98px;
height: 44px;
- left: calc(50% - 98px / 2);
- top: 0px;
+ transition: all 0.3s;
background: #00db96;
border-radius: 4px;
border: none;
color: white;
cursor: pointer;
- transition: all 0.3s;
- box-shadow: rgba(99, 99, 99, 0.2) 0px 2px 8px 0px;
+ box-shadow: 0px 4px 5px rgba(0, 0, 0, 0.14), 0px 1px 10px rgba(0, 0, 0, 0.12),
+ 0px 2px 4px rgba(0, 0, 0, 0.2);
&:hover {
filter: brightness(95%);
}
@@ -239,6 +238,9 @@ export const TBHead = styled.h2`
@media only screen and (max-width: 1240px) {
font-size: 5vw;
}
+ @media only screen and (min-width: 768px) {
+ margin-bottom: 30px;
+ }
`;
export const TBDetail = styled.p`
diff --git a/src/container/form/index.js b/src/container/form/index.js
new file mode 100644
index 0000000..65a5696
--- /dev/null
+++ b/src/container/form/index.js
@@ -0,0 +1,218 @@
+import React, { useRef, useState } from 'react';
+
+import { useHistory } from 'react-router-dom';
+
+import background from '../../assets/form/Frame.svg';
+import { put } from '../../utils/Apicaller';
+import Popup from './popup';
+import {
+ SectionWrapper,
+ FormContainer,
+ FormContent,
+ ImageContainer,
+ FormImage,
+ FormTitle,
+ FormHead,
+ FormDetail,
+ FormWrap,
+ Label,
+ NameInput,
+ InputSmall,
+ InputSmallLeft,
+ OptionContainer,
+ Select,
+ Option,
+ SmallSelect,
+ FormLineWrap,
+ Input,
+ CheckBox,
+ SubmitButton,
+} from './style.js';
+
+const initialFormData = Object.freeze({
+ lname: '',
+ fname: '',
+ spec: 'Kĩ thuật phần mềm',
+ id: '',
+ sem: 'LUK1',
+ phone: '',
+ confirm: false,
+});
+
+const Form = () => {
+ const [popupSpec, setPopupSpec] = useState({ isShowing: false, type: '' });
+ const [submit, setSubmit] = useState(initialFormData);
+ const history = useHistory();
+ const svg = useRef();
+ const handleChange = (e) => {
+ setSubmit({
+ ...submit,
+ [e.target.name]:
+ e.target.type === 'checkbox' ? e.target.checked : e.target.value.trim(),
+ });
+ };
+ const handleSubmit = async (e) => {
+ e.preventDefault();
+ if (submit.lname === '' || submit.id === '' || submit.fname === '' || submit.phone === '') {
+ setPopupSpec({ isShowing: true, type: 'missing' });
+ } else if (!submit.confirm) {
+ setPopupSpec({ isShowing: true, type: 'notConfirmed' });
+ } else {
+ setPopupSpec({ isShowing: true, type: 'success' });
+ await put('/api/students', {
+ student: submit,
+ });
+ }
+ };
+
+ const animate = () => {
+ const img = svg.current.contentDocument;
+ let paths = img.querySelectorAll('path');
+ [...paths].forEach((item) => {
+ item.style.strokeDasharray = item.getTotalLength();
+ item.style.fillOpacity = 0;
+ item.style.strokeWidth = '0.5px';
+ item.animate(
+ [
+ {
+ strokeDashoffset: item.getTotalLength(),
+ stroke: '#333',
+ fillOpacity: 0,
+ },
+ {
+ strokeDashoffset: 0,
+ stroke: '#333',
+ fillOpacity: 0,
+ },
+ {
+ stroke: 'white',
+ fillOpacity: '0.5',
+ },
+ {
+ stroke: 'white',
+ fillOpacity: '1',
+ },
+ ],
+ {
+ duration: 4000,
+ fill: 'forwards',
+ easing: 'linear',
+ }
+ );
+ });
+ };
+ return (
+
+ {popupSpec.isShowing ? (
+ setPopupSpec({ isShowing: false, type: '' })}
+ redirect={() => history.push('/')}
+ />
+ ) : null}
+
+
+
+ Đăng kí
+
+ Bạn điền đầy đủ các thông tin dưới đây để nhận thử thách sắp tới từ CLB
+ nhé!
+
+
+
+
+
+ handleChange(e)}
+ >
+ handleChange(e)}
+ >
+
+
+
+
+
+
+ handleChange(e)}
+ >
+
+
+
+ handleChange(e)}>
+
+
+
+
+
+
+
+
+
+
+
+ handleChange(e)}
+ >
+ handleChange(e)}
+ >
+
+
+ handleSubmit(e)}>ĐẮNG KÍ
+
+
+
+
+
+
+ );
+};
+
+export default Form;
diff --git a/src/container/form/popup/index.js b/src/container/form/popup/index.js
new file mode 100644
index 0000000..86d86a0
--- /dev/null
+++ b/src/container/form/popup/index.js
@@ -0,0 +1,60 @@
+import React from 'react';
+
+import { Box, Content, Title, Head, Detail, CloseButton, Overlay, PopupOverlay } from './style.js';
+
+const Missing = (props) => {
+ return (
+
+
+ Đăng kí thất bại
+ Bạn chưa điền đầy đủ thông tin
+
+ ĐÃ HIỂU
+
+ );
+};
+const NotConfirmed = (props) => {
+ return (
+
+
+ Đăng kí thất bại
+ Bạn chưa đồng ý tham gia thử thách
+
+ ĐÃ HIỂU
+
+ );
+};
+const Success = (props) => {
+ return (
+
+
+ Đăng kí thành công
+ Bạn hãy chờ thông báo tiếp theo từ CLB nhé
+
+ ĐÃ HIỂU
+
+ );
+};
+
+const Popup = (props) => {
+ const renderSwitch = (param) => {
+ switch (param) {
+ case 'missing':
+ return ;
+ case 'notConfirmed':
+ return ;
+ case 'success':
+ return ;
+ default:
+ return null;
+ }
+ };
+ return (
+
+
+ {renderSwitch(props.type)}
+
+ );
+};
+
+export default Popup;
diff --git a/src/container/form/popup/style.js b/src/container/form/popup/style.js
new file mode 100644
index 0000000..0c2ca49
--- /dev/null
+++ b/src/container/form/popup/style.js
@@ -0,0 +1,78 @@
+import styled from 'styled-components';
+
+export const PopupOverlay = styled.div`
+ position: fixed;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ width: 100%;
+ height: 100%;
+ overflow: hidden;
+ z-index: 99;
+`;
+export const Box = styled.div`
+ width: 450px;
+ height: 200px;
+ background: #ffffff;
+ filter: drop-shadow(0px 2px 4px rgba(0, 0, 0, 0.14))
+ drop-shadow(0px 3px 4px rgba(0, 0, 0, 0.12)) drop-shadow(0px 1px 5px rgba(0, 0, 0, 0.2));
+
+ border-radius: 12px;
+ left: 50%;
+ top: 50%;
+ margin: auto;
+`;
+
+export const Content = styled.div`
+ align-items: center;
+ text-align: center;
+ padding-top: 20px;
+`;
+
+export const Title = styled.div`
+ margin-bottom: 25px;
+`;
+
+export const Head = styled.h2`
+ font-family: Roboto;
+ font-style: normal;
+ font-weight: 500;
+ font-size: 36px;
+ color: #00d17d;
+ margin-bottom: 15px;
+`;
+
+export const Detail = styled.p`
+ font-family: Roboto;
+ font-style: normal;
+ font-weight: normal;
+ font-size: 18px;
+ color: rgba(0, 0, 0, 0.6);
+`;
+
+export const CloseButton = styled.button`
+ width: 98px;
+ height: 44px;
+ background: #00d17d;
+ border-radius: 4px;
+ border: none;
+ color: white;
+ font-family: Roboto;
+ font-style: normal;
+ font-weight: 500;
+ font-size: 14px;
+ cursor: pointer;
+ box-shadow: 0px 4px 5px rgba(0, 0, 0, 0.14), 0px 1px 10px rgba(0, 0, 0, 0.12),
+ 0px 2px 4px rgba(0, 0, 0, 0.2);
+ transition: all 0.3s;
+ &:hover {
+ filter: brightness(95%);
+ }
+`;
+
+export const Overlay = styled.div`
+ position: absolute;
+ background-color: rgb(0, 0, 0, 0.3);
+ width: 100%;
+ height: 100%;
+`;
diff --git a/src/container/form/style.js b/src/container/form/style.js
new file mode 100644
index 0000000..432e833
--- /dev/null
+++ b/src/container/form/style.js
@@ -0,0 +1,213 @@
+import styled from 'styled-components';
+
+export const SectionWrapper = styled.div`
+ overflow-x: hidden;
+ @media (max-width: 568px) {
+ padding: 8px;
+ box-sizing: border-box;
+ }
+`;
+
+export const FormContainer = styled.div`
+ padding-top: 100px;
+ width: 100%;
+ min-height: 100vh;
+ display: grid;
+ gap: 25px;
+ grid-template-rows: 1fr;
+ grid-template-columns: 1fr 1fr;
+
+ @media (max-width: 1200px) {
+ grid-template-columns: 1fr 0px;
+ gap: 0px;
+ }
+`;
+
+export const FormContent = styled.div`
+ max-width: 450px;
+ align-items: center;
+ margin: auto;
+`;
+
+export const FormTitle = styled.div``;
+
+export const FormHead = styled.h2`
+ font-family: Roboto;
+ font-style: normal;
+ font-weight: bold;
+ font-size: 64px;
+ color: #262727;
+`;
+
+export const FormDetail = styled.p`
+ font-family: Roboto;
+ font-style: normal;
+ font-weight: normal;
+ font-size: 24px;
+ color: rgba(0, 0, 0, 0.6);
+`;
+
+export const FormWrap = styled.form`
+ margin-top: 50px;
+`;
+
+export const Label = styled.label`
+ font-family: Roboto;
+ font-style: normal;
+ font-weight: 500;
+ font-size: 14px;
+ line-height: 16px;
+ color: rgba(0, 0, 0, 0.6);
+ margin-bottom: 10px;
+`;
+
+export const NameInput = styled.div`
+ margin-bottom: 20px;
+ display: flex;
+ gap: 30px;
+
+ @media (max-width: 576px) {
+ flex-direction: column;
+ }
+`;
+
+export const Input = styled.input`
+ height: 56px;
+ width: 100%;
+ border: 1px solid rgba(0, 0, 0, 0.36);
+ box-sizing: border-box;
+ border-radius: 4px;
+ margin-right: 26px;
+ padding-left: 15px;
+ margin-bottom: 20px;
+ padding-right: 15px;
+ &:focus {
+ outline: none;
+ }
+ ::placeholder {
+ font-family: Roboto;
+ font-style: normal;
+ font-weight: normal;
+ font-size: 16px;
+ line-height: 19px;
+ color: rgba(0, 0, 0, 0.36);
+ }
+`;
+
+export const InputSmallLeft = styled.input`
+ width: 100%;
+ height: 56px;
+ border: 1px solid rgba(0, 0, 0, 0.36);
+ box-sizing: border-box;
+ border-radius: 4px;
+ padding-left: 15px;
+ padding-right: 15px;
+ &:focus {
+ outline: none;
+ }
+ ::placeholder {
+ font-family: Roboto;
+ font-style: normal;
+ font-weight: normal;
+ font-size: 16px;
+ line-height: 19px;
+ color: rgba(0, 0, 0, 0.36);
+ }
+`;
+
+export const InputSmall = styled.input`
+ width: 100%;
+ height: 56px;
+ border: 1px solid rgba(0, 0, 0, 0.36);
+ box-sizing: border-box;
+ border-radius: 4px;
+ padding-left: 15px;
+ padding-right: 15px;
+ &:focus {
+ outline: none;
+ }
+ ::placeholder {
+ font-family: Roboto;
+ font-style: normal;
+ font-weight: normal;
+ font-size: 16px;
+ line-height: 19px;
+ color: rgba(0, 0, 0, 0.36);
+ }
+`;
+
+export const OptionContainer = styled.div`
+ display: flex;
+ margin-bottom: 20px;
+ justify-content: space-between;
+ gap: 30px;
+
+ @media (max-width: 576px) {
+ flex-direction: column;
+ }
+`;
+
+export const Select = styled.select`
+ width: 100%;
+ height: 56px;
+ padding-left: 15px;
+ padding-right: 15px;
+ outline: none;
+ border: 1px solid rgba(0, 0, 0, 0.36);
+ margin-bottom: 20px;
+`;
+
+export const Option = styled.option`
+ height: 100px;
+`;
+
+export const SmallSelect = styled.select`
+ outline: none;
+ width: 100%;
+ height: 56px;
+ border: 1px solid rgba(0, 0, 0, 0.36);
+ padding-left: 15px;
+ padding-right: 15px;
+`;
+
+export const SmallOption = styled.option``;
+
+export const FormLineWrap = styled.div`
+ width: 100%;
+`;
+
+export const CheckBox = styled.input`
+ margin-right: 12px;
+`;
+
+export const SubmitButton = styled.button`
+ width: 120px;
+ height: 44px;
+ background: #00d17d;
+ box-shadow: 0px 4px 5px rgba(0, 0, 0, 0.14), 0px 1px 10px rgba(0, 0, 0, 0.12),
+ 0px 2px 4px rgba(0, 0, 0, 0.2);
+ border-radius: 4px;
+ margin-top: 50px;
+ color: white;
+ border: none;
+ cursor: pointer;
+ transition: all 0.3s;
+ &:hover {
+ filter: brightness(95%);
+ }
+`;
+
+export const ImageContainer = styled.div`
+ position: absolute;
+ margin-top: auto;
+ right: 0;
+ z-index: -1;
+ @media (max-width: 1200px) {
+ display: none;
+ }
+`;
+
+export const FormImage = styled.object`
+ width: 100%;
+ height: 100%;
+`;
diff --git a/src/container/home/Landing/About/index.js b/src/container/home/Landing/About/index.js
index 05b1e4f..0ebbb02 100644
--- a/src/container/home/Landing/About/index.js
+++ b/src/container/home/Landing/About/index.js
@@ -20,8 +20,12 @@ const About = () => {
Còn chờ gì nữa, nhanh tay đăng kí nào các bạn!
- ĐĂNG KÍ
- TIMELINE
+
+ ĐĂNG KÍ
+
+
+ TIMELINE
+
);
diff --git a/src/container/home/submission/index.js b/src/container/home/submission/index.js
index d9d6583..5c0be93 100644
--- a/src/container/home/submission/index.js
+++ b/src/container/home/submission/index.js
@@ -51,7 +51,7 @@ const Submission = () => {
return (
-
+
Giai Đoạn Tuyển Chọn
diff --git a/src/container/home/submission/items/index.js b/src/container/home/submission/items/index.js
index 07af1c1..0426af8 100644
--- a/src/container/home/submission/items/index.js
+++ b/src/container/home/submission/items/index.js
@@ -29,7 +29,7 @@ const Items = (props) => {
if (isDeatails !== 'Đăng kí') {
setOpen(true);
} else {
- history.push('/activities');
+ history.push('/form');
}
};
const closePopup = () => setOpen(false);
diff --git a/src/container/index.js b/src/container/index.js
index 8eb25de..c2aafcd 100644
--- a/src/container/index.js
+++ b/src/container/index.js
@@ -6,6 +6,7 @@ import FAQ from './FAQ';
import Footer from './Footer';
import Header from './Header';
import Activities from './activities';
+import Form from './form';
import Home from './home';
const LandingPage = () => {
@@ -19,6 +20,9 @@ const LandingPage = () => {
+
+
+
diff --git a/src/index.css b/src/index.css
index 6f2fe34..c4d4754 100644
--- a/src/index.css
+++ b/src/index.css
@@ -9,3 +9,7 @@
img {
pointer-events: none;
}
+
+a {
+ text-decoration: none;
+}
diff --git a/src/utils/Apicaller.js b/src/utils/Apicaller.js
new file mode 100644
index 0000000..484d0f1
--- /dev/null
+++ b/src/utils/Apicaller.js
@@ -0,0 +1,29 @@
+import axios from 'axios';
+
+import { API_URL } from '../config';
+
+const request = (endpoint, method, headers, params, body) => {
+ return axios({
+ url: API_URL + endpoint,
+ method: method,
+ headers: Object.assign({}, headers),
+ params: Object.assign({}, params),
+ data: body,
+ });
+};
+
+export const get = (endpoint, params, headers) => {
+ return request(endpoint, 'GET', headers, params);
+};
+
+export const post = (endpoint, body, params, headers) => {
+ return request(endpoint, 'POST', headers, params, body);
+};
+
+export const put = (endpoint, body, params, headers) => {
+ return request(endpoint, 'PUT', headers, params, body);
+};
+
+export const remove = (endpoint, body, params, headers) => {
+ return request(endpoint, 'DELETE', headers, params, body);
+};
diff --git a/src/utils/index.js b/src/utils/index.js
deleted file mode 100644
index e69de29..0000000
diff --git a/yarn.lock b/yarn.lock
index b7031f4..5f853c7 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -944,6 +944,13 @@ aws4@^1.8.0:
resolved "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz"
integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==
+axios@^0.23.0:
+ version "0.23.0"
+ resolved "https://registry.yarnpkg.com/axios/-/axios-0.23.0.tgz#b0fa5d0948a8d1d75e3d5635238b6c4625b05149"
+ integrity sha512-NmvAE4i0YAv5cKq8zlDoPd1VLKAqX5oLuZKs8xkJa4qi6RGn0uhCYFjWtHHC9EM/MwOwYWOs53W+V0aqEXq1sg==
+ dependencies:
+ follow-redirects "^1.14.4"
+
axobject-query@^0.1.0:
version "0.1.0"
resolved "https://registry.npmjs.org/axobject-query/-/axobject-query-0.1.0.tgz"
@@ -4084,7 +4091,7 @@ flatten@^1.0.2:
resolved "https://registry.npmjs.org/flatten/-/flatten-1.0.3.tgz"
integrity sha512-dVsPA/UwQ8+2uoFe5GHtiBMu48dWLTdsuEd7CKGlZlD78r1TTWBvDuFaFGKCo/ZfEr95Uk56vZoX86OsHkUeIg==
-follow-redirects@^1.0.0:
+follow-redirects@^1.0.0, follow-redirects@^1.14.4:
version "1.14.4"
resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.4.tgz"
integrity sha512-zwGkiSXC1MUJG/qmeIFH2HBJx9u0V46QGUe3YR1fXG8bXQxq7fLj0RjLZQ5nubr9qNJUZrH+xUcwXEoXNpfS+g==