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

Search bar changes #294

Merged
merged 4 commits into from
May 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading