diff --git a/cypress/e2e/1-dx/1-test-login.cy.ts b/cypress/e2e/1-dx/1-test-login.cy.ts index 0684dc07e..d21fcf3ff 100644 --- a/cypress/e2e/1-dx/1-test-login.cy.ts +++ b/cypress/e2e/1-dx/1-test-login.cy.ts @@ -13,6 +13,6 @@ describe("Login for a test user on DX", () => { }); it("Is Logged in", function () { - cy.contains("CREATE REPORT").should("be.visible"); + cy.contains("Welcome").should("be.visible"); }); }); diff --git a/cypress/e2e/1-dx/2-datasets.cy.ts b/cypress/e2e/1-dx/2-datasets.cy.ts index a572ef7d3..9c5c5f27a 100644 --- a/cypress/e2e/1-dx/2-datasets.cy.ts +++ b/cypress/e2e/1-dx/2-datasets.cy.ts @@ -28,13 +28,11 @@ describe("Testing connecting data on DX", () => { cy.get('[data-cy="cookie-btn"]').click(); - cy.get('[data-cy="create-report-dropdown"]').click(); - cy.get('[data-cy="appbar-connect-data"]').click(); + cy.get('[data-cy="home-connect-dataset-button"]').click(); }); it("Can import data from External Search", () => { cy.intercept(`${apiUrl}/external-sources/search?q=*`).as("getDefaultData"); - cy.get('[data-cy="external-search-button"]').click(); cy.wait("@getDefaultData").then((interception) => { cy.get('[data-cy="external-search-card-Kaggle"]').should( @@ -129,6 +127,8 @@ describe("Testing connecting data on DX", () => { // }); it("Can import data through local upload", () => { + cy.get('[data-cy="file-upload-tab"]').click(); + cy.get('[data-cy="upload-option-button"').first().click(); cy.get('[data-cy="local-upload-input"]').as("fileInput"); cy.fixture("football-players.csv").then((fileContent) => { cy.get("@fileInput").attachFile({ @@ -154,6 +154,8 @@ describe("Testing connecting data on DX", () => { }); it("Can import another dataset through local upload", () => { + cy.get('[data-cy="file-upload-tab"]').click(); + cy.get('[data-cy="upload-option-button"').first().click(); cy.get('[data-cy="local-upload-input"]').as("fileInput"); cy.fixture("grossing-movies.csv").then((fileContent) => { cy.get("@fileInput").attachFile({ diff --git a/cypress/e2e/1-dx/3-charts.cy.ts b/cypress/e2e/1-dx/3-charts.cy.ts index 98f45e2bb..552d93f6d 100644 --- a/cypress/e2e/1-dx/3-charts.cy.ts +++ b/cypress/e2e/1-dx/3-charts.cy.ts @@ -25,9 +25,8 @@ describe("Testing create chart on DX", () => { cy.get('[data-cy="cookie-btn"]').click(); - cy.get('[data-cy="create-report-dropdown"]').click(); cy.intercept("GET", `${apiUrl}/datasets?filter=*`).as("getDatasets"); - cy.get('[data-cy="appbar-create-chart"]').click(); + cy.get('[data-cy="home-create-chart-button"]').click(); cy.wait("@getDatasets"); cy.intercept("GET", `${apiUrl}/chart/sample-data/*`).as("getDataset"); @@ -190,10 +189,8 @@ describe("Testing Ai chart creation", () => { cy.visit("/"); cy.get('[data-cy="cookie-btn"]').click(); - - cy.get('[data-cy="create-report-dropdown"]').click(); cy.intercept("GET", `${apiUrl}/datasets?filter=*`).as("getDatasets"); - cy.get('[data-cy="appbar-create-chart"]').click(); + cy.get('[data-cy="home-create-chart-button"]').click(); cy.wait("@getDatasets"); cy.intercept("GET", `${apiUrl}/chart/sample-data/*`).as("getDataset"); diff --git a/cypress/e2e/1-dx/4-reports.cy.ts b/cypress/e2e/1-dx/4-reports.cy.ts index 10f30c02a..e9fec3630 100644 --- a/cypress/e2e/1-dx/4-reports.cy.ts +++ b/cypress/e2e/1-dx/4-reports.cy.ts @@ -27,7 +27,7 @@ describe("Testing reports on DX", () => { }); it("Can Create report", () => { - cy.get('[data-cy="appbar-create-report/login"]').click(); + cy.get('[data-cy="home-create-report-button"]').click(); cy.contains( '[data-cy="report-template-card"]', @@ -153,8 +153,6 @@ describe("Testing reports on DX", () => { cy.visit("/"); - cy.wait("@fetchReports"); - cy.get('[data-cy="home-charts-tab"]').scrollIntoView().click(); cy.get('[data-cy="home-reports-tab"]').scrollIntoView().click(); diff --git a/cypress/e2e/1-dx/5-home.cy.ts b/cypress/e2e/1-dx/5-home.cy.ts index 707d0550b..509e35b08 100644 --- a/cypress/e2e/1-dx/5-home.cy.ts +++ b/cypress/e2e/1-dx/5-home.cy.ts @@ -14,7 +14,7 @@ describe("Home page tests", () => { cy.location("pathname").should("include", "/about"); cy.get('[data-cy="empower-block-explore-reports-link"]').click(); cy.wait(2000); - cy.location("pathname").should("include", "/explore"); + cy.location("pathname").should("include", "/"); cy.get('[data-cy="nav-about"]').click(); cy.wait(2000); diff --git a/package.json b/package.json index 14bc83db9..9d255b02d 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ }, "dependencies": { "@auth0/auth0-react": "^2.2.1", + "@azure/msal-browser": "^3.16.0", "@craco/craco": "^6.4.3", "@devhammed/use-cookie": "^1.1.1", "@draft-js-plugins/anchor": "^4.1.4", diff --git a/src/app/Routes.tsx b/src/app/Routes.tsx index e649ade7d..8cde0fef2 100755 --- a/src/app/Routes.tsx +++ b/src/app/Routes.tsx @@ -242,9 +242,9 @@ export function MainRoutes() { - + {/* - + */} diff --git a/src/app/assets/icons/data-themes-chart-types/coloredBigNumber.tsx b/src/app/assets/icons/data-themes-chart-types/coloredBigNumber.tsx index 299ce87e1..243104ac9 100644 --- a/src/app/assets/icons/data-themes-chart-types/coloredBigNumber.tsx +++ b/src/app/assets/icons/data-themes-chart-types/coloredBigNumber.tsx @@ -3,15 +3,15 @@ import React from "react"; export default function Icon() { return ( ); diff --git a/src/app/components/AppBar/index.tsx b/src/app/components/AppBar/index.tsx index bcfa407a8..0ece6c626 100644 --- a/src/app/components/AppBar/index.tsx +++ b/src/app/components/AppBar/index.tsx @@ -229,16 +229,17 @@ export function AppBar() { justify-content: flex-end; `} > + {" "} +
+ + Explore + +
Why DataXplorer?
-
- - Explore - -
About @@ -290,7 +291,6 @@ const ActionMenu = () => {
{ :nth-child(1) { width: ${isAuthenticated ? "146px" : "110px"}; height: 34px; - border-radius: ${isAuthenticated ? "24px 0px 0px 24px" : "24px"}; + border-radius: 24px; &:hover { - opacity: 1; + opacity: 0.8; } } - :nth-child(2) { + /* :nth-child(2) { width: 41px; height: 34px; border-radius: 0px 24px 24px 0px; background: ${openActionPopover ? "#b5b5db" : "#dadaf8"}; &:hover { background: #b5b5db; - } + } */ } svg { ${openActionPopover ? "transform: rotate(180deg)" : ""} @@ -327,14 +327,12 @@ const ActionMenu = () => { } `} > - + - {isAuthenticated && ( + {/* {isAuthenticated && ( - )} + )} */} {isAuthenticated && ( - - -
- - {/* {props.uploadError && ( -
+

-

- {" "} - Unable to upload your file. Please try again! -

- Error -
- )} */} - - )} + Drag and Drop Spreadsheets File here +

+ + )} +
+
- + + {fileRejections.length > 0 && fileRejectionItems} + ); }; diff --git a/src/app/modules/dataset-upload-module/component/externalSourcesList.tsx b/src/app/modules/dataset-upload-module/component/externalSourcesList.tsx new file mode 100644 index 000000000..24356ce29 --- /dev/null +++ b/src/app/modules/dataset-upload-module/component/externalSourcesList.tsx @@ -0,0 +1,79 @@ +import React from "react"; + +interface Source { + name: string; + value: string; +} + +interface Props { + baseSources: Source[]; + sources: string[]; + setSources: (c: string[]) => void; + customCss?: { gap: string }; +} +export default function SourceCategoryList(props: Readonly) { + const { customCss } = props; + return ( +
button { + font-size: 14px; + cursor: pointer; + min-width: 160px; + background: #fff; + border-radius: 30px; + border: 0.5px solid #231d2c; + font-family: "GothamNarrow-Book", sans-serif; + padding: 12px 0px; + } + `} + > + {props.baseSources.map((s) => ( + + ))} +
+ ); +} diff --git a/src/app/modules/dataset-upload-module/component/tabs.tsx b/src/app/modules/dataset-upload-module/component/tabs.tsx new file mode 100644 index 000000000..18429ba5a --- /dev/null +++ b/src/app/modules/dataset-upload-module/component/tabs.tsx @@ -0,0 +1,69 @@ +import React from "react"; +import ComputerIcon from "../assets/computer-icon"; +import { Search } from "@material-ui/icons"; + +interface Props { + activeTab: "search" | "file"; + setActiveTab: (c: "search" | "file") => void; +} + +function UploadTabs({ activeTab, setActiveTab }: Props) { + return ( +
+ + + +
+ ); +} + +export default UploadTabs; diff --git a/src/app/modules/dataset-upload-module/component/uploadOption.tsx b/src/app/modules/dataset-upload-module/component/uploadOption.tsx new file mode 100644 index 000000000..ebef45a68 --- /dev/null +++ b/src/app/modules/dataset-upload-module/component/uploadOption.tsx @@ -0,0 +1,198 @@ +import React from "react"; +import SettingsIcon from "../assets/upload-options-icons/settings"; + +const UploadOption = (props: { + name: string; + type: string; + formats: string[]; + icon: React.ReactNode; + disabled?: boolean; + onClick: (e: React.MouseEvent) => void; + setActiveOption: React.Dispatch>; + connected?: boolean; + onLogout?: () => void; + canConnect?: boolean; +}) => { + const [openSettings, setOpenSettings] = React.useState(false); + return ( + + + + + + ) : null} + + ) : ( +
+ {props.formats.map((f) => ( + {f} + ))} +
+ )} + + + ); +}; + +export default UploadOption; diff --git a/src/app/modules/dataset-upload-module/style.ts b/src/app/modules/dataset-upload-module/style.ts index 14969be72..f94d6767c 100644 --- a/src/app/modules/dataset-upload-module/style.ts +++ b/src/app/modules/dataset-upload-module/style.ts @@ -18,36 +18,14 @@ export const stepcss = css` height: 50px; } `; -export const uploadDatasetcss = css` - div:nth-child(1) { - background: #dadaf8; - height: 55px; - width: 100%; - color: #231d2c; - font-family: "GothamNarrow-Book"; - font-style: normal; - font-weight: 500; - font-size: 14px; - display: flex; - align-items: center; - padding-left: 2rem; - margin-top: 5rem; - } -`; export const uploadAreacss = (isDragActive: boolean, disabled?: boolean) => css` - height: 529px; + height: 131px; display: flex; flex-direction: column; justify-content: center; align-items: center; - background-color: ${isDragActive && !disabled ? "#c4c4c4" : "#ffffff"}; - background-image: ${isDragActive && !disabled - ? `url( - "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='163' height='163' viewBox='0 0 20 20'%3E%3Cg %3E%3Cpolygon fill='%23ffffff' points='20 10 10 0 0 0 20 20'/%3E%3Cpolygon fill='%23ffffff' points='0 10 0 20 10 20'/%3E%3C/g%3E%3C/svg%3E" - )` - : "unset"}; &:hover { cursor: ${!isDragActive && !disabled ? "pointer" : "default"}; } @@ -94,7 +72,7 @@ export const metaDatacss = css` font-weight: 500; font-size: 48px; font-family: "Inter", sans-serif; - margin-top: 6rem; + margin-top: 40px; margin-bottom: 4.5rem; } diff --git a/src/app/modules/dataset-upload-module/upload-steps/addDatasetFragment.tsx b/src/app/modules/dataset-upload-module/upload-steps/addDatasetFragment.tsx index 583b07326..509fcae5a 100644 --- a/src/app/modules/dataset-upload-module/upload-steps/addDatasetFragment.tsx +++ b/src/app/modules/dataset-upload-module/upload-steps/addDatasetFragment.tsx @@ -1,69 +1,59 @@ /**third party */ -import React, { useCallback, useEffect } from "react"; -import { useDropzone } from "react-dropzone"; -import useDrivePicker from "react-google-drive-picker"; -import { - CallbackDoc, - PickerCallback, -} from "react-google-drive-picker/dist/typeDefs"; -import axios from "axios"; +import React, { useCallback } from "react"; + /** project */ -import { formatBytes } from "app/utils/formatBytes"; -import { useStoreState } from "app/state/store/hooks"; + import { DropZone } from "app/modules/dataset-upload-module/component/dropzone/"; +import { Box } from "@material-ui/core"; +import LocalIcon from "../assets/upload-options-icons/local"; +import GoogleIcon from "../assets/upload-options-icons/google"; +import MicrosoftIcon from "../assets/upload-options-icons/microsoft"; +import ApiIcon from "../assets/upload-options-icons/api"; +import MSSQLIcon from "../assets/upload-options-icons/mssql.png"; +import MYSQLIcon from "../assets/upload-options-icons/mysql.png"; +import PostgresIcon from "../assets/upload-options-icons/postgres"; +import MongoDbIcon from "../assets/upload-options-icons/mongodb"; +import HubspotIcon from "../assets/upload-options-icons/hubspot"; +import UploadOption from "../component/uploadOption"; +import { useCookie } from "react-use"; +import useGoogleDrivePicker from "app/hooks/useGoogleDrivePicker"; +import { useOneDrivePicker } from "app/hooks/useOneDrivePicker"; interface Props { disabled: boolean; onFileSubmit: (file: File) => void; processingError: string | null; - setIsExternalSearch: React.Dispatch>; + activeOption: string | null; + setActiveOption: React.Dispatch>; + setActiveStep: React.Dispatch>; } export default function AddDatasetFragment(props: Props) { - const [openPicker] = useDrivePicker(); - const token = useStoreState((state) => state.AuthToken.value); - - const handleGoogleDriveFilePicker = async ( - file: CallbackDoc, - accessToken: string - ) => { - try { - const response = await axios({ - url: `https://www.googleapis.com/drive/v3/files/${file.id}${ - file.type === "file" ? "?alt=media" : "/export?mimeType=text/csv" - }`, - method: "GET", - headers: { - Authorization: `Bearer ${accessToken}`, - }, - responseType: "blob", // important - }); - - const b = response?.data; - const gfile = new File([b], file.name, { type: "text/csv" }); + const [googleDriveToken, setGoogleDriveToken, deleteGoogleDriveToken] = + useCookie("googleDriveToken"); - props.onFileSubmit(gfile); - } catch (e) { - console.log(e, "handleGoogleDriveFilePicker error"); - } - }; - useEffect(() => { - if (process.env.REACT_APP_CYPRESS_TEST === "true") { - window.handleGoogleDriveFilePicker = function (file: any, token: string) { - handleGoogleDriveFilePicker(file, token); - }; - } - }, []); + const { getAccessTokenAndOpenPicker } = useGoogleDrivePicker({ + onCancel: () => { + props.setActiveOption(null); + }, + onFileSubmit: (file: File) => { + props.onFileSubmit(file); + }, + googleDriveToken, + setGoogleDriveToken, + }); - const ACCEPTED_FILES = { - "text/csv": [".csv"], - "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": [ - ".xlsx", - ], - "application/xml": [".xml"], - "application/vnd.ms-excel": [".xls"], - "application/xhtml+xml": [".xhtml"], - }; + const { launchPicker, clearToken, connected } = useOneDrivePicker({ + onCancel: () => { + props.setActiveOption(null); + }, + onFileSubmit: (file: File) => { + props.onFileSubmit(file); + }, + onDownloadStart: () => { + props.setActiveStep(1); + }, + }); const onDrop = useCallback((acceptedFiles: File[]) => { if (acceptedFiles.length > 0) { @@ -71,76 +61,207 @@ export default function AddDatasetFragment(props: Props) { } }, []); - const { - getRootProps, - getInputProps, - isDragActive, - acceptedFiles, - fileRejections, - } = useDropzone({ onDrop, accept: ACCEPTED_FILES }); - - const getAccessToken = () => { - return axios.get( - `${process.env.REACT_APP_API}/dataset/google-drive/user-token`, - { - headers: { - Authorization: `Bearer ${token}`, - }, - } - ); - }; - - const getAccessTokenAndOpenPicker = async () => { - try { - const res = await getAccessToken(); - - //opens google drive picker - openPicker({ - clientId: process.env.REACT_APP_GOOGLE_API_CLIENT_ID as string, - developerKey: process.env.REACT_APP_GOOGLE_API_DEV_KEY as string, - viewId: "SPREADSHEETS", - supportDrives: true, - token: res.data, - setSelectFolderEnabled: true, - callbackFunction: (d: PickerCallback) => { - handleGoogleDriveFilePicker(d.docs[0], res.data); - }, - }); - } catch (e) { - console.log(e, "error"); - } - }; - - function handleOpenPicker(e: React.MouseEvent) { - e.stopPropagation(); - getAccessTokenAndOpenPicker(); - } - - const fileRejectionItems = fileRejections.map(({ file, errors }) => ( -
  • - {file.name} - {formatBytes(file.size)} -
      - {errors.map((e) => ( -
    • {e.message}
    • - ))} -
    -
  • - )); + const uploadOptions = [ + { + name: "Local upload", + type: "Table Dataset", + formats: ["CSV", "XSLX", "JSON", "ODS", "SQLite"], + icon: , + onClick: () => {}, + }, + { + name: "Google Drive", + type: "Upload", + formats: [], + icon: , + onClick: (e: React.MouseEvent) => { + e.stopPropagation(); + getAccessTokenAndOpenPicker(); + }, + canConnect: true, + connected: !!googleDriveToken, + onLogout: () => { + deleteGoogleDriveToken(); + }, + }, + { + name: "Microsoft Cloud", + type: "Upload", + formats: [], + icon: , + onClick: (e: React.MouseEvent) => { + e.stopPropagation(); + launchPicker(); + }, + canConnect: true, + connected: connected, + onLogout: async () => { + await clearToken(); + }, + }, + { + name: "API Connection", + type: "URL, JSON or XML root", + formats: ["CSV", "XSLX", "JSON", "ODS", "SQLite"], + icon: , + onClick: () => {}, + }, + { + name: "MSSQL", + type: "DataBase Connection", + formats: ["Coming Soon"], + icon: , + onClick: () => {}, + }, + { + name: "MSSQL", + type: "DataBase Connection", + formats: ["Coming Soon"], + icon: , + onClick: () => {}, + }, + { + name: "PostgreSQL", + type: "DataBase Connection", + formats: ["Coming Soon"], + icon: , + onClick: () => {}, + }, + { + name: "MongoDB", + type: "DataBase Connection", + formats: ["Coming Soon"], + icon: , + onClick: () => {}, + }, + { + name: "Hubspot", + type: "DataBase Connection", + formats: ["Coming Soon"], + icon: , + onClick: () => {}, + }, + ]; return ( <> - - {fileRejections.length > 0 && fileRejectionItems} +
    +

    File Upload

    + +

    + Upload your favourite data effortlessly in DataXplorer, and with just + a few clicks, import datasets without the hassle of downloading,{" "} +
    + enabling you to visualize and analyse diverse data like never before. +

    +
    + + + + {props.activeOption === "Local upload" ? ( + <> + + + ) : ( +
    +
    + Connect your data +
    +
    +
    + {uploadOptions.slice(0, 3).map((option) => ( + + ))} +
    + +
    + +
    + {uploadOptions.slice(3).map((option) => ( + + ))} +
    +
    +
    + )} ); } diff --git a/src/app/modules/dataset-upload-module/upload-steps/externalSearch.tsx b/src/app/modules/dataset-upload-module/upload-steps/externalSearch.tsx index 2ffa2cd79..edd865467 100644 --- a/src/app/modules/dataset-upload-module/upload-steps/externalSearch.tsx +++ b/src/app/modules/dataset-upload-module/upload-steps/externalSearch.tsx @@ -9,6 +9,7 @@ import useDebounce from "react-use/lib/useDebounce"; import axios from "axios"; import CircleLoader from "app/modules/home-module/components/Loader"; import { useInfinityScroll } from "app/hooks/useInfinityScroll"; +import SourceCategoryList from "../component/externalSourcesList"; export interface IExternalDataset { name: string; @@ -32,11 +33,16 @@ export default function ExternalSearch(props: { handleDownload: (dataset: IExternalDataset) => void; setProcessingError: React.Dispatch>; setActiveStep: React.Dispatch>; - setIsExternalSearch: React.Dispatch>; + searchValue: string | undefined; + setSearchValue: React.Dispatch>; + openSearch: boolean; + setOpenSearch: React.Dispatch>; + sources: string[]; + setSources: React.Dispatch>; }) { const observerTarget = React.useRef(null); const [tableView, setTableView] = React.useState(false); - const [searchValue, setSearchValue] = React.useState(""); + const [sortValue, setSortValue] = React.useState("createdDate"); const token = useStoreState((state) => state.AuthToken.value); const history = useHistory(); @@ -45,6 +51,14 @@ export default function ExternalSearch(props: { const limit = 20; const [datasets, setDatasets] = React.useState([]); + const baseSources = [ + { name: "Kaggle", value: "Kaggle" }, + { name: "World Bank", value: "World Bank" }, + { name: "WHO", value: "WHO" }, + { name: "HDX", value: "HDX" }, + { name: "TGF", value: "TGF" }, + ]; + const { isObserved } = useInfinityScroll(observerTarget); const abortControllerRef = React.useRef( @@ -53,8 +67,8 @@ export default function ExternalSearch(props: { const terminateSearch = () => { abortControllerRef.current.abort(); abortControllerRef.current = new AbortController(); - setDatasets([]); - setOffset(0); + // setDatasets([]); + // setOffset(0); }; // Pagination on scroll @@ -68,9 +82,13 @@ export default function ExternalSearch(props: { try { setLoading(true); const response = await axios.get( - `${ - process.env.REACT_APP_API - }/external-sources/search?q=${searchValue}&source=${"Kaggle,World Bank,WHO,HDX"}&offset=${offset}&limit=${limit}`, + `${process.env.REACT_APP_API}/external-sources/search?q=${ + props.searchValue + }&source=${ + props.sources.length + ? props.sources.join(",") + : "Kaggle,World Bank,WHO,HDX,TGF" + }&offset=${offset}&limit=${limit}`, { signal: abortControllerRef.current.signal, headers: { @@ -112,70 +130,69 @@ export default function ExternalSearch(props: { } }, 500, - [searchValue, token] + [props.searchValue, token, props.sources] ); return ( <>
    - props.setIsExternalSearch(false)}> - - -
    -

    External Search

    -

    - Connect to your favourite data sources effortlessly in DataXplorer, - and with just a few clicks, import datasets without the hassle of - downloading, enabling you to visualize and analyse diverse data like - never before. -

    -
    +

    Federated Search

    + +

    + Connect to your favourite data sources effortlessly in DataXplorer, + and with just a few clicks, import datasets without the hassle of + downloading, +
    enabling you to visualize and analyse diverse data like never + before. +

    - - - - + + + + + - + + {datasets && datasets?.map((dataset, index) => ( diff --git a/src/app/modules/dataset-upload-module/upload-steps/finishedFragment.tsx b/src/app/modules/dataset-upload-module/upload-steps/finishedFragment.tsx index e3b6473f7..2e82da0d5 100644 --- a/src/app/modules/dataset-upload-module/upload-steps/finishedFragment.tsx +++ b/src/app/modules/dataset-upload-module/upload-steps/finishedFragment.tsx @@ -85,7 +85,7 @@ export default function FinishedFragment(props: Props) { { setDisplay("data"); - return location.search.includes("?fromHome=true") ? "/" : "/explore"; + return "/"; })()} css={` display: flex; diff --git a/src/app/modules/dataset-upload-module/upload-steps/index.tsx b/src/app/modules/dataset-upload-module/upload-steps/index.tsx index 888cc59b5..fc935e550 100644 --- a/src/app/modules/dataset-upload-module/upload-steps/index.tsx +++ b/src/app/modules/dataset-upload-module/upload-steps/index.tsx @@ -1,5 +1,5 @@ /** third party */ -import React from "react"; +import React, { useEffect } from "react"; import axios from "axios"; import Container from "@material-ui/core/Container"; import { useAuth0, withAuthenticationRequired } from "@auth0/auth0-react"; @@ -21,6 +21,9 @@ import Stepper from "app/modules/dataset-upload-module/component/stepper"; import { Box } from "@material-ui/core"; import { useTitle } from "react-use"; import { DatasetListItemAPIModel } from "app/modules/dataset-module/data"; +import BreadCrumbs from "app/modules/home-module/components/Breadcrumbs"; +import UploadTabs from "../component/tabs"; +import SmallFooter from "app/modules/home-module/components/Footer/smallFooter"; interface Props { datasetId: string; @@ -47,8 +50,12 @@ function DatasetUploadSteps(props: Props) { ); const [processed, setProcessed] = React.useState(false); const [selectedFile, setSelectedFile] = React.useState(null); - const [isExternalSearch, setIsExternalSearch] = React.useState(false); - const [fromExternalSearch, setFromExternalSearch] = React.useState(false); + const [searchValue, setSearchValue] = React.useState(""); + const [openSearch, setOpenSearch] = React.useState(false); + const [sources, setSources] = React.useState([]); + + const [activeTab, setActiveTab] = React.useState<"search" | "file">("search"); + const [activeOption, setActiveOption] = React.useState(null); const defaultProcessingError = "Data could not be processed, please try again or contact your administrator"; @@ -214,9 +221,6 @@ function DatasetUploadSteps(props: Props) { const id = ObjectId(); //expose file id to datasetId state; to be used in dataset upload props.setDatasetId(id); - //set isExternalSearch to false - setIsExternalSearch(false); - setFromExternalSearch(true); //set active step to processing setActiveStep(1); axios @@ -260,21 +264,67 @@ function DatasetUploadSteps(props: Props) { const tryAgain = () => { setActiveStep(0); - if (fromExternalSearch) { - setIsExternalSearch(true); - } }; + useEffect(() => { + if (activeOption) { + setActiveOption(null); + } + }, [activeTab]); + const currentStep = () => { switch (activeStep) { case 0: return ( - + <> + + { + if (activeOption) { + setActiveOption(null); + } + }} + > + Connect Data + + ), + path: "#", + }, + ...(activeOption ? [{ title: activeOption }] : []), + ]} + /> + + + + {activeTab === "search" ? ( + + ) : ( + + )} + ); case 1: return ( @@ -324,7 +374,9 @@ function DatasetUploadSteps(props: Props) { onFileSubmit={onFileSubmit} disabled={false} processingError={processingError} - setIsExternalSearch={setIsExternalSearch} + setActiveOption={setActiveOption} + activeOption={activeOption} + setActiveStep={setActiveStep} /> ); } @@ -332,38 +384,32 @@ function DatasetUploadSteps(props: Props) { return ( <> - -
    - {steps.map((tab, index) => ( - 0 && !processed && activeStep !== index} - /> - ))} -
    +
    + +
    + {steps.map((tab, index) => ( + 0 && !processed && activeStep !== index} + /> + ))} +
    - {isExternalSearch ? ( - <> - - - - ) : ( <>
    {currentStep()}
    - )} -
    + +
    + {activeStep === 0 ? : null} ); } diff --git a/src/app/modules/home-module/assets/add-img-white.svg b/src/app/modules/home-module/assets/add-img-white.svg new file mode 100644 index 000000000..8c7f21fd4 --- /dev/null +++ b/src/app/modules/home-module/assets/add-img-white.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/app/modules/home-module/assets/tgf-logo.svg b/src/app/modules/home-module/assets/tgf-logo.svg new file mode 100644 index 000000000..020f6e39a --- /dev/null +++ b/src/app/modules/home-module/assets/tgf-logo.svg @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/src/app/modules/home-module/components/All/assetsGrid.tsx b/src/app/modules/home-module/components/All/assetsGrid.tsx new file mode 100644 index 000000000..1d6b44157 --- /dev/null +++ b/src/app/modules/home-module/components/All/assetsGrid.tsx @@ -0,0 +1,388 @@ +/* third party */ +import React from "react"; +import axios from "axios"; +import get from "lodash/get"; +import find from "lodash/find"; +import Box from "@material-ui/core/Box"; +import Grid, { GridSize } from "@material-ui/core/Grid"; +import useDebounce from "react-use/lib/useDebounce"; +import { useUpdateEffect } from "react-use"; +/* project */ +import { useInfinityScroll } from "app/hooks/useInfinityScroll"; +import CircleLoader from "app/modules/home-module/components/Loader"; +import { useStoreActions, useStoreState } from "app/state/store/hooks"; +import DeleteChartDialog from "app/components/Dialogs/deleteChartDialog"; +import { coloredEchartTypes } from "app/modules/chart-module/routes/chart-type/data"; +import ChartAddnewCard from "app/modules/home-module/components/Charts/chartAddNewCard"; +import ChartGridItem from "app/modules/home-module/components/Charts/gridItem"; +import DatasetGridItem from "app/modules/home-module/components/Datasets/gridItem"; +import ReportGridItem from "app/modules/home-module/components/Reports/gridItem"; +import DatasetAddnewCard from "../Datasets/datasetAddNewCard"; +import ReportAddnewCard from "../Reports/reportAddNewCard"; +import ColoredReportIcon from "app/assets/icons/ColoredReportIcon"; +import DeleteDatasetDialog from "app/components/Dialogs/deleteDatasetDialog"; +import DeleteReportDialog from "app/components/Dialogs/deleteReportDialog"; +import { HomepageTable } from "../Table"; + +interface Props { + sortBy: string; + searchStr: string; + tableView: boolean; + showMenuButton: boolean; + inChartBuilder?: boolean; + category?: string; + onItemClick?: (v: string) => void; + md?: GridSize; + lg?: GridSize; +} +type assetType = "chart" | "dataset" | "report"; + +export default function AssetsGrid(props: Props) { + const observerTarget = React.useRef(null); + const [cardId, setCardId] = React.useState(""); + const [loadedAssets, setLoadedAssets] = React.useState([]); + const [modalDisplay, setModalDisplay] = React.useState(false); + const [activeAssetType, setActiveAssetType] = + React.useState(null); + const [enableButton, setEnableButton] = React.useState(false); + + const token = useStoreState((state) => state.AuthToken.value); + + const limit = 15; + const [offset, setOffset] = React.useState(0); + + const { isObserved } = useInfinityScroll(observerTarget); + + const assets = useStoreState( + (state) => (state.assets.AssetGetList.crudData ?? []) as any[] + ); + const loadAssetsCount = useStoreActions( + (actions) => actions.assets.AssetsCount.fetch + ); + const assetsCount = useStoreState( + (state) => get(state, "assets.AssetsCount.data.count", 0) as number + ); + + const loadAssets = useStoreActions( + (actions) => actions.assets.AssetGetList.fetch + ); + + const loading = useStoreState((state) => state.assets.AssetGetList.loading); + + const assetsLoadSuccess = useStoreState( + (state) => state.assets.AssetGetList.success + ); + + const getFilterString = (fromZeroOffset?: boolean) => { + const value = + props.searchStr?.length > 0 + ? `"where":{"name":{"like":"${props.searchStr}.*","options":"i"}},` + : ""; + return `filter={${value}"order":"${ + props.sortBy + } desc","limit":${limit},"offset":${fromZeroOffset ? 0 : offset}}`; + }; + + const getWhereString = () => { + return props.searchStr?.length > 0 + ? `where={"name":{"like":"${props.searchStr}.*","options":"i"}}` + : ""; + }; + + const loadData = (fromZeroOffset?: boolean) => { + if (token) { + loadAssets({ + token, + storeInCrudData: true, + filterString: getFilterString(fromZeroOffset), + }); + } else { + loadAssets({ + nonAuthCall: true, + storeInCrudData: true, + filterString: getFilterString(fromZeroOffset), + }); + } + }; + + const reloadData = () => { + if (token) { + loadAssetsCount({ token, filterString: getWhereString() }); + } else { + loadAssetsCount({ nonAuthCall: true, filterString: getWhereString() }); + } + setLoadedAssets([]); + setOffset(0); + + loadData(true); + }; + + React.useEffect(() => { + //load data if intersection observer is triggered + if (assetsCount > limit) { + if (isObserved && assetsLoadSuccess) { + if (loadedAssets.length !== assetsCount) { + //update the offset value for the next load + setOffset(offset + limit); + } + } + } + }, [isObserved]); + + useUpdateEffect(() => { + if (offset === 0) { + return; + } + loadData(); + }, [offset, token]); + + const handleDelete = (id: string) => { + setModalDisplay(false); + setEnableButton(false); + + console.log(activeAssetType); + + if (!id) { + return; + } + + const url = { + chart: `${process.env.REACT_APP_API}/chart/${id}`, + dataset: `${process.env.REACT_APP_API}/datasets/${id}`, + report: `${process.env.REACT_APP_API}/report/${id}`, + }[activeAssetType as assetType]; + + axios + .delete(url, { + headers: { + Authorization: `Bearer ${token}`, + }, + }) + .then(() => { + reloadData(); + setActiveAssetType(null); + }) + .catch((error) => console.log(error)); + }; + + const handleDuplicate = (id: string, assettype: assetType) => { + if (!id) { + return; + } + const url = { + chart: `${process.env.REACT_APP_API}/chart/duplicate/${id}`, + dataset: `${process.env.REACT_APP_API}/dataset/duplicate/${id}`, + report: `${process.env.REACT_APP_API}/report/duplicate/${id}`, + }[assettype]; + axios + .get(url, { + headers: { + Authorization: `Bearer ${token}`, + }, + }) + .then(() => { + reloadData(); + }) + .catch((error) => console.log(error)); + }; + + const handleInputChange = (e: React.ChangeEvent) => { + if (e.target.value === "DELETE") { + setEnableButton(true); + } else { + setEnableButton(false); + } + }; + + const handleModal = (id: string) => { + setCardId(id); + setModalDisplay(true); + }; + + const getIcon = (vizType: string) => { + const type = find(coloredEchartTypes(), { id: vizType }); + if (type) { + return type.icon; + } + return coloredEchartTypes()[0].icon; + }; + + React.useEffect(() => { + if (!assetsLoadSuccess) { + return; + } + //update the loaded reports + setLoadedAssets((prevAssets) => { + const prevAssetsIds = prevAssets.map((c) => c.id); + const f = assets.filter((asset) => !prevAssetsIds.includes(asset.id)); + return [...prevAssets, ...f]; + }); + }, [assetsLoadSuccess]); + + React.useEffect(() => { + reloadData(); + }, [props.sortBy, token]); + + const [,] = useDebounce( + () => { + if (props.searchStr !== undefined) { + reloadData(); + } + }, + 500, + [props.searchStr] + ); + + return ( + <> + {props.tableView ? ( + { + if (data.assetType === "chart") { + return { + id: data.id, + name: data.name, + description: data.title, + createdDate: data.createdDate, + type: data.assetType, + }; + } else if (data.assetType === "dataset") { + return { + id: data.id, + name: data.name, + description: data.description, + createdDate: data.createdDate, + type: data.assetType, + }; + } + + return { + id: data.id, + name: data.name, + description: data.title, + createdDate: data.createdDate, + type: data.assetType, + }; + })} + /> + ) : ( + + {loadedAssets.map((d, index) => ( + + { + { + chart: ( + { + setActiveAssetType(d.assetType as assetType); + handleModal(d.id); + }} + handleDuplicate={() => + handleDuplicate(d.id, d.assetType as assetType) + } + owner={d.owner} + isAIAssisted={d.isAIAssisted} + /> + ), + dataset: ( + { + setActiveAssetType(d.assetType as assetType); + handleModal(d.id); + }} + descr={d.description} + handleDuplicate={() => { + handleDuplicate(d.id, d.assetType as assetType); + }} + showMenu={!props.inChartBuilder} + id={d.id} + owner={d.owner} + inChartBuilder={props.inChartBuilder as boolean} + /> + ), + report: ( + } + color={d.backgroundColor} + showMenuButton={props.showMenuButton} + handleDelete={() => { + setActiveAssetType(d.assetType as assetType); + handleModal(d.id); + }} + handleDuplicate={() => + handleDuplicate(d.id, d.assetType as assetType) + } + title={d.title || d.name} + owner={d.owner} + /> + ), + }[d.assetType as assetType] + } + + + + ))} + + )} + + + +
    + {loading && } + + { + { + chart: ( + + ), + dataset: ( + + ), + report: ( + + ), + }[activeAssetType as assetType] + } + + ); +} diff --git a/src/app/modules/home-module/components/Breadcrumbs/index.tsx b/src/app/modules/home-module/components/Breadcrumbs/index.tsx new file mode 100644 index 000000000..9e5b45b9d --- /dev/null +++ b/src/app/modules/home-module/components/Breadcrumbs/index.tsx @@ -0,0 +1,81 @@ +import { KeyboardArrowRight } from "@material-ui/icons"; +import React from "react"; +import { Link } from "react-router-dom"; + +interface BreadCrumbsProps { + items: { path?: string; title: React.ReactNode }[]; +} + +function BreadCrumbs(props: BreadCrumbsProps) { + return ( +
    + {props.items.map((item, index) => ( +
    + {index === props.items.length - 1 ? ( + + {item.title} + + ) : ( + + {item.title} + + )} + + {index < props.items.length - 1 && ( + + )} +
    + ))} +
    + ); +} + +export default BreadCrumbs; diff --git a/src/app/modules/home-module/components/Charts/chartAddNewCard.tsx b/src/app/modules/home-module/components/Charts/chartAddNewCard.tsx index 4eb233fb3..97d274b74 100644 --- a/src/app/modules/home-module/components/Charts/chartAddNewCard.tsx +++ b/src/app/modules/home-module/components/Charts/chartAddNewCard.tsx @@ -2,7 +2,7 @@ import React from "react"; import Grid from "@material-ui/core/Grid"; import { useHistory } from "react-router-dom"; import IconButton from "@material-ui/core/IconButton"; -import { ReactComponent as AddNewImage } from "app/modules/home-module/assets/add-img.svg"; +import { ReactComponent as AddNewImage } from "app/modules/home-module/assets/add-img-white.svg"; export default function ChartAddnewCard() { const history = useHistory(); @@ -18,10 +18,11 @@ export default function ChartAddnewCard() { css={` width: 296px; height: 161.59px; - background: #f2f7fd; + background: #6061e5; padding: 12px 16px; box-shadow: 0px 4px 20px 0px rgba(0, 0, 0, 0.1); display: flex; + border-radius: 8px; align-items: center; cursor: pointer; `} @@ -44,28 +45,25 @@ export default function ChartAddnewCard() {

    - New Chart{" "} -

    -

    - Create a new chart in your library + Create Chart

    diff --git a/src/app/modules/home-module/components/Charts/chartsGrid.tsx b/src/app/modules/home-module/components/Charts/chartsGrid.tsx index 909abcbdb..ef90a2044 100644 --- a/src/app/modules/home-module/components/Charts/chartsGrid.tsx +++ b/src/app/modules/home-module/components/Charts/chartsGrid.tsx @@ -210,9 +210,9 @@ export default function ChartsGrid(props: Props) { <> {!props.tableView && ( - {props.addCard && isAuthenticated ? : null} + {props.addCard ? : null} {loadedCharts.map((c, index) => ( - + )} - +
    {loading && }

    - New Data{" "} -

    -

    - Upload data in your library + Connect Data{" "}

    diff --git a/src/app/modules/home-module/components/Datasets/datasetsGrid.tsx b/src/app/modules/home-module/components/Datasets/datasetsGrid.tsx index 9b9328956..192a21638 100644 --- a/src/app/modules/home-module/components/Datasets/datasetsGrid.tsx +++ b/src/app/modules/home-module/components/Datasets/datasetsGrid.tsx @@ -28,7 +28,6 @@ interface Props { onItemClick?: (v: string) => void; md?: GridSize; lg?: GridSize; - fromHome?: boolean; } export default function DatasetsGrid(props: Readonly) { @@ -136,8 +135,7 @@ export default function DatasetsGrid(props: Readonly) { setEnableButton(false); }; - const handleDuplicate = (index: number) => { - const id = loadedDatasets[index].id; + const handleDuplicate = (id: string) => { if (!id) { return; } @@ -210,7 +208,7 @@ export default function DatasetsGrid(props: Readonly) { <> {!props.tableView && ( - {props.addCard && isAuthenticated ? : null} + {props.addCard ? : null} {loadedDatasets?.map((data, index) => ( ) { }} descr={data.description} handleDuplicate={() => { - handleDuplicate(index); + handleDuplicate(data.id); }} showMenu={!props.inChartBuilder} id={data.id} owner={data.owner} inChartBuilder={props.inChartBuilder as boolean} - fromHome={props.fromHome} /> {!props.inChartBuilder && } @@ -274,7 +271,6 @@ export default function DatasetsGrid(props: Readonly) { {props.tableView && ( ({ @@ -282,16 +278,17 @@ export default function DatasetsGrid(props: Readonly) { name: data.name, description: data.description, createdDate: data.createdDate, + type: "dataset", }))} /> )} - +
    {loading && } ; case "HDX": return hdx-logo; + case "TGF": + return tgf-logo; default: return
    ; } diff --git a/src/app/modules/home-module/components/Datasets/gridItem.tsx b/src/app/modules/home-module/components/Datasets/gridItem.tsx index 08e64d231..0a247e8c0 100644 --- a/src/app/modules/home-module/components/Datasets/gridItem.tsx +++ b/src/app/modules/home-module/components/Datasets/gridItem.tsx @@ -27,7 +27,6 @@ interface Props { id?: string; owner: string; inChartBuilder: boolean; - fromHome?: boolean; } export default function GridItem(props: Readonly) { @@ -64,7 +63,7 @@ export default function GridItem(props: Readonly) { data-cy="dataset-grid-item" > ) {
    >; tableView: boolean; terminateSearch?: () => void; + searchInputWidth?: string; + openSearch: boolean; + setOpenSearch: React.Dispatch>; }> ) { const inputRef = React.useRef(null); @@ -29,7 +32,6 @@ export default function Filter( setSortPopoverAnchorEl(null); }; const openSortPopover = Boolean(sortPopoverAnchorEl); - const [openSearch, setOpenSearch] = React.useState(false); const sortOptions = [ { label: "Last updated", value: "updatedDate" }, { label: "Created date", value: "createdDate" }, @@ -55,7 +57,7 @@ export default function Filter( gap: 8px; `} > -
    +
    { props.setSearchValue(""); props.terminateSearch && props.terminateSearch(); - setOpenSearch(false); + props.setOpenSearch(false); }} css={` &:hover { @@ -86,10 +88,10 @@ export default function Filter( { - setOpenSearch(true); + props.setOpenSearch(true); inputRef.current?.focus(); }} - css={iconButtonCss(openSearch)} + css={iconButtonCss(props.openSearch)} > diff --git a/src/app/modules/home-module/components/Footer/smallFooter.tsx b/src/app/modules/home-module/components/Footer/smallFooter.tsx new file mode 100644 index 000000000..b131764ab --- /dev/null +++ b/src/app/modules/home-module/components/Footer/smallFooter.tsx @@ -0,0 +1,80 @@ +import React from "react"; +import moment from "moment"; +import { ReactComponent as CopyIcon } from "app/modules/home-module/components/Footer/asset/copy.svg"; +import { Container } from "@material-ui/core"; + +function SmallFooter() { + return ( + <> +
    +
    + +
    +

    + + {moment(new Date()).format("YYYY")} DataXplorer All Rights + Reserved +

    +

    + + Privacy + +

    +

    + + Terms and conditions + +

    +
    +
    +
    + + ); +} + +export default SmallFooter; diff --git a/src/app/modules/home-module/components/Reports/gridItem.tsx b/src/app/modules/home-module/components/Reports/gridItem.tsx index 4b7dc29f2..ea3bebe63 100644 --- a/src/app/modules/home-module/components/Reports/gridItem.tsx +++ b/src/app/modules/home-module/components/Reports/gridItem.tsx @@ -46,11 +46,11 @@ export default function gridItem(props: Props) {

    - New Report{" "} -

    -

    - Create a new report in your library + Create Report

    diff --git a/src/app/modules/home-module/components/Reports/reportsGrid.tsx b/src/app/modules/home-module/components/Reports/reportsGrid.tsx index 830db39da..fcd207cbe 100644 --- a/src/app/modules/home-module/components/Reports/reportsGrid.tsx +++ b/src/app/modules/home-module/components/Reports/reportsGrid.tsx @@ -198,7 +198,7 @@ export default function ReportsGrid(props: Props) { <> {!props.tableView && ( - {props.addCard && isAuthenticated ? : null} + {props.addCard ? : null} {loadedReports.map((data, index) => ( )} - +
    {loading && } void; - fromHome?: boolean; + all?: boolean; }) { const history = useHistory(); - const display = useRecoilState(homeDisplayAtom)[0]; - const pathBase = { - data: "dataset", - charts: "chart", - reports: "report", - }; return ( - Name - Description + Name + {props.all && Type} + Description Creation date @@ -89,10 +84,8 @@ export function HomepageTable(props: { onClick={() => { if (!props.inChartBuilder) { history.push( - `/${pathBase[display]}/${data.id}${ - display === "data" - ? `/detail?fromHome=${props.fromHome}` - : "" + `/${data.type}/${data.id}${ + data.type === "dataset" ? `/detail?` : "" }` ); } else if (props.inChartBuilder && props.onItemClick) { @@ -108,6 +101,7 @@ export function HomepageTable(props: { > {index + 1} {data.name} + {props.all && {_.capitalize(data.type)}} {data.description} {moment(data.createdDate).format("MMMM YYYY")} diff --git a/src/app/modules/home-module/index.tsx b/src/app/modules/home-module/index.tsx index acab49118..ef0bf87ab 100644 --- a/src/app/modules/home-module/index.tsx +++ b/src/app/modules/home-module/index.tsx @@ -7,21 +7,14 @@ import { useRecoilState, useResetRecoilState } from "recoil"; import { Box, Grid, Container, IconButton, Popover } from "@material-ui/core"; /* project */ import { Tab } from "app/components/Styled/tabs"; -import { socialAuth } from "app/utils/socialAuth"; import HomeFooter from "app/modules/home-module/components/Footer"; import ChartsGrid from "app/modules/home-module/components/Charts/chartsGrid"; import ReportsGrid from "app/modules/home-module/components/Reports/reportsGrid"; -import DatasetDetailImage from "app/modules/home-module/assets/dataset-detail.png"; import DatasetsGrid from "app/modules/home-module/components/Datasets/datasetsGrid"; import { ReactComponent as SortIcon } from "app/modules/home-module/assets/sort-fill.svg"; import { ReactComponent as GridIcon } from "app/modules/home-module/assets/grid-fill.svg"; import { ReactComponent as CloseIcon } from "app/modules/home-module/assets/close-icon.svg"; import { ReactComponent as SearchIcon } from "app/modules/home-module/assets/search-fill.svg"; -import { ReactComponent as GoogleIcon } from "app/modules/onboarding-module/asset/google-img.svg"; -import { ReactComponent as LinkedInIcon } from "app/modules/onboarding-module/asset/linkedIn-img.svg"; -import { ReactComponent as TopRightEllipse } from "app/modules/home-module/assets/top-right-ellipse.svg"; -import { ReactComponent as BottomLeftEllipse } from "app/modules/home-module/assets/bottom-left-ellipse.svg"; -import { ReactComponent as BottomRightEllipse } from "app/modules/home-module/assets/bottom-right-ellipse.svg"; import { homeDisplayAtom, persistedReportStateAtom, @@ -29,10 +22,6 @@ import { unSavedReportPreviewModeAtom, } from "app/state/recoil/atoms"; import { - TopRightEllipseCss, - bottomLeftEllipseCss, - bottomRightEllipseCss, - datsetDetailImgcss, featuredAssetsCss, iconButtonCss, rowFlexCss, @@ -40,11 +29,16 @@ import { sortByItemCss, turnsDataCss, } from "app/modules/home-module/style"; +import DatasetCategoryList from "./components/Datasets/datasetCategoryList"; +import { datasetCategories } from "../dataset-upload-module/upload-steps/metaData"; +import AssetsGrid from "./components/All/assetsGrid"; +import BreadCrumbs from "./components/Breadcrumbs"; +import SmallFooter from "./components/Footer/smallFooter"; export default function HomeModule() { useTitle("DX DataXplorer"); - const { isAuthenticated } = useAuth0(); + const { isAuthenticated, user } = useAuth0(); // clear persisted states const clearPersistedReportState = useResetRecoilState( @@ -60,6 +54,8 @@ export default function HomeModule() { setReportPreviewMode(false); }, []); + const [categories, setCategories] = React.useState([]); + const [tableView, setTableView] = React.useState(false); const [searchValue, setSearchValue] = React.useState( undefined @@ -81,7 +77,7 @@ export default function HomeModule() { { label: "Name", value: "name" }, ]; - const handleChange = (newValue: "data" | "charts" | "reports") => { + const handleChange = (newValue: "all" | "data" | "charts" | "reports") => { setDisplay(newValue); }; @@ -101,8 +97,7 @@ export default function HomeModule() { sortBy={sortByStr} searchStr={searchStr} tableView={tableView} - addCard - fromHome + categories={categories} /> ); case "charts": @@ -111,7 +106,6 @@ export default function HomeModule() { sortBy={sortByStr} searchStr={searchStr} tableView={tableView} - addCard /> ); case "reports": @@ -121,7 +115,15 @@ export default function HomeModule() { searchStr={searchStr} tableView={tableView} showMenuButton={false} - addCard + /> + ); + case "all": + return ( + ); default: @@ -141,145 +143,80 @@ export default function HomeModule() { const openSortPopover = Boolean(sortPopoverAnchorEl); React.useEffect(() => { - if (display === "data") { + if (display === "all" || display === "data") { setTabPrevPosition("left"); } else { setTabPrevPosition("right"); } }, [display]); + const descriptions = { + all: "Explore the collection of Assets", + data: "Explore the collection of Datasets used to create Charts", + charts: "Explore the collection of Charts used in Reports", + reports: "Explore the collection of Reports", + }; + return ( - -
    + - - - -
    -

    Turn data into impact with DataXplorer

    - -

    - - DataXplorer simplifies and empowers visual data reporting - for all. - -

    - - {isAuthenticated && ( -
    - - CREATE REPORT - - -
    - )} - {!isAuthenticated && ( -
    + {isAuthenticated ? ( +

    Welcome {user?.given_name ?? user?.name?.split(" ")[0]}

    + ) : ( +
    + )} - > button { - gap: 10px; - color: #fff; - display: flex; - padding: 9px 18px; - background: #a1a2ff; - align-items: center; - justify-content: center; - text-transform: uppercase; - - > svg { - transform: scale(0.8); - } - } - `} - > - - -
    - )} -
    - - + - dataset-detail-img - - - - - - -
    - - + CONNECT DATASET + + + CREATE CHART + + + CREATE REPORT + +
    +
    + -

    Featured assets in DX:

    - + + handleChange("all")} + data-cy="home-all-tab" + > + All + + handleChange("data")} + position={tabPrevPosition} data-cy="home-data-tab" > Data - + handleChange("charts")} @@ -432,7 +377,25 @@ export default function HomeModule() {
    - +
    + {descriptions[display]} +
    + {display === "data" ? ( + + ) : ( + + )}
    - - + +
    ); } diff --git a/src/app/modules/home-module/style.ts b/src/app/modules/home-module/style.ts index af2acdb48..c7901f648 100644 --- a/src/app/modules/home-module/style.ts +++ b/src/app/modules/home-module/style.ts @@ -1,20 +1,23 @@ import { css } from "styled-components/macro"; export const turnsDataCss = css` - margin-top: 60px; + /* margin-top: 60px; padding: 0; @media screen and (max-width: 768px) { justify-content: center; align-items: center; - } + } */ + display: flex; + justify-content: space-between; + align-items: center; h1 { font-family: "GothamNarrow-Bold", sans-serif; - font-weight: 700; - font-size: 40px; - line-height: 48px; - text-align: center; - color: #231d2c; + font-weight: 400; + font-size: 34px; + line-height: 42px; + /* text-align: center; */ + color: #2b3674; margin: 0; padding: 0; } @@ -32,17 +35,21 @@ export const turnsDataCss = css` button, a { - padding: 9px 27px; + padding: 8px 24px; height: 41px; - border-radius: 30px; + border-radius: 25px; outline: none; border: none; color: #ffffff; - font-family: "Inter", sans-serif; - font-weight: 700; + font-family: "GothamNarrow-Bold", sans-serif; + font-weight: 400; font-size: 14px; text-transform: uppercase; text-decoration: none; + font-size: 14px; + font-style: normal; + line-height: 24px; /* 171.429% */ + letter-spacing: -0.28px; :hover { opacity: 0.8; @@ -95,11 +102,14 @@ export const TopRightEllipseCss = css` position: absolute; `; -export const searchInputCss = (openSearch: boolean) => css` +export const searchInputCss = ( + openSearch: boolean, + width: string = "385px" +) => css` background: #dadaf8; display: flex; align-items: center; - width: 385px; + width: ${width ?? "385px"}; height: 32px; border-radius: 20px; opacity: ${openSearch ? 1 : 0}; diff --git a/src/app/modules/home-module/sub-modules/explore-assets/index.tsx b/src/app/modules/home-module/sub-modules/explore-assets/index.tsx index b53235897..37c170a14 100644 --- a/src/app/modules/home-module/sub-modules/explore-assets/index.tsx +++ b/src/app/modules/home-module/sub-modules/explore-assets/index.tsx @@ -75,6 +75,7 @@ export default function ExploreAssetsModule() { const [searchValue, setSearchValue] = React.useState( undefined ); + const [openSearch, setOpenSearch] = React.useState(false); const [sortValue, setSortValue] = React.useState("updatedDate"); const exploreViewRef = React.useRef(null); const [display, setDisplay] = useRecoilState(homeDisplayAtom); @@ -90,6 +91,7 @@ export default function ExploreAssetsModule() { }, [display]); const descriptions = { + all: "Explore the collection of Reports, Charts and Datasets", data: "Explore the collection of Datasets used to create Charts", charts: "Explore the collection of Charts used in Reports", reports: "Explore the collection of Reports", @@ -181,6 +183,8 @@ export default function ExploreAssetsModule() { setTableView={setTableView} sortValue={sortValue} tableView={tableView} + openSearch={openSearch} + setOpenSearch={setOpenSearch} /> diff --git a/src/app/modules/home-module/sub-modules/partners/components/empowerBlock.tsx b/src/app/modules/home-module/sub-modules/partners/components/empowerBlock.tsx index 55a34734d..5d3415bc3 100644 --- a/src/app/modules/home-module/sub-modules/partners/components/empowerBlock.tsx +++ b/src/app/modules/home-module/sub-modules/partners/components/empowerBlock.tsx @@ -88,7 +88,7 @@ export default function EmpowerBlock(props: { CREATE REPORT - + EXPLORE REPORTS diff --git a/src/app/modules/report-module/components/right-panel-create-view/data.tsx b/src/app/modules/report-module/components/right-panel-create-view/data.tsx index 108ba5018..13237e7e4 100644 --- a/src/app/modules/report-module/components/right-panel-create-view/data.tsx +++ b/src/app/modules/report-module/components/right-panel-create-view/data.tsx @@ -3122,15 +3122,15 @@ export const Charts = [ id: "bigNumber", icon: ( ), diff --git a/src/app/modules/report-module/index.tsx b/src/app/modules/report-module/index.tsx index c1fbd190a..3df021854 100644 --- a/src/app/modules/report-module/index.tsx +++ b/src/app/modules/report-module/index.tsx @@ -20,7 +20,7 @@ import { } from "app/modules/report-module/data"; import ReportCreateView from "app/modules/report-module/views/create"; import { ReportPreviewView } from "app/modules/report-module/views/preview"; -import { ReportInitialView } from "app/modules/report-module/views/initial"; +import ReportInitialView from "app/modules/report-module/views/initial"; import { IFramesArray } from "app/modules/report-module/views/create/data"; import { ReportRightPanel } from "app/modules/report-module/components/right-panel"; import { ReportElementsType } from "app/modules/report-module/components/right-panel-create-view"; diff --git a/src/app/modules/report-module/views/initial/index.tsx b/src/app/modules/report-module/views/initial/index.tsx index 0ebc5f6f9..90daf460c 100644 --- a/src/app/modules/report-module/views/initial/index.tsx +++ b/src/app/modules/report-module/views/initial/index.tsx @@ -27,8 +27,9 @@ import { useHistory, useParams } from "react-router-dom"; import { useStoreActions, useStoreState } from "app/state/store/hooks"; import { useMount, useTitle, useUpdateEffect } from "react-use"; import { isEmpty } from "lodash"; +import { withAuthenticationRequired } from "@auth0/auth0-react"; -export function ReportInitialView(props: Readonly) { +function ReportInitialView(props: Readonly) { useTitle("DX DataXplorer - New Report"); const history = useHistory(); @@ -294,3 +295,5 @@ export function ReportInitialView(props: Readonly) { ); } + +export default withAuthenticationRequired(ReportInitialView); diff --git a/src/app/state/api/action-reducers/assets/index.ts b/src/app/state/api/action-reducers/assets/index.ts new file mode 100644 index 000000000..f228ecd28 --- /dev/null +++ b/src/app/state/api/action-reducers/assets/index.ts @@ -0,0 +1,10 @@ +import { APIModel } from "app/state/api"; +import { ApiCallModel } from "app/state/api/interfaces"; + +export const AssetGetList: ApiCallModel = { + ...APIModel(`${process.env.REACT_APP_API}/assets`), +}; + +export const AssetsCount: ApiCallModel = { + ...APIModel(`${process.env.REACT_APP_API}/assets/count`), +}; diff --git a/src/app/state/api/interfaces/index.ts b/src/app/state/api/interfaces/index.ts index bddee9510..b0fc7bd6f 100644 --- a/src/app/state/api/interfaces/index.ts +++ b/src/app/state/api/interfaces/index.ts @@ -287,6 +287,10 @@ export interface StoreModel { ExternalDatasetDownload: ApiCallModel; }; + assets: { + AssetGetList: ApiCallModel; + AssetsCount: ApiCallModel; + }; charts: { ChartGet: ApiCallModel; ChartGetInReport: ApiCallModel; diff --git a/src/app/state/recoil/atoms/index.ts b/src/app/state/recoil/atoms/index.ts index 45b4cd706..925009a78 100644 --- a/src/app/state/recoil/atoms/index.ts +++ b/src/app/state/recoil/atoms/index.ts @@ -59,9 +59,9 @@ export const untitledReportAtom = atom({ default: false, }); -export const homeDisplayAtom = atom<"data" | "charts" | "reports">({ +export const homeDisplayAtom = atom<"all" | "data" | "charts" | "reports">({ key: "homeDisplayAtom", - default: "reports", + default: "all", }); export const reportRightPanelViewAtom = atom< diff --git a/src/app/state/store/index.ts b/src/app/state/store/index.ts index 35ed0490e..06dc45abc 100644 --- a/src/app/state/store/index.ts +++ b/src/app/state/store/index.ts @@ -129,6 +129,7 @@ import { ReportUpdate, ReportsCount, } from "app/state/api/action-reducers/reports"; +import { AssetGetList, AssetsCount } from "../api/action-reducers/assets"; const storeContent: StoreModel = { // global search @@ -240,6 +241,10 @@ const storeContent: StoreModel = { DatasetCount: persist(DatasetCount), DatasetCreate: persist(DatasetCreate), }, + assets: { + AssetGetList: AssetGetList, + AssetsCount: AssetsCount, + }, charts: { ChartGet: persist(ChartGet), ChartGetInReport: persist(ChartGetInReport), diff --git a/yarn.lock b/yarn.lock index 7fbe7f0df..a95a71521 100644 --- a/yarn.lock +++ b/yarn.lock @@ -28,6 +28,18 @@ resolved "https://registry.yarnpkg.com/@auth0/auth0-spa-js/-/auth0-spa-js-2.1.2.tgz#2217db13ce0feb480a190ed165b36681bd48633c" integrity sha512-xdA65Z/U7++Y7L9Uwh8Q8OVOs6qgFz+fb7GAzHFjpr1icO37B//xdzLXm7ZRgA19RWrsNe1nme3h896igJSvvw== +"@azure/msal-browser@^3.16.0": + version "3.16.0" + resolved "https://registry.yarnpkg.com/@azure/msal-browser/-/msal-browser-3.16.0.tgz#f1e2dc957006783b29816273d531761116215a33" + integrity sha512-WKobvIisBK7sFSOwHuchH9tUMekwhJRLgLE9tKhIq0wFYGRcVGK0KivP5vZrobVZEMNCZWto0fI1VcSVoa+cig== + dependencies: + "@azure/msal-common" "14.11.0" + +"@azure/msal-common@14.11.0": + version "14.11.0" + resolved "https://registry.yarnpkg.com/@azure/msal-common/-/msal-common-14.11.0.tgz#a34483ae6b1009d0058472f2c5db4569071dc51a" + integrity sha512-B6+IKLFs7Lsr06vjX8dPN61ENpTgiFrHf+CVo1UasHcmk5uEOq5D4thrbjsauKX+xtFryYsCDtznVDmWS4/sCg== + "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.16.0", "@babel/code-frame@^7.16.7", "@babel/code-frame@^7.8.3": version "7.16.7" resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz"