diff --git a/application/src/main/webapp/src/Routes.tsx b/application/src/main/webapp/src/Routes.tsx index 1c1c7b8a..0e82cc4e 100644 --- a/application/src/main/webapp/src/Routes.tsx +++ b/application/src/main/webapp/src/Routes.tsx @@ -1,69 +1,52 @@ import { lazy, Suspense } from "react"; import { Navigate, Route, Routes } from "react-router-dom"; -import ProjectEditGeneral from "pages/project-edit/general"; +const GettingStarted = lazy(() => import("./pages/getting-started")); -const ProjectList = lazy(() => import("./pages/project-list")); -const ProjectEdit = lazy(() => import("./pages/project-edit")); -// const ProjectEditGeneral = lazy(() => import("./pages/project-edit/general")); -const ProjectEditSunat = lazy(() => import("./pages/project-edit/sunat")); -const ProjectEditCertificates = lazy( - () => import("./pages/project-edit/certificates") -); -const ProjectEditCompanies = lazy( - () => import("./pages/project-edit/companies") -); - -const DocumentList = lazy(() => import("./pages/document-list")); +const ProjectWrapper = lazy(() => import("./pages/project")); +const ProjectSettings = lazy(() => import("./pages/project/settings")); +const ProjectSunat = lazy(() => import("./pages/project/sunat")); +const ProjectCertificates = lazy(() => import("./pages/project/certificates")); +const ProjectCompanies = lazy(() => import("./pages/project/companies")); +const ProjectDocuments = lazy(() => import("./pages/project/documents")); export const AppRoutes = () => { const routes = [ { - Component: ProjectList, - path: "/projects", - hasDescendant: true, + Component: GettingStarted, + path: "/getting-started", + hasDescendant: false, }, { - Component: ProjectEdit, + Component: ProjectWrapper, path: "/projects/:projectName", children: [ { - Component: () => , + Component: () => , path: "", }, { - Component: ProjectEditGeneral, - path: "general", + Component: ProjectSettings, + path: "settings", }, { - Component: ProjectEditSunat, + Component: ProjectSunat, path: "sunat", }, { - Component: ProjectEditCertificates, + Component: ProjectCertificates, path: "certificates", }, { - Component: ProjectEditCompanies, + Component: ProjectCompanies, path: "companies", }, + { + Component: ProjectDocuments, + path: "documents", + }, ], }, - { - Component: ProjectList, - path: "/projects", - hasDescendant: true, - }, - { - Component: DocumentList, - path: "/documents", - hasDescendant: false, - }, - { - Component: DocumentList, - path: "/documents/projects/:projectName", - hasDescendant: false, - }, ]; return ( @@ -86,7 +69,7 @@ export const AppRoutes = () => { )} ))} - } /> + } /> } /> diff --git a/application/src/main/webapp/src/layout/sidebar.css b/application/src/main/webapp/src/layout/sidebar.css new file mode 100644 index 00000000..d0ee38b4 --- /dev/null +++ b/application/src/main/webapp/src/layout/sidebar.css @@ -0,0 +1,32 @@ +.project { + padding: var(--pf-c-nav__link--PaddingTop) var(--pf-c-nav__link--PaddingRight) + var(--pf-global--spacer--lg) var(--pf-c-nav__link--PaddingLeft); + border-bottom: 1px solid var(--pf-c-nav__link--before--BorderColor); +} + +.project .pf-c-context-selector__toggle { + color: #FFFFFF; +} + +.project .pf-c-select .pf-c-select__toggle { + color: var(--pf-global--BackgroundColor--100); + background-color: var(--pf-global--Color--100); +} + +.project .pf-c-select .pf-c-select__toggle::before { + border-color: var(--pf-global--BackgroundColor--dark-400) + var(--pf-global--BackgroundColor--dark-400) + var(--pf-c-select__toggle--before--BorderBottomColor) + var(--pf-global--BackgroundColor--dark-400); +} + +.project .pf-c-select .pf-c-select__toggle:focus::before, +.project .pf-c-select .pf-c-select__toggle:hover::before { + border-bottom-color: var(--pf-c-select__toggle--before--BorderBottomColor); +} + +.project .pf-c-select .pf-c-select__toggle:focus::before { + border-bottom-width: var( + --pf-c-select__toggle--m-expanded--before--BorderBottomWidth + ); +} diff --git a/application/src/main/webapp/src/layout/sidebar.tsx b/application/src/main/webapp/src/layout/sidebar.tsx index 4412f3da..cc675efc 100644 --- a/application/src/main/webapp/src/layout/sidebar.tsx +++ b/application/src/main/webapp/src/layout/sidebar.tsx @@ -1,42 +1,136 @@ -import React from "react"; -import { NavLink } from "react-router-dom"; +import React, { useReducer } from "react"; +import { NavLink, useMatch, useNavigate } from "react-router-dom"; -import { Nav, NavList, PageSidebar } from "@patternfly/react-core"; +import { + Button, + ContextSelector, + ContextSelectorFooter, + ContextSelectorItem, + Nav, + NavList, + PageSidebar, +} from "@patternfly/react-core"; import { css } from "@patternfly/react-styles"; -import { useProjectContext } from "shared/context"; import { LayoutTheme } from "./layout-constants"; +import "./sidebar.css"; +import { useProjectsQuery } from "queries/projects"; +import { AddProjectWizard } from "shared/components"; + export const SidebarApp: React.FC = () => { - const { currentContext } = useProjectContext(); + const navigate = useNavigate(); + const routeParams = useMatch("/projects/:projectName/*"); + + const projectsQuery = useProjectsQuery(); + + const [isCreateProjectWizardOpen, toggleCreateProjectWizard] = useReducer( + (val) => !val, + false + ); + const [isCtxSelectorToggled, toggleCtxSelector] = useReducer( + (val) => !val, + false + ); + + const navigateToSelectedProject = (projectName: string) => { + navigate(`/projects/${projectName}`); + }; const renderPageNav = () => { return ( ); }; diff --git a/application/src/main/webapp/src/pages/document-list/index.ts b/application/src/main/webapp/src/pages/document-list/index.ts deleted file mode 100644 index 098a1fcf..00000000 --- a/application/src/main/webapp/src/pages/document-list/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { DocumentList as default } from "./document-list"; diff --git a/application/src/main/webapp/src/pages/getting-started/getting-started.tsx b/application/src/main/webapp/src/pages/getting-started/getting-started.tsx new file mode 100644 index 00000000..23528ac8 --- /dev/null +++ b/application/src/main/webapp/src/pages/getting-started/getting-started.tsx @@ -0,0 +1,66 @@ +import React from "react"; + +import { + Bullseye, + Button, + EmptyState, + EmptyStateBody, + EmptyStateIcon, + EmptyStateSecondaryActions, + SelectVariant, + Title, +} from "@patternfly/react-core"; + +import FileCodeIcon from "@patternfly/react-icons/dist/esm/icons/file-code-icon"; +import { SimpleSelect, useModal } from "@project-openubl/lib-ui"; +import { ProjectDto } from "api/models"; +import { AddProjectWizard } from "shared/components"; +import { useNavigate } from "react-router-dom"; +import { useProjectsQuery } from "queries/projects"; + +export const GettingStarted: React.FC = () => { + const navigate = useNavigate(); + const projectModal = useModal<"ADD", ProjectDto>(); + + const projectsQuery = useProjectsQuery(); + + return ( + <> + + + + + Bienvenido a Ublhub + + + Selecciona un proyecto o crea uno nuevo. + + + + elem.name)} + onChange={(selection) => { + navigate(`/projects/${selection}`); + }} + /> + + + + {projectModal.isOpen && projectModal.isAction("ADD") && ( + { + projectModal.close(); + navigate(`/projects/${project.name}`); + }} + onClose={projectModal.close} + /> + )} + + ); +}; diff --git a/application/src/main/webapp/src/pages/getting-started/index.ts b/application/src/main/webapp/src/pages/getting-started/index.ts new file mode 100644 index 00000000..702d1f06 --- /dev/null +++ b/application/src/main/webapp/src/pages/getting-started/index.ts @@ -0,0 +1 @@ +export { GettingStarted as default } from "./getting-started"; diff --git a/application/src/main/webapp/src/pages/project-edit/companies.tsx b/application/src/main/webapp/src/pages/project-edit/companies.tsx deleted file mode 100644 index bf92f6cd..00000000 --- a/application/src/main/webapp/src/pages/project-edit/companies.tsx +++ /dev/null @@ -1,380 +0,0 @@ -import React, { useState } from "react"; -import { Link, useOutletContext } from "react-router-dom"; -import { Trans, useTranslation } from "react-i18next"; - -import { useSelectionState } from "@migtools/lib-ui"; -import { - ConditionalRender, - useConfirmationContext, - useModal, - useTable, - useTableControls, -} from "@project-openubl/lib-ui"; - -import { - Bullseye, - Button, - ButtonVariant, - DataList, - DataListAction, - DataListCell, - DataListItem, - DataListItemCells, - DataListItemRow, - Drawer, - DrawerActions, - DrawerCloseButton, - DrawerContent, - DrawerContentBody, - DrawerHead, - DrawerPanelContent, - Dropdown, - DropdownItem, - DropdownPosition, - EmptyState, - EmptyStateBody, - EmptyStateIcon, - Icon, - KebabToggle, - Modal, - SearchInput, - Stack, - StackItem, - Text, - TextContent, - Title, - Toolbar, - ToolbarContent, - ToolbarGroup, - ToolbarItem, - ToolbarToggleGroup, -} from "@patternfly/react-core"; -import { - CheckCircleIcon, - FilterIcon, - InfoAltIcon, - InfrastructureIcon, -} from "@patternfly/react-icons"; - -import { CompanyDto, ProjectDto } from "api/models"; -import { useCompaniesQuery, useDeleteCompanyMutation } from "queries/companies"; - -import { AddCompanyForm } from "./components/add-company-form"; -import { CompanyDetailsTabs } from "./components/company-details-tabs"; -import { CompanyLogo } from "./components/company-logo"; - -export const compareByColumnIndex = ( - a: CompanyDto, - b: CompanyDto, - columnIndex?: number -) => { - switch (columnIndex) { - default: - return 0; - } -}; - -export const filterByText = (filterText: string, item: CompanyDto) => { - const text = filterText.toLowerCase(); - return ( - item.ruc.toString().toLowerCase().indexOf(text) !== -1 || - item.name.toString().toLowerCase().indexOf(text) !== -1 || - item.description?.toString().toLowerCase().indexOf(text) !== -1 - ); -}; - -const Companies: React.FC = () => { - const { t } = useTranslation(); - - const confirmationModal = useConfirmationContext(); - - const project = useOutletContext(); - const companiesQuery = useCompaniesQuery(project?.name || null); - const deleteCompanyMutation = useDeleteCompanyMutation( - project?.name || null, - () => { - confirmationModal.close(); - } - ); - - const companyModal = useModal<"ADD", CompanyDto>(); - const drawerModal = useModal<"VIEW", CompanyDto>(); - - const { - isItemSelected: isActionsKebabExpanded, - toggleItemSelected: toggleActionsKebab, - } = useSelectionState({ - items: companiesQuery.data || [], - isEqual: (a, b) => { - return a.ruc === b.ruc; - }, - }); - - const [filterText, setFilterText] = useState(""); - const { page: currentPage, sortBy: currentSortBy } = useTableControls({ - page: { page: 1, perPage: 1_000 }, - sortBy: { index: 0, direction: "asc" }, - }); - - const { filteredItems } = useTable({ - items: companiesQuery.data || [], - currentPage: currentPage, - currentSortBy: currentSortBy, - compareToByColumn: compareByColumnIndex, - filterItem: (item) => filterByText(filterText, item), - }); - - const deleteCompany = (row: CompanyDto) => { - confirmationModal.open({ - title: t("modal.confirm-delete.title", { - what: t("terms.company"), - }), - titleIconVariant: "warning", - message: ( - - ¿Estas seguro de querer eliminar este(a) type? Esta acción - eliminará name permanentemente. - - ), - confirmBtnVariant: ButtonVariant.danger, - confirmBtnLabel: t("actions.delete"), - cancelBtnLabel: t("actions.cancel"), - onConfirm: () => { - confirmationModal.enableProcessing(); - deleteCompanyMutation.mutate(row); - }, - }); - }; - - return ( - <> -
- - - - - {drawerModal.data?.name} - - - - - -
- {project && drawerModal.data && ( - - )} -
- - } - > - - - - } - breakpoint="xl" - > - - setFilterText(value)} - /> - - - - - - - - - - - - - - - Vacío - - - No hay empresas registradas - - - - } - > - - {filteredItems.map((item) => ( - - - -
{item.name}
- {item.description} - , - - RUC: {item.ruc} - , - - {item.sunat ? ( -
-
- - - - {" "} - SUNAT - -
-
- ) : ( -
-
- - - - {" "} - SUNAT - -
- - Proyecto - -
- )} -
, - - {project?.name && item.ruc && ( - - )} - , - - - - - - - , - ]} - /> - - toggleActionsKebab(item)} - /> - } - dropdownItems={[ - { - drawerModal.open("VIEW", item); - toggleActionsKebab(item); - }} - > - {t("actions.edit")} - , - deleteCompany(item)} - > - {t("actions.delete")} - , - ]} - menuAppendTo={() => - document.getElementById("data-list") as any - } - /> - -
-
- ))} -
-
-
-
-
-
- - - {project && ( - - )} - - - ); -}; - -export default Companies; diff --git a/application/src/main/webapp/src/pages/project-edit/general.tsx b/application/src/main/webapp/src/pages/project-edit/general.tsx deleted file mode 100644 index 120063db..00000000 --- a/application/src/main/webapp/src/pages/project-edit/general.tsx +++ /dev/null @@ -1,198 +0,0 @@ -import React, { useEffect } from "react"; -import { useNavigate, useOutletContext } from "react-router-dom"; -import { useTranslation } from "react-i18next"; - -import { ResolvedQueries } from "@migtools/lib-ui"; -import { NotificationContext } from "@project-openubl/lib-ui"; - -import { Controller, useForm } from "react-hook-form"; -import { yupResolver } from "@hookform/resolvers/yup"; -import { object, string } from "yup"; - -import { - ActionGroup, - Button, - Card, - CardBody, - Form, - FormGroup, - TextArea, - TextInput, -} from "@patternfly/react-core"; -import spacing from "@patternfly/react-styles/css/utilities/Spacing/spacing"; - -import { ProjectDto } from "api/models"; -import { useUpdateProjectMutation } from "queries/projects"; -import { - getValidatedFromError, - getValidatedFromErrorTouched, -} from "utils/modelUtils"; - -interface IGeneralForm { - name: string; - description: string; -} - -const General: React.FC = () => { - const { t } = useTranslation(); - - const navigate = useNavigate(); - const { pushNotification } = React.useContext(NotificationContext); - - const project = useOutletContext(); - const updateProjectMutation = useUpdateProjectMutation((p) => { - pushNotification({ - title: t("info.data-saved"), - message: "", - key: p.name, - variant: "success", - actionClose: true, - timeout: 4000, - }); - }); - - const { - control, - formState: { errors, isValid, isValidating, isDirty }, - reset, - getValues, - } = useForm({ - defaultValues: { name: "", description: "" }, - resolver: yupResolver( - object().shape({ - name: string() - .trim() - .required() - .matches(/[a-z0-9]([-a-z0-9]*[a-z0-9])?/) - .max(250), - // .test("duplicateName", (value, options) => { - // return axios - // .post("/projects/check-name", { name: value }) - // .then(() => true) - // .catch((error: AxiosError) => { - // return value === project?.name - // ? true - // : options.createError({ message: error.response?.data }); - // }); - // }), - description: string().trim().max(250), - }) - ), - mode: "onChange", - }); - - useEffect(() => { - if (project) { - reset({ name: project.name, description: project.description }); - } - }, [project, reset]); - - const save = () => { - if (!project) { - return; - } - - updateProjectMutation.mutate({ - ...project, - ...getValues(), - }); - }; - - return ( - - -
- - ( - - )} - /> - - - ( -