From a0c6ba5c3a661b17a1a11b5668e8fd95579b55c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nikolas=20G=C3=B6rlitz?= Date: Sun, 14 Apr 2024 10:46:20 +0200 Subject: [PATCH] add user filter --- .../20221115171242-create-user-table.ts | 1 - backend/src/models/User.ts | 18 ++++- backend/src/models/UserData.ts | 16 +++++ backend/src/models/UserSession.ts | 19 +++++- .../components/ui/UserFilter/UserFilter.tsx | 57 ++++++++++++++++ .../ui/UserFilter/UserFilter.types.tsx | 37 ++++++++++ frontend/src/models/UserModel.ts | 2 + .../create/MentorGroupCreate.view.tsx | 68 ++++--------------- .../create/_types/MGCUsersTable.types.tsx | 6 +- .../_modals/CAVRequestTraining.modal.tsx | 4 +- 10 files changed, 164 insertions(+), 64 deletions(-) create mode 100644 frontend/src/components/ui/UserFilter/UserFilter.tsx create mode 100644 frontend/src/components/ui/UserFilter/UserFilter.types.tsx diff --git a/backend/db/migrations/20221115171242-create-user-table.ts b/backend/db/migrations/20221115171242-create-user-table.ts index 776674b..c227272 100644 --- a/backend/db/migrations/20221115171242-create-user-table.ts +++ b/backend/db/migrations/20221115171242-create-user-table.ts @@ -27,7 +27,6 @@ export const USER_TABLE_ATTRIBUTES = { refresh_token: { type: DataType.TEXT, }, - createdAt: DataType.DATE, updatedAt: DataType.DATE, }; diff --git a/backend/src/models/User.ts b/backend/src/models/User.ts index 73b4c6d..9a8e388 100644 --- a/backend/src/models/User.ts +++ b/backend/src/models/User.ts @@ -1,6 +1,6 @@ import { Model, InferAttributes, CreationOptional, InferCreationAttributes, NonAttribute, Association } from "sequelize"; import { sequelize } from "../core/Sequelize"; -import { UserData } from "./UserData"; +import { IUserData, UserData } from "./UserData"; import { UserSettings } from "./UserSettings"; import { MentorGroup } from "./MentorGroup"; import { TrainingSession } from "./TrainingSession"; @@ -18,6 +18,22 @@ import { UserSolo } from "./UserSolo"; import { USER_TABLE_ATTRIBUTES, USER_TABLE_NAME } from "../../db/migrations/20221115171242-create-user-table"; import { UserNote } from "./UserNote"; +export type IMinimalUser = Pick; +export type IUser = Omit; + +export interface ISensitiveUser { + id: number; + first_name: string; + last_name: string; + email: string; + access_token?: string; + refresh_token?: string; + createdAt?: Date; + updatedAt?: Date; + + user_data?: IUserData; +} + export class User extends Model, InferCreationAttributes> { // // Attributes diff --git a/backend/src/models/UserData.ts b/backend/src/models/UserData.ts index 4e59bf0..6dfdb77 100644 --- a/backend/src/models/UserData.ts +++ b/backend/src/models/UserData.ts @@ -3,6 +3,22 @@ import { User } from "./User"; import { sequelize } from "../core/Sequelize"; import { USER_DATA_TABLE_ATTRIBUTES, USER_DATA_TABLE_NAME } from "../../db/migrations/20221115171243-create-user-data-table"; +export interface IUserData { + user_id: number; + rating_atc: number; + rating_pilot: number; + country_code?: string; + country_name?: string; + region_code?: string; + region_name?: string; + division_code?: string; + division_name?: string; + subdivision_code?: string; + subdivision_name?: string; + createdAt?: Date; + updatedAt?: Date; +} + export class UserData extends Model, InferCreationAttributes> { // // Attributes diff --git a/backend/src/models/UserSession.ts b/backend/src/models/UserSession.ts index c89b0ff..02b17c1 100644 --- a/backend/src/models/UserSession.ts +++ b/backend/src/models/UserSession.ts @@ -1,9 +1,24 @@ import { Model, InferAttributes, CreationOptional, InferCreationAttributes, ForeignKey, NonAttribute, Association } from "sequelize"; -import { User } from "./User"; -import { DataType } from "sequelize-typescript"; +import { ISensitiveUser, IUser, User } from "./User"; import { sequelize } from "../core/Sequelize"; import { USER_SESSION_ATTRIBUTES, USER_SESSION_TABLE_NAME } from "../../db/migrations/20221115171243-create-user-session-table"; +export interface IUserSession { + id: number; + uuid: string; + browser_uuid: string; + client: string; + user_id: number; + expires_at: Date; + expires_latest: Date; + createdAt?: Date; + updatedAt?: Date; + + // The login session is pretty sensitive anyway, so we might as well add the + // sensitive user here + user?: ISensitiveUser; +} + export class UserSession extends Model, InferCreationAttributes> { // // Attributes diff --git a/frontend/src/components/ui/UserFilter/UserFilter.tsx b/frontend/src/components/ui/UserFilter/UserFilter.tsx new file mode 100644 index 0000000..e2be5eb --- /dev/null +++ b/frontend/src/components/ui/UserFilter/UserFilter.tsx @@ -0,0 +1,57 @@ +import useApi from "@/utils/hooks/useApi"; +import { Table } from "@/components/ui/Table/Table"; +import { Input } from "@/components/ui/Input/Input"; +import { useFilter } from "@/utils/hooks/useFilter"; +import { useMemo, useState } from "react"; +import { useDebounce } from "@/utils/hooks/useDebounce"; +import { fuzzySearch } from "@/utils/helper/fuzzysearch/FuzzySearchHelper"; +import UserFilterTypes from "@/components/ui/UserFilter/UserFilter.types"; +import { RenderIf } from "@/components/conditionals/RenderIf"; +import { IMinimalUser } from "@models/User"; + +interface IUserFilterProps { + onUserSelect: (user: IMinimalUser) => any; + removeIDs?: number[]; +} + +function filterFunction(user: IMinimalUser, searchValue: string) { + return fuzzySearch(searchValue, [user.first_name + " " + user.last_name, user.id.toString()]).length > 0; +} + +export function UserFilter(props: IUserFilterProps) { + const { data: users, loading: loadingUsers } = useApi({ + url: "/administration/user/min", + method: "GET", + }); + + const [searchValue, setSearchValue] = useState(""); + const debouncedSearchValue = useDebounce(searchValue); + + const filteredData = useFilter( + users?.filter(u => !props.removeIDs?.includes(u.id)) ?? [], + searchValue, + debouncedSearchValue, + filterFunction, + true + ); + + return ( + <> + setSearchValue(e.target.value)} /> + + + } + /> + + ); +} diff --git a/frontend/src/components/ui/UserFilter/UserFilter.types.tsx b/frontend/src/components/ui/UserFilter/UserFilter.types.tsx new file mode 100644 index 0000000..2c374d8 --- /dev/null +++ b/frontend/src/components/ui/UserFilter/UserFilter.types.tsx @@ -0,0 +1,37 @@ +import { TableColumn } from "react-data-table-component"; +import { Button } from "@/components/ui/Button/Button"; +import { COLOR_OPTS, ICON_SIZE_OPTS, SIZE_OPTS } from "@/assets/theme.config"; +import { TbArrowRight } from "react-icons/tb"; +import { IMinimalUser } from "@models/User"; + +function getColumns(onUserSelect: (u: IMinimalUser) => any): TableColumn[] { + return [ + { + name: "CID", + selector: row => row.id.toString(), + }, + { + name: "Name", + selector: row => `${row.first_name} ${row.last_name}`, + }, + { + name: "Aktion", + cell: row => { + return ( + + ); + }, + }, + ]; +} + +export default { + getColumns, +}; diff --git a/frontend/src/models/UserModel.ts b/frontend/src/models/UserModel.ts index b528d19..6233fad 100644 --- a/frontend/src/models/UserModel.ts +++ b/frontend/src/models/UserModel.ts @@ -6,6 +6,8 @@ import { TrainingRequestModel } from "@/models/TrainingRequestModel"; import { TrainingSessionModel } from "@/models/TrainingSessionModel"; import { TrainingLogModel } from "@/models/TrainingSessionBelongsToUser.model"; +export type MinimalUser = Pick; + export type UserModel = { id: number; first_name: string; diff --git a/frontend/src/pages/administration/lm/mentor-group/create/MentorGroupCreate.view.tsx b/frontend/src/pages/administration/lm/mentor-group/create/MentorGroupCreate.view.tsx index edd3d09..ed5b737 100644 --- a/frontend/src/pages/administration/lm/mentor-group/create/MentorGroupCreate.view.tsx +++ b/frontend/src/pages/administration/lm/mentor-group/create/MentorGroupCreate.view.tsx @@ -7,7 +7,7 @@ import { COLOR_OPTS, SIZE_OPTS } from "@/assets/theme.config"; import { Separator } from "@/components/ui/Separator/Separator"; import React, { FormEvent, useState } from "react"; import FormHelper from "../../../../../utils/helper/FormHelper"; -import { UserModel } from "@/models/UserModel"; +import { MinimalUser, UserModel } from "@/models/UserModel"; import { Table } from "@/components/ui/Table/Table"; import { MentorGroupModel } from "@/models/MentorGroupModel"; import ToastHelper from "../../../../../utils/helper/ToastHelper"; @@ -18,9 +18,10 @@ import { CommonConstants, CommonRegexp } from "@/core/Config"; import { useUserSelector } from "@/app/features/authSlice"; import MGCUsersTableTypes from "@/pages/administration/lm/mentor-group/create/_types/MGCUsersTable.types"; import { axiosInstance } from "@/utils/network/AxiosInstance"; +import { UserFilter } from "@/components/ui/UserFilter/UserFilter"; export interface IUserInMentorGroup { - user: UserModel; + user: MinimalUser; admin: boolean; can_manage: boolean; } @@ -29,7 +30,7 @@ export function MentorGroupCreateView() { const navigate = useNavigate(); const user = useUserSelector(); - const defaultUser: IUserInMentorGroup = { user: user ?? ({} as UserModel), admin: true, can_manage: true }; + const defaultUser: IUserInMentorGroup = { user: user ?? ({} as MinimalUser), admin: true, can_manage: true }; const [newUserID, setNewUserID] = useState(""); const [loadingUser, setLoadingUser] = useState(false); const [users, setUsers] = useState([defaultUser]); @@ -64,36 +65,13 @@ export function MentorGroupCreateView() { .finally(() => setSubmitting(false)); } - function addUser() { - setLoadingUser(true); - - axiosInstance - .get(`/administration/user/data/basic`, { - params: { user_id: newUserID }, - }) - .then((res: AxiosResponse) => { - const newUser = { - user: res.data as UserModel, - admin: false, - can_manage: false, - }; - setUsers([...users, newUser]); - }) - .catch(() => { - ToastHelper.error(`Fehler beim Laden des Benutzers mit der ID ${newUserID}`); - }) - .finally(() => { - setLoadingUser(false); - setNewUserID(""); - }); - } - - function removeUser(user: UserModel) { - const newUsers = users.filter(u => { - return u.user.id != user.id; - }); - - setUsers(newUsers); + function addUser(u: MinimalUser) { + const newUser = { + user: { ...u }, + admin: false, + can_manage: false, + }; + setUsers([...users, newUser]); } return ( @@ -140,29 +118,7 @@ export function MentorGroupCreateView() { - setNewUserID(e.target.value)} - regex={CommonRegexp.CID} - maxLength={CommonConstants.CID_MAX_LEN} - label={"Benutzer Hinzufügen"} - labelSmall - inputError={users.length == 0} - preIcon={} - placeholder={users[0]?.user.id.toString() ?? "1373921"} - /> - - + u.user.id)} /> diff --git a/frontend/src/pages/administration/lm/mentor-group/create/_types/MGCUsersTable.types.tsx b/frontend/src/pages/administration/lm/mentor-group/create/_types/MGCUsersTable.types.tsx index 11530fc..9964f98 100644 --- a/frontend/src/pages/administration/lm/mentor-group/create/_types/MGCUsersTable.types.tsx +++ b/frontend/src/pages/administration/lm/mentor-group/create/_types/MGCUsersTable.types.tsx @@ -81,7 +81,11 @@ function getColumns(my_user_id: number | undefined, users: IUserInMentorGroup[], { name: "Aktion", cell: row => { - if (isEditing && row.user.id != my_user_id) { + if (row.user.id == my_user_id) { + return <>; + } + + if (isEditing) { return (