diff --git a/README.md b/README.md index 6423952..6a8ef04 100644 --- a/README.md +++ b/README.md @@ -1,27 +1,87 @@ -# ITA-LANDING -IT Academy fullstack project with php and React +# ITA LANDING FRONTEND -## Installation +Welcome to the ITA LANDING GitHub repository! -Install npm packages: +This repository houses the source code for the IT Academy's landing page and the accompanying administrative backoffice. +IT Academy is a leading educational institution dedicated to providing tech education. -```bash -npm i -``` +**The code is developed by students who have completed the IT Academy Bootcamp** and are currently in the project phase. In these projects, there is a Product Owner and a Scrum Master to enable students to apply their knowledge in a real-world environment. + + +## Demo + +https://it-academy-landing.netlify.app/ -## Run -```bash +## Screenshots + +![App Screenshot](https://user-images.githubusercontent.com/92028251/272572895-7ca2d8a7-71e0-4f3c-bee6-a47194030b6a.png) + + +## Instalation + +```console +git clone https://github.com/IT-Academy-BCN/ita-landing-frontend.git +npm i npm run dev ``` -## Contributing -- Create the components in the /components folder. -- If it is necessary to create a page, it must be done in the /pages folder. -- Logic and API calls should not be implemented in components. Context or Redux must be used for this purpose. +## Contribution Guidelines + +Contributions are always welcome! + + +To ensure a smooth and organized development process, please follow these guidelines when contributing: + +### Folder Structure +Components: All React components should be created within the /components folder. These components should focus solely on rendering and presenting data. They should not contain any logic or API calls. + +Pages: If it is necessary to create a new page, please do so within the /pages folder. Pages should serve as the top-level components that define the structure of a route and may contain a composition of components. Keep pages clean and avoid adding complex logic directly to them. + +### Separation of Concerns +Logic and API Calls: Components should not contain logic or make API calls directly. For handling application logic and data fetching, utilize either the React Context API or Redux state management. Create separate files for actions, reducers, and selectors as needed within the Redux structure. + +Redux: If you're working on state management, follow the Redux pattern for actions and reducers. Ensure that reducers are pure functions and keep state updates predictable. + +### Git Workflow + +1. Branches: When working on a new feature or bug fix, create a new branch from the dev branch with a descriptive name, such as feature/add-user-profile. Make your changes within this branch. + +2. Commits: Make frequent, well-documented commits. Use clear and concise commit messages that describe the purpose of each commit. + +3. Pull Requests: Submit a pull request when your feature or bug fix is ready for review. Include a description of your changes in the pull request, and reference any related issues, if applicable. + +### Code Quality + +1. Code Style: Maintain a consistent code style throughout the project. Use appropriate naming conventions, indentation, and follow any established coding standards. + +2. Code Reviews: Be open to feedback during code reviews. Reviewers may suggest improvements or changes to ensure code quality and maintainability. + + +By following these contribution guidelines, you'll help maintain a clean and organized codebase, + + +## Used By + +This project is being used by IT Academy at Barcelona Activa + + + + +## Acknowledgements + +To all the students whose hard work makes these projects possible and move forward. + +## FAQ + +#### What are the requirements to participate in projects? + +Complete the React specialization at IT Academy. + + +#### Why should I collaborate on this project? -## License +Because it provides a real-world environment to apply all the concepts learned in the bootcamp. It also allows for learning more advanced concepts and facing real-life situations that may occur in a company. -[MIT](https://choosealicense.com/licenses/mit/) diff --git a/src/assets/img/githubLogo.svg b/src/assets/img/githubLogo.svg new file mode 100644 index 0000000..39b50ae --- /dev/null +++ b/src/assets/img/githubLogo.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/assets/img/pencil.png b/src/assets/img/pencil.png deleted file mode 100644 index e51a2bc..0000000 Binary files a/src/assets/img/pencil.png and /dev/null differ diff --git a/src/assets/img/plus.svg b/src/assets/img/plus.svg new file mode 100644 index 0000000..d217c5f --- /dev/null +++ b/src/assets/img/plus.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/img/user.svg b/src/assets/img/user.svg new file mode 100644 index 0000000..d5ee283 --- /dev/null +++ b/src/assets/img/user.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/components/BackOfficeComponent.tsx b/src/components/BackOfficeComponent.tsx index fb42edc..1df234a 100644 --- a/src/components/BackOfficeComponent.tsx +++ b/src/components/BackOfficeComponent.tsx @@ -4,6 +4,7 @@ import { useEffect, useState } from "react"; import FAQs from "./faqs/FAQsComponent"; import ProjectsComponent from "./apps/ProjectsComponent"; import menu from "../assets/img/menu.png"; +import { AdminButtons } from "./faqs/faqsAdminView/AdminButtons"; function ViewBackOffice({ setIsLogged, @@ -40,17 +41,21 @@ function ViewBackOffice({ } const [isDropdownOpen, setIsDropdownOpen] = useState(false); - const [windowWidth] = useWindowSize(); + const size = useWindowSize(); + const [hiddenAdminButtons, sethiddenAdminButtons] = useState(false); const toggleDropdown = ():void=> { setIsDropdownOpen(!isDropdownOpen); }; useEffect(() => { - if(windowWidth>=1024){ + if(size[1]>=1024){ setIsDropdownOpen(false) + sethiddenAdminButtons(false) + }else{ + sethiddenAdminButtons(true) } - }, [windowWidth]); + }, [size[1]]); return ( @@ -87,11 +92,12 @@ return ( -
+
{/* Mobile */}
+
@@ -115,17 +121,17 @@ return (
- - {/*
{ setIsDropdownOpen(false)}} className="opacity-25 fixed inset-0 z-40 bg-black">
*/}
)} {!isDropdownOpen && ( <> -
- +
+ {!hiddenAdminButtons && } +
+ {!hiddenAdminButtons && }
diff --git a/src/components/apps/Apps.tsx b/src/components/apps/Apps.tsx new file mode 100644 index 0000000..4647cfc --- /dev/null +++ b/src/components/apps/Apps.tsx @@ -0,0 +1,87 @@ +import { RootState } from "../../store/store"; +import { createToken, ApiStateApps } from "../../interfaces/interfaces"; +import { FaArrowRight } from "react-icons/fa"; +import { useEffect, useState } from "react"; +import { apiCallApps, apiCallAppsInfo, deleteApiApps } from "../../store/reducers/appsCall/appsCallApiFunctionality"; +import { useDispatch, useSelector } from "react-redux"; +import ModalApps from "./appsAdminView/modalApps"; +import trashIcon from "../../assets/img/icon-delete-faq-backoffice.png" +import githubLogo from "../../assets/img/githubLogo.svg" + +declare global { + interface Window { + my_modal_1: { + showModal: () => void; + } + } + } + +const Apps = () => { + const { acces_token }: createToken = useSelector( + (state: RootState) => state.apiPostRegister + ); + + const { apps }: ApiStateApps = useSelector( + (state: RootState) => state.appsCallApiFunctionality + ); + + const handleSendApiInfo = (id: number) => { + apiCallAppsInfo(dispatch, id, acces_token); + }; + + const dispatch = useDispatch(); + + useEffect(() => { + apiCallApps(dispatch); + }, []); + + const [newInfoApps, setNewInfoApps] = useState({ + title: "", + description: "", + url: "", + state: "", + github: "", + }); + + return ( + <> + {apps.map((app) => { + return ( +
+ {window.location.pathname === "/backoffice" && ( +
+ + deleteApiApps(app.id, acces_token, dispatch)}> + eliminar + +
+ )} + {window.location.pathname === "/" && ( + + github_link + + )} +

{app.title}

+

{app.description}

+ + Ir a app + +
+ ); + })} + + + ) +} + +export default Apps \ No newline at end of file diff --git a/src/components/apps/ProjectsComponent.tsx b/src/components/apps/ProjectsComponent.tsx index 8ef4075..9311bba 100644 --- a/src/components/apps/ProjectsComponent.tsx +++ b/src/components/apps/ProjectsComponent.tsx @@ -1,179 +1,29 @@ -import curvedArrow from "../../assets/img/curvedArrow.svg"; -import gitLogo from "../../assets/img/gitLogo.svg"; -import { FaRegCircle, FaArrowRight } from "react-icons/fa"; -import { IconContext } from "react-icons"; -import { useEffect, useState } from "react"; -import { - apiCallApps, - apiCallAppsInfo, - postApiApps, -} from "../../store/reducers/appsCall/appsCallApiFunctionality"; -import { useDispatch, useSelector } from "react-redux"; -import { RootState } from "../../store/store"; -import { createToken, ApiStateApps } from "../../interfaces/interfaces"; -import Pencil from "../../assets/img/vector-10.png"; -import ModalApps from "./appsAdminView/modalApps"; -import ModalsAddApps from "./appsAdminView/ModalsAddApps"; - -declare global { - interface Window { - my_modal_1: { - showModal: () => void; - }; - my_modal_2: { - showModal: () => void; - }; - } -} +import Apps from "./Apps"; +import CreateApp from "./appsAdminView/CreateApp"; +import TasksProcess from "./appsHomepageView/TasksProcess"; +import TitleApps from "./appsHomepageView/TitleApps"; export const ProjectsComponent = () => { - const { acces_token }: createToken = useSelector( - (state: RootState) => state.apiPostRegister - ); - - const handleSendApiInfo = (id: number) => { - apiCallAppsInfo(dispatch, id, acces_token); - }; - - const { apps }: ApiStateApps = useSelector( - (state: RootState) => state.appsCallApiFunctionality - ); - - const dispatch = useDispatch(); - - useEffect(() => { - apiCallApps(dispatch); - }, []); - - const [newInfoApps, setNewInfoApps] = useState({ - title: "", - description: "", - url: "", - state: "", - }); - - const sendInfo = () => { - postApiApps(newInfoApps, acces_token, dispatch); - }; return ( <> -
- {/*Projects section*/} -
- {/*Projects title area*/} - {window.location.pathname != "/backoffice" && ( -
- {window.location.pathname !== "/backoffice" && ( - - )} - -

- Directorio de aplicaciones IT Academy -

-
- )} - {/* Projects legend*/} - {window.location.pathname !== "/backoffice" && ( -
-
- - - Terminadas - -
-
- - En construcción - -
-
- - - Próximamente - -
-
- )} - {/*Cards*/} - + {/*Projects section*/} +
+ + {/* HomePage exclusive components */} + {window.location.pathname !== "/backoffice" && (<> )} + + {/*Cards*/} +
{window.location.pathname == "/backoffice" && ( -
- -
+ <> +

Apps

+ + )} - -
- {/*Card 1*/} - - {apps.map((cards) => { - return ( -
-
-

{cards.title}

{" "} - - - -
-
- {window.location.pathname == "/backoffice" && ( -
- { - window.my_modal_1?.showModal(); - handleSendApiInfo(cards.id); - }} - className="w-4 ms-auto cursor-pointer hover:scale-110 " - src={Pencil} - alt="" - /> -
- )} - -

{cards.description}

-
- -
-
-
- ); - })} - - -
-
-
+ +
+ ); }; diff --git a/src/components/apps/appsAdminView/CreateApp.tsx b/src/components/apps/appsAdminView/CreateApp.tsx new file mode 100644 index 0000000..4196c88 --- /dev/null +++ b/src/components/apps/appsAdminView/CreateApp.tsx @@ -0,0 +1,61 @@ +import { RootState } from "../../../store/store"; +import { postApiApps } from "../../../store/reducers/appsCall/appsCallApiFunctionality"; +import { useDispatch, useSelector } from "react-redux"; +import { createToken } from "../../../interfaces/interfaces"; +import { useState } from "react"; +import ModalsAddApps from "./ModalsAddApps"; +import plusIcon from "../../../assets/img/plus.svg" +declare global { + interface Window { + my_modal_2: { + showModal: () => void; + }; + } +} + +const CreateApp = () => { + + const dispatch = useDispatch(); + + const { acces_token }: createToken = useSelector( + (state: RootState) => state.apiPostRegister + ); + + const [newInfoApps, setNewInfoApps] = useState({ + title: "", + description: "", + url: "", + state: "", + github: "", + }); + + const sendInfo = () => { + console.log(newInfoApps, acces_token, dispatch) + postApiApps(newInfoApps, acces_token, dispatch); + }; + + return ( + <> +
+ +
+ + + + + ) +} + +export default CreateApp \ No newline at end of file diff --git a/src/components/apps/appsAdminView/ModalsAddApps.tsx b/src/components/apps/appsAdminView/ModalsAddApps.tsx index c079606..cd32964 100644 --- a/src/components/apps/appsAdminView/ModalsAddApps.tsx +++ b/src/components/apps/appsAdminView/ModalsAddApps.tsx @@ -8,22 +8,21 @@ export default function ModalsAddApps({ setNewInfoApps: any; sendInfo: any; }) { + return ( <> - -
+ + + setNewInfoApps({ ...newInfoApps, title: e.target.value }) } - className="resize-none p-5 focus:border-0 focus:outline-none text-black text-4 font-poppins font-medium w-full" + className="focus:outline-none mb-2 text-black font-poppins font-bold" type="text" /> @@ -32,65 +31,70 @@ export default function ModalsAddApps({ onChange={(e) => setNewInfoApps({ ...newInfoApps, description: e.target.value }) } - className="placeholder-dark bg-transparent text-gray-600 h-full p-4 w-full h-10 focus:border-0 focus:outline-none" - /> -

Url del repositorio

- - setNewInfoApps({ ...newInfoApps, url: e.target.value }) - } - type="text" - placeholder="URL" - className="input input-bordered w-full py-2 " + className="focus:outline-none mt-2 mb-4 w-full text-[#7e7e7e]" /> -

Estado

-
+
+

Url del proyecto

+ + setNewInfoApps({ ...newInfoApps, url: e.target.value }) + } + type="text" + className="input input-bordered w-full py-2" + /> +
+ +
+

Url del repositorio de GitHub

+ + setNewInfoApps({ ...newInfoApps, github: e.target.value }) + } + type="text" + className="input input-bordered w-full py-2" + /> +
+ +

Estado

+

{ - setNewInfoApps({ ...newInfoApps, state: "COMPLETED" }); - }} - className={ - newInfoApps.state == "COMPLETED" - ? " font-bold bg-completed rounded-full p-3 cursor-pointer " + onClick={() => { setNewInfoApps({ ...newInfoApps, state: "COMPLETED" })}} + className={` + ${newInfoApps.state == "COMPLETED" + ? "font-bold bg-completed rounded-full p-3 cursor-pointer" : "cursor-pointer p-3" - } + } text-sm`} > - Completado + Completada

{ - setNewInfoApps({ ...newInfoApps, state: "IN PROGRESS" }); - }} - className={ - newInfoApps.state == "IN PROGRESS" - ? "font-bold bg-building ml-2 rounded-full p-3 cursor-pointer " - : "ml-2 cursor-pointer p-3" - } + onClick={() => {setNewInfoApps({ ...newInfoApps, state: "IN PROGRESS" })}} + className={` + ${newInfoApps.state == "IN PROGRESS" + ? "font-bold bg-building ml-2 rounded-full p-3 cursor-pointer" + : "cursor-pointer p-3 ml-2" + } text-sm`} > En progreso

{ - setNewInfoApps({ ...newInfoApps, state: "SOON" }); - }} - className={ - newInfoApps.state == "SOON" + onClick={() => {setNewInfoApps({ ...newInfoApps, state: "SOON" })}} + className={` + ${newInfoApps.state == "SOON" ? "font-bold bg-soon ml-2 rounded-full p-3 cursor-pointer" - : "ml-2 p-1 cursor-pointer p-3" - } + : "cursor-pointer p-3 ml-2" + } text-sm`} > - Terminado + Próximamente

-
- +
+ +
diff --git a/src/components/apps/appsAdminView/modalApps.tsx b/src/components/apps/appsAdminView/modalApps.tsx index a3c9930..8a7efb7 100644 --- a/src/components/apps/appsAdminView/modalApps.tsx +++ b/src/components/apps/appsAdminView/modalApps.tsx @@ -1,7 +1,6 @@ import { useEffect } from "react"; import { useDispatch, useSelector } from "react-redux"; import { RootState } from "../../../store/store"; -import { deleteApiApps } from "../../../store/reducers/appsCall/appsCallApiFunctionality"; import { putApiApps } from "../../../store/reducers/appsCall/appsCallApiFunctionality"; import Cross from "../../../assets/img/cross.png"; import { createToken } from "../../../interfaces/interfaces"; @@ -27,6 +26,7 @@ export default function ModalApps({ title: appsInfo.title, description: appsInfo.description, url: appsInfo.url, + github: appsInfo.github, state: appsInfo.state, id: appsInfo.id, })); @@ -37,63 +37,77 @@ export default function ModalApps({ <> {!loadingApps ? ( -
+ + setNewInfoApps({ ...newInfoApps, title: e.target.value }) } + className="focus:outline-none mb-2 text-black font-poppins font-bold" + type="text" /> +
+ +
- )} + ); } diff --git a/src/interfaces/interfaces.tsx b/src/interfaces/interfaces.tsx index 29034b4..8813e9b 100644 --- a/src/interfaces/interfaces.tsx +++ b/src/interfaces/interfaces.tsx @@ -21,7 +21,8 @@ export interface AppData { description: string; url: string; state: string; - id: number; + id:number; + github: string; } export interface faqContent {