Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/filter search #183

Open
wants to merge 14 commits into
base: staging
Choose a base branch
from
30,294 changes: 20,577 additions & 9,717 deletions radlab-ui/webapp/package-lock.json

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion radlab-ui/webapp/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,10 @@
"firebase-admin": "^11.0.1",
"formik": "^2.2.9",
"hcl2-parser": "^1.0.3",
"heroicons": "^2.0.18",
"lodash": "^4.17.21",
"lodash.debounce": "^4.0.8",
"lunr": "^2.3.9",
"next": "^12.0.8",
"next-i18next": "^10.2.0",
"next-seo": "^4.29.0",
Expand Down Expand Up @@ -91,4 +93,4 @@
"autoprefixer": "10.4.5"
},
"license": "ISC"
}
}
9 changes: 6 additions & 3 deletions radlab-ui/webapp/public/locales/en/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@
"access-denied": "Access Denied!",
"access-denied-message": "You are not authorized to use this part of the application",
"gcp-admin": "Please check with your GCP admin to proceed further",
"delete-deployment": "Are you sure you want to delete the deployment?",
"delete-deployment-title": "Delete Deployment",
"delete-deployment-message": "Are you sure you want to delete the deployment(s)?",
"close": "Close",
"deployment-id": "Deployment ID:",
"available-modules": "Available Modules",
Expand Down Expand Up @@ -76,5 +77,7 @@
"loading": "Loading",
"no-modules-message": "If you are not seeing the module you want, reach out to your RAD Lab Admin",
"build-status": "Build Status",
"history": "History"
}
"history": "History",
"modules": "Modules",
"clear": "Clear"
}
9 changes: 6 additions & 3 deletions radlab-ui/webapp/public/locales/es/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@
"access-denied": "Acceso denegado!",
"access-denied-message": "No está autorizado para usar esta aplicación.",
"gcp-admin": "Consulte con su administrador de GCP para continuar",
"delete-deployment": "¿Estás segura de que quieres eliminar el ID de despliegue?",
"delete-deployment": "Eliminar implementación",
"delete-deployment-message": "¿Está seguro de que desea eliminar las implementaciones?",
"close": "Cerca",
"deployment-id": "Id. de implementación:",
"available-modules": "Módulos disponibles",
Expand Down Expand Up @@ -76,5 +77,7 @@
"loading": "Cargando",
"no-modules-message": "Si no ve el módulo que desea, comuníquese con su administrador de RAD Lab",
"build-status": "Estado de construcción",
"history": "Historia"
}
"history": "Historia",
"modules": "Módulos",
"clear": "claro"
}
78 changes: 59 additions & 19 deletions radlab-ui/webapp/src/components/DeleteDeploymentModal.tsx
Original file line number Diff line number Diff line change
@@ -1,43 +1,54 @@
import axios from "axios"
import { useTranslation } from "next-i18next"
import { useState } from "react"
import { useNavigate } from "react-router-dom"
import { alertStore, userStore } from "@/store"
import { ALERT_TYPE } from "@/utils/types"
import Loading from "@/navigation/Loading"
import { XCircleIcon } from "@heroicons/react/outline"

interface IDeleteDeploymentModal {
deployId: string
deployId?: string
deploymentIds?: string[]
handleClick: Function
handleRefresh?: Function
}

const DeleteDeploymentModal: React.FC<IDeleteDeploymentModal> = ({
deployId,
deploymentIds,
handleClick,
handleRefresh,
}) => {
const [modal, setModal] = useState(true)
const navigate = useNavigate()
const { t } = useTranslation()
const setAlert = alertStore((state) => state.setAlert)
const [loading, setLoading] = useState(false)
const user = userStore((state) => state.user)

const handleDelete = async () => {
const config = {
const multipleDeleteConfig = {
data: { deployedByEmail: user?.email, deploymentIds: deploymentIds },
}

const singleDeleteConfig = {
data: { deployedByEmail: user?.email },
}

setLoading(true)
await axios
.delete(`/api/deployments/${deployId}`, config)

const request = deploymentIds?.length
? axios.delete(`/api/deployments`, multipleDeleteConfig)
: axios.delete(`/api/deployments/${deployId}`, singleDeleteConfig)

request
.then((res) => {
if (res.status === 200) {
setAlert({
message: t("delete-success"),
durationMs: 10000,
type: ALERT_TYPE.SUCCESS,
})
navigate("/deployments")
handleRefresh && handleRefresh(true)
} else {
setAlert({
message: t("delete-error"),
Expand All @@ -58,6 +69,8 @@ const DeleteDeploymentModal: React.FC<IDeleteDeploymentModal> = ({
})
.finally(() => {
setLoading(false)
setModal(false)
handleClick(false)
})
}

Expand All @@ -72,27 +85,54 @@ const DeleteDeploymentModal: React.FC<IDeleteDeploymentModal> = ({
/>
<div className="modal">
<div className="modal-box">
<h3 className="font-bold text-lg">{t("delete")}</h3>
<p className="py-4">{t("delete-deployment")}</p>
<p className="text-md font-normal">
{`${t("deployment-id")} ${deployId}`}
<div className="flex justify-center">
<XCircleIcon className="w-12 h-12 text-error" />
</div>
<h3 className="font-bold text-lg text-center">
{t("delete-deployment-title")}
</h3>
<hr className="border border-base-200 mt-2" />
<p className="p-1 bg-error bg-opacity-10 text-sm rounded-md mt-4 text-center text-error font-semibold">
{t("delete-deployment-message")}
</p>
<div className="flex justify-center mt-2">
{loading && <Loading />}
</div>
{deploymentIds?.length ? (
<>
{deploymentIds.map((deploymentId) => {
return (
<p
className="text-sm font-normal mt-6 text-center font-semibold"
key={deploymentId}
>
{`${t("deployment-id")} ${deploymentId}`}
</p>
)
})}
</>
) : (
<p className="text-sm font-normal mt-6 text-center font-semibold">
{`${t("deployment-id")} ${deployId}`}
</p>
)}
<div className="modal-action">
{loading ? (
<Loading />
) : (
<button className="btn btn-error" onClick={handleDelete}>
{t("delete")}
</button>
)}
<button
className="btn btn-outline"
className="btn btn-outline btn-sm"
onClick={() => {
setModal(false), handleClick(false)
}}
>
{t("close")}
</button>
<button
className="btn btn-error btn-sm"
onClick={() => {
handleDelete()
}}
>
{t("delete")}
</button>
</div>
</div>
</div>
Expand Down
156 changes: 156 additions & 0 deletions radlab-ui/webapp/src/components/Filter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
import Search, { ISearchResults } from "@/components/Search"
import Loading from "@/navigation/Loading"
import { deploymentStore, moduleNamesStore } from "@/store"
import { DEPLOYMENT_STATUSES } from "@/utils/data"
import { startCase } from "lodash"
import { useTranslation } from "next-i18next"
import { useEffect, useRef, useState } from "react"

type IFilterType =
| "module"
| "deploymentId"
| "deployedByEmail"
| "createdAt"
| "status"

export default function Filter({
filters,
}: {
filters: IFilterType[]
clearFilter: boolean
}) {
const deployments = deploymentStore((state) => state.deployments)
const setFilteredDeployments = deploymentStore(
(state) => state.setFilteredDeployments,
)
const inputRef = useRef(null)
const [search, setSearch] = useState<ISearchResults | null>(null)
const [status, setStatus] = useState("Deleted")
const [moduleName, setModuleName] = useState<string>("")
const statuses = DEPLOYMENT_STATUSES
const { t } = useTranslation()

const moduleNames = moduleNamesStore((state) => state.moduleNames)
const [isLoading, _] = useState<boolean>(false)

// TODO. Likely change this to the firebase.uid for the record
const refField = "id"
const handleQuery = (search: ISearchResults) => setSearch(search)

// Filter based on Selects and Search

useEffect(() => {
setStatus("Deleted")
if (!deployments) {
setFilteredDeployments(null)
return
}

//No active filtering happening
if (
!search?.query &&
status === "" &&
moduleName === ""
// category === "" &&
// classification === ""
) {
setFilteredDeployments(null)
return
}

deployments.map((deployment) => {
if (deployment.hasOwnProperty("deletedAt")) {
//@ts-ignore
deployment["status"] = "DELETED"
}
})
let filtered = deployments

if (search?.query) {
// @ts-ignore TS is wrong about possible type (can't be udefined[] because of filter(Boolean) step)
filtered = search.result
.sort((a, b) => b.score - a.score)
.map((result) => result.ref)
.map((ref) =>
deployments.find((deployment) => deployment[refField] === ref),
)
.filter(Boolean)
}

setFilteredDeployments(filtered)
}, [deployments, search, moduleName])

const clearAllFilters = () => {
// @ts-ignore
inputRef.current.value = ""
setSearch(null)
}

if (isLoading) return <Loading />

return (
<div className="w-full">
<Search
inputRef={inputRef}
placeholder="Search by Module or Project ID or Deployment ID"
// @ts-ignore
documents={deployments}
refField={refField}
handleQuery={handleQuery}
/>

<div className="grid grid-cols-2 lg:grid-cols-10 gap-4 mt-4">
{filters.includes("status") && (
<select
onChange={(e) => setStatus(e.target.value)}
className="select select-bordered w-full max-w-xs col-span-4"
>
<option disabled selected>
{t("status")}
</option>
{statuses.map((status) => {
const statusValue = status.toLowerCase()
return (
<>
<input
type="checkbox"
//@ts-ignore
checked="checked"
className="checkbox checkbox-sm"
/>
<option key={statusValue} value={statusValue}>
{startCase(statusValue)}
</option>
</>
)
})}
</select>
)}

{filters.includes("module") && (
<select
onChange={(e) => setModuleName(e.target.value)}
className="select select-bordered w-full max-w-xs col-span-4"
>
<option value="">{t("modules")}</option>
{moduleNames &&
moduleNames.length &&
moduleNames.map((module) => {
return (
<option key={module.name}>{startCase(module.name)}</option>
)
})}
</select>
)}
<div className="flex justify-end col-span-2">
<button
className="btn btn-link no-underline hover:no-underline btn-md bg-primary bg-opacity-80 text-base-100 hover:bg-opacity-90 hover:bg-primary"
onClick={clearAllFilters}
>
{t("clear")}
</button>
</div>
</div>
</div>
)
}
4 changes: 2 additions & 2 deletions radlab-ui/webapp/src/components/Search.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,12 @@ const Search = ({

return (
<div className="relative">
<SearchIcon className="h-6 absolute mt-3 ml-3 text-base-content" />
<SearchIcon className="h-4 absolute mt-4 ml-3 text-base-content" />
<input
ref={inputRef ?? null}
type="text"
placeholder={placeholder || "Search"}
className="input px-10 py-3 w-full"
className="input px-10 py-6 w-full input-sm"
onChange={onChange}
/>
</div>
Expand Down
Loading