Skip to content

Commit

Permalink
Merge pull request #294 from SELab-2/search-bar-fix
Browse files Browse the repository at this point in the history
Search bar changes
  • Loading branch information
Aqua-sc authored May 23, 2024
2 parents 9bd9b31 + 87c9415 commit caea261
Show file tree
Hide file tree
Showing 5 changed files with 109 additions and 50 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,6 @@ public void testRunDockerTest() throws IOException {

verify(dockerModel, times(1)).addZipInputFiles(file);
verify(dockerModel, times(1)).cleanUp();
verify(dockerModel, times(1)).addUtilFiles(extraFilesPathResolved);
assertEquals(1, filehandlerCalled.get());

/* artifacts are empty */
Expand All @@ -128,7 +127,6 @@ public void testRunDockerTest() throws IOException {
assertEquals(dockerTemplateTestOutput, result);
verify(dockerModel, times(2)).addZipInputFiles(file);
verify(dockerModel, times(2)).cleanUp();
verify(dockerModel, times(2)).addUtilFiles(extraFilesPathResolved);
assertEquals(1, filehandlerCalled.get());

/* aritifacts are null */
Expand All @@ -137,7 +135,6 @@ public void testRunDockerTest() throws IOException {
assertEquals(dockerTemplateTestOutput, result);
verify(dockerModel, times(3)).addZipInputFiles(file);
verify(dockerModel, times(3)).cleanUp();
verify(dockerModel, times(3)).addUtilFiles(extraFilesPathResolved);
assertEquals(1, filehandlerCalled.get());

/* No template */
Expand All @@ -147,7 +144,6 @@ public void testRunDockerTest() throws IOException {
assertEquals(dockerTestOutput, result);
verify(dockerModel, times(4)).addZipInputFiles(file);
verify(dockerModel, times(4)).cleanUp();
verify(dockerModel, times(4)).addUtilFiles(extraFilesPathResolved);

/* Error gets thrown */
when(dockerModel.runSubmission(testEntity.getDockerTestScript())).thenThrow(new RuntimeException("Error"));
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/i18n/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,8 @@
"search": "Search",
"emailError": "Please enter a valid email",
"emailTooShort": "Email must be at least 3 characters long",
"nameError": "Name must be at least 3 characters long",
"surnameError": "Surname must be at least 3 characters long",
"nameError": "Name or surname must be at least 3 characters long",
"surnameError": "Name or surname must be at least 3 characters long",
"searchTutorial": "Enter a name, surname, or email to find users.",
"searchTooShort": "The search must be at least 3 characters long",
"noUsersFound": "No users found",
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/i18n/nl/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,8 @@
"search": "Zoeken",
"emailError": "Vul een geldig email adres in",
"emailTooShort": "Email moet minstens 3 karakters lang zijn",
"nameError": "Naam moet minstens 3 karakters lang zijn",
"surnameError": "Achternaam moet minstens 3 karakters lang zijn",
"nameError": "Naam of achternaam moet minstens 3 karakters lang zijn",
"surnameError": "Naam of achternaam moet minstens 3 karakters lang zijn",
"searchTooShort": "Zoekopdracht moet minstens 3 karakters lang zijn",
"searchTutorial": "Vul een email adres, naam of achternaam in om gebruikers op te zoeken.",
"noUsersFound": "Geen gebruikers gevonden",
Expand Down
130 changes: 95 additions & 35 deletions frontend/src/pages/editRole/EditRole.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useContext, useEffect, useState } from "react"
import { Form, Input, Spin, Select, Typography } from "antd"
import { Form, Input, Spin, Select, Typography, Space } from "antd"
import UserList from "./components/UserList"
import { ApiRoutes, GET_Responses, UserRole } from "../../@types/requests.d"
import apiCall from "../../util/apiFetch"
Expand All @@ -10,18 +10,22 @@ import { UserContext } from "../../providers/UserProvider"
import useUser from "../../hooks/useUser"

export type UsersType = GET_Responses[ApiRoutes.USERS]
type SearchType = "name" | "surname" | "email"
type SearchType = "name" | "email"
const ProfileContent = () => {
const [users, setUsers] = useState<UsersType | null>(null)
const myself = useUser()
const [loading, setLoading] = useState(false)
const [form] = Form.useForm()
const searchValue = Form.useWatch("search", form)
const firstSearchValue = Form.useWatch("first", form)
const secondSearchValue = Form.useWatch("second", form)
const searchValue = `${firstSearchValue || ''} ${secondSearchValue || ''}`.trim();
const [debouncedSearchValue] = useDebounceValue(searchValue, 250)
const [searchType, setSearchType] = useState<SearchType>("name")

const { t } = useTranslation()

const emailRegex = /^[\w-]+(\.[\w-]+)*@([\w-]+\.)+[a-zA-Z]{2,7}$/;

useEffect(() => {
onSearch()
}, [debouncedSearchValue])
Expand All @@ -43,14 +47,56 @@ const ProfileContent = () => {
})
}

const [isError, setIsError] = useState(false)

const checkValidate = () => {
if (searchType === "email") {
if (!emailRegex.test(form.getFieldValue("first"))) {
return false
} else {
return true
}
} else {
const firstValue = form.getFieldValue("first")
const secondValue = form.getFieldValue("second")
const firstValueLength = firstValue ? firstValue.length : 0
const secondValueLength = secondValue ? secondValue.length : 0
if (firstValueLength < 3 && secondValueLength < 3) {
console.log("error")
return false
} else {
console.log("no error")
return true
}
}
}

const validate = () => {
if (!checkValidate()) {
setIsError(true)
} else {
setIsError(false)
}
}

const onSearch = async () => {
const value = form.getFieldValue("search")
if (!value || value.length < 3) return
//validation
if (!checkValidate()) {
return
}

const firstValue = form.getFieldValue("first")
setLoading(true)
const params = new URLSearchParams()
params.append(searchType, form.getFieldValue("search"))
if (searchType === "email") {
params.append(searchType, form.getFieldValue("first"))
} else {
const secondValue = form.getFieldValue("second")
if (firstValue) params.append("name", firstValue)
if (secondValue) params.append("surname", secondValue)
}
console.log(params)
apiCall.get((ApiRoutes.USERS + "?" + params.toString()) as ApiRoutes.USERS).then((res) => {

setUsers(res.data)
setLoading(false)
})
Expand All @@ -62,49 +108,63 @@ const ProfileContent = () => {
form={form}
name="search"
onFinish={onSearch}
onChange={validate}
validateTrigger={[]}
>

<Form.Item>
<Space.Compact style={{ display: 'flex' }}>
<Select size="large"
value={searchType}
onChange={(value) => setSearchType(value)}
style={{ width: 120 }}
options={[
{ label: t("editRole.email"), value: "email" },
{ label: t("editRole.name"), value: "name" },
]}
/>
<Form.Item
name="search"
name="first"
rules={[
{
validator: (_, value) => {
// Validate email
if (searchType === "email") {
// Validate email
const emailRegex = /^[\w-]+(\.[\w-]+)*@([\w-]+\.)+[a-zA-Z]{2,7}$/
if (!emailRegex.test(value)) {
return Promise.reject(new Error(t("editRole.invalidEmail")))
return Promise.reject(new Error(t("editRole.invalidEmail")));
}
// Validate name
} else if (searchType === "name") {
if (value && value.length < 3) {
return Promise.reject(new Error(t("editRole.nameError")));
}
}
// Validate name and surname


return Promise.resolve()
},
},
{
message: t("editRole.searchTooShort"),
min: 3,
}
]}
noStyle
>
<Input
size="large"
addonBefore={
<Select
value={searchType}
onChange={(value) => setSearchType(value)}
style={{ width: 120 }}
options={[
{ label: t("editRole.email"), value: "email" },
{ label: t("editRole.name"), value: "name" },
{ label: t("editRole.surname"), value: "surname" },
]}
/>
}
/>
<Input size="large" placeholder={searchType === "email" ? t("editRole.email") : t("editRole.name")}/>
</Form.Item>
{searchType === "name" && (
<Form.Item
name="second"
rules={[
{
message: t("editRole.surnameError"),
min: 3,
},
]}
noStyle
>
<Input size="large" placeholder={t("editRole.surname")}/>
</Form.Item>
)}
</Space.Compact>
</Form.Item>
{isError && <Typography.Text type="danger">{searchType === "email" ? t("editRole.emailError") : t("editRole.nameError")}</Typography.Text>}
<br />
</Form>

{users !== null ? (
<>
{loading ? (
Expand Down
17 changes: 10 additions & 7 deletions frontend/src/pages/editRole/components/UserList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { useState } from "react"
import { UsersType } from "../EditRole"
import { GET_Responses, ApiRoutes } from "../../../@types/requests.d"
import { User } from "../../../providers/UserProvider"
import useUser from "../../../hooks/useUser"

//this is ugly, but if I put this in GET_responses, it will be confused with the User type (and there's no GET request with this as a response).
//this is also the only place this is used, so I think it's fine.
Expand All @@ -16,9 +17,10 @@ const UserList: React.FC<{ users: UsersType; updateRole: (user: UsersListItem, r
const [visible, setVisible] = useState(false)
const [selectedUser, setSelectedUser] = useState<UsersListItem | null>(null)
const [selectedRole, setSelectedRole] = useState<UserRole | null>(null)
const { user } = useUser()

const handleMenuClick = (user: UsersListItem, role: UserRole) => {
setSelectedUser(user)
const handleMenuClick = (listuser: UsersListItem, role: UserRole) => {
setSelectedUser(listuser)
setSelectedRole(role)
setVisible(true)
}
Expand All @@ -44,12 +46,13 @@ const UserList: React.FC<{ users: UsersType; updateRole: (user: UsersListItem, r
return a.email.localeCompare(b.email);
});

const renderUserItem = (user: UsersListItem) => (
const renderUserItem = (listuser: UsersListItem) => (
<List.Item>
<List.Item.Meta title={user.name + " " + user.surname} description={user.email} />
<List.Item.Meta title={listuser.name + " " + listuser.surname} description={listuser.email} />
<Dropdown
trigger={["click"]}
placement="bottomRight"
disabled={listuser.id === user?.id}
menu={{
items: [
{
Expand All @@ -65,13 +68,13 @@ const UserList: React.FC<{ users: UsersType; updateRole: (user: UsersListItem, r
label: t("editRole.admin"),
},
],
selectedKeys: [user.role],
onClick: (e) => handleMenuClick(user, e.key as UserRole),
selectedKeys: [listuser.role],
onClick: (e) => handleMenuClick(listuser, e.key as UserRole),
}}
>
<a onClick={(e) => e.preventDefault()}>
<Space>
{t("editRole." + user.role)}
{t("editRole." + listuser.role)}
<DownOutlined />
</Space>
</a>
Expand Down

0 comments on commit caea261

Please sign in to comment.