From dfdc4366ae3301c0af60a63cbcc13b8b813dcc78 Mon Sep 17 00:00:00 2001 From: Noah Hsu Date: Wed, 27 Jul 2022 22:01:40 +0800 Subject: [PATCH] feat: user add or edit --- src/components/FolderTree.tsx | 151 +++++++++++++++++++++++ src/components/index.ts | 1 + src/lang/en/test.json | 3 - src/lang/en/users.json | 9 +- src/lang/zh-cn/test.json | 3 - src/pages/manage/Profile.tsx | 41 ++----- src/pages/manage/index.tsx | 6 +- src/pages/manage/routes.tsx | 8 ++ src/pages/manage/storages/AddOrEdit.tsx | 8 +- src/pages/manage/users/AddOrEdit.tsx | 152 ++++++++++++++++++++++++ src/pages/manage/users/Users.tsx | 41 ++----- src/store/settings.ts | 1 + src/types/obj.ts | 2 +- src/types/user.ts | 58 +++++---- src/utils/path_join.ts | 5 + 15 files changed, 391 insertions(+), 98 deletions(-) create mode 100644 src/components/FolderTree.tsx delete mode 100644 src/lang/en/test.json delete mode 100644 src/lang/zh-cn/test.json create mode 100644 src/pages/manage/users/AddOrEdit.tsx diff --git a/src/components/FolderTree.tsx b/src/components/FolderTree.tsx new file mode 100644 index 000000000..117d911ef --- /dev/null +++ b/src/components/FolderTree.tsx @@ -0,0 +1,151 @@ +import { + Box, + Button, + createDisclosure, + HStack, + Icon, + Input, + Modal, + ModalBody, + ModalCloseButton, + ModalContent, + ModalFooter, + ModalHeader, + ModalOverlay, + Spinner, + Text, + VStack, +} from "@hope-ui/solid"; +import { BiSolidRightArrow } from "solid-icons/bi"; +import { + Accessor, + createContext, + createSignal, + useContext, + Show, + For, +} from "solid-js"; +import { useFetch, useT } from "~/hooks"; +import { getIconColor } from "~/store"; +import { Obj, Resp } from "~/types"; +import { baseName, handleRresp, pathJoin, r } from "~/utils"; + +export interface FolderTreeProps { + onChange: (path: string) => void; +} +const context = createContext<{ + value: Accessor; + onChange: (val: string) => void; +}>(); +export const FolderTree = (props: FolderTreeProps) => { + const [path, setPath] = createSignal("/"); + return ( + { + setPath(val); + props.onChange(val); + }, + }} + > + + + ); +}; + +const FolderTreeNode = (props: { path: string }) => { + const [children, setChildren] = createSignal([]); + const { value, onChange } = useContext(context)!; + const [loading, fetchDirs] = useFetch(() => + r.post("/fs/dirs", { path: props.path }) + ); + const load = async () => { + if (children().length > 0) return; + const resp: Resp = await fetchDirs(); + handleRresp(resp, setChildren); + }; + const { isOpen, onToggle } = createDisclosure(); + const active = () => value() === props.path; + return ( + + + }> + { + onToggle(); + if (isOpen()) { + load(); + } + }} + /> + + { + onChange(props.path); + }} + > + {props.path === "/" ? "root" : baseName(props.path)} + + + + + + {(item) => ( + + )} + + + + + ); +}; + +export const FolderChooseInput = (props: { + value: string; + onChange: (path: string) => void; +}) => { + const { isOpen, onOpen, onClose } = createDisclosure(); + const t = useT(); + return ( + <> + + + + + + {t("global.choose_folder")} + + + + + + + + + + ); +}; diff --git a/src/components/index.ts b/src/components/index.ts index 56c13e592..bfb410051 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -2,3 +2,4 @@ export * from "./FullLoading"; export * from "./SwitchColorMode"; export * from "./SwitchLanguage"; export * from "./Hello"; +export * from "./FolderTree" diff --git a/src/lang/en/test.json b/src/lang/en/test.json deleted file mode 100644 index b3d0094f7..000000000 --- a/src/lang/en/test.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "hello": "hello, world" -} \ No newline at end of file diff --git a/src/lang/en/users.json b/src/lang/en/users.json index f9a46871a..1cd72e0cc 100644 --- a/src/lang/en/users.json +++ b/src/lang/en/users.json @@ -1,5 +1,5 @@ { - "permission": { + "permissions": { "see_hides": "Can see hides", "access_without_password": "Access without password", "add_aria2": "Add aria2 tasks", @@ -10,5 +10,10 @@ "remove": "Delete", "webdav_read": "Webdav read", "webdav_manage": "Webdav manage" - } + }, + "username": "Username", + "password": "Pssword", + "base_path": "Base path", + "role": "Role", + "permission": "Permission" } diff --git a/src/lang/zh-cn/test.json b/src/lang/zh-cn/test.json deleted file mode 100644 index 2ff47c677..000000000 --- a/src/lang/zh-cn/test.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "hello": "你好,世界" -} \ No newline at end of file diff --git a/src/pages/manage/Profile.tsx b/src/pages/manage/Profile.tsx index eddbac4a6..4c7723931 100644 --- a/src/pages/manage/Profile.tsx +++ b/src/pages/manage/Profile.tsx @@ -10,10 +10,10 @@ import { SimpleGrid, VStack, } from "@hope-ui/solid"; -import { createSignal, JSXElement } from "solid-js"; +import { createSignal, For, JSXElement } from "solid-js"; import { useFetch, useRouter, useT } from "~/hooks"; import { setUser, user } from "~/store"; -import { UserMethods } from "~/types"; +import { UserMethods, UserPermissions } from "~/types"; import { handleRresp, notify, r } from "~/utils"; const PermissionBadge = (props: { can: boolean; children: JSXElement }) => { @@ -77,36 +77,13 @@ const Profile = () => { {t("global.save")} - - {t("users.permission.see_hides")} - - - {t("users.permission.access_without_password")} - - - {t("users.permission.add_aria2")} - - - {t("users.permission.write")} - - - {t("users.permission.rename")} - - - {t("users.permission.move")} - - - {t("users.permission.copy")} - - - {t("users.permission.remove")} - - - {t("users.permission.webdav_read")} - - - {t("users.permission.webdav_manage")} - + + {(item, i) => ( + + {t(`users.permission.${item}`)} + + )} + ); diff --git a/src/pages/manage/index.tsx b/src/pages/manage/index.tsx index 0a15b928d..6addc323b 100644 --- a/src/pages/manage/index.tsx +++ b/src/pages/manage/index.tsx @@ -12,7 +12,11 @@ const Manage = () => { const t = useT(); useTitle(() => t("manage.title")); return ( - +
import("./storages/AddOrEdit")), }, + { + to: "/users/add", + component: lazy(() => import("./users/AddOrEdit")), + }, + { + to: "/users/edit/:id", + component: lazy(() => import("./users/AddOrEdit")), + } ]; const Placeholder = (props: { title: string; to: string }) => { diff --git a/src/pages/manage/storages/AddOrEdit.tsx b/src/pages/manage/storages/AddOrEdit.tsx index 5069795c3..3517316f7 100644 --- a/src/pages/manage/storages/AddOrEdit.tsx +++ b/src/pages/manage/storages/AddOrEdit.tsx @@ -39,7 +39,7 @@ const AddOrEdit = () => { const t = useT(); const { params, back } = useRouter(); const { id } = params; - const [loadingDrivers, loadDrivers] = useFetch( + const [driversLoading, loadDrivers] = useFetch( () => r.get("/admin/driver/list"), true ); @@ -49,11 +49,11 @@ const AddOrEdit = () => { handleRresp(resp, setDrivers); }; - const [loadingStorage, loadStorage] = useFetch( + const [storageLoading, loadStorage] = useFetch( () => r.get(`/admin/storage/get?id=${id}`), true ); - const [loadingDriver, loadDriver] = useFetch( + const [driverLoading, loadDriver] = useFetch( () => r.get(`/admin/driver/items?driver=${storage.driver}`), true ); @@ -81,7 +81,7 @@ const AddOrEdit = () => { }); return ( {t(`global.${id ? "edit" : "add"}`)} diff --git a/src/pages/manage/users/AddOrEdit.tsx b/src/pages/manage/users/AddOrEdit.tsx new file mode 100644 index 000000000..43d6b7620 --- /dev/null +++ b/src/pages/manage/users/AddOrEdit.tsx @@ -0,0 +1,152 @@ +import { + Button, + Checkbox, + Flex, + FormControl, + FormLabel, + Heading, + Input, + VStack, +} from "@hope-ui/solid"; +import { MaybeLoading, FolderChooseInput } from "~/components"; +import { useFetch, useRouter, useT } from "~/hooks"; +import { handleRresp, notify, r } from "~/utils"; +import { + Addition, + Resp, + Storage, + Type, + User, + UserMethods, + UserPermissions, +} from "~/types"; +import { createStore } from "solid-js/store"; +import { For } from "solid-js"; + +const Permission = (props: { + can: boolean; + onChange: (val: boolean) => void; + name: string; +}) => { + const t = useT(); + return ( + + {t(`users.permissions.${props.name}`)} + props.onChange(!props.can)} + /> + + ); +}; + +const AddOrEdit = () => { + const t = useT(); + const { params, back } = useRouter(); + const { id } = params; + const [user, setUser] = createStore({ + id: 0, + username: "", + password: "", + base_path: "", + role: 0, + permission: 0, + }); + const [userLoading, loadUser] = useFetch(() => + r.get(`/admin/user/get?id=${id}`) + ); + + const initEdit = async () => { + const resp: Resp = await loadUser(); + handleRresp(resp, setUser); + }; + if (id) { + initEdit(); + } + const [okLoading, ok] = useFetch(() => { + return r.post(`/admin/user/${id ? "update" : "create"}`, user); + }); + return ( + + + {t(`global.${id ? "edit" : "add"}`)} + + + {t(`users.username`)} + + setUser("username", e.currentTarget.value)} + /> + + + + {t(`users.password`)} + + setUser("password", e.currentTarget.value)} + /> + + + + {t(`users.base_path`)} + + setUser("base_path", path)} + /> + + + + {t(`users.permission`)} + + + + {(item, i) => ( + { + if (val) { + setUser("permission", (user.permission |= 1 << i())); + } else { + setUser("permission", (user.permission &= ~(1 << i()))); + } + }} + /> + )} + + + + + + + ); +}; + +export default AddOrEdit; diff --git a/src/pages/manage/users/Users.tsx b/src/pages/manage/users/Users.tsx index 335cdcb33..020bd0ba4 100644 --- a/src/pages/manage/users/Users.tsx +++ b/src/pages/manage/users/Users.tsx @@ -27,7 +27,7 @@ import { useT, } from "~/hooks"; import { handleRresp, notify, r } from "~/utils"; -import { PageResp, User, UserMethods } from "~/types"; +import { PageResp, UserPermissions, User, UserMethods } from "~/types"; const Role = (props: { role: number }) => { const roles = [ @@ -47,35 +47,14 @@ const Permissions = (props: { user: User }) => { const color = (can: boolean) => `$${can ? "success" : "danger"}9`; return ( - - {(item) => ( - - + + {(item, i) => ( + + )} @@ -125,7 +104,7 @@ const Users = () => { "username", "base_path", "role", - "permissions", + "permission", "operations", ]} > diff --git a/src/store/settings.ts b/src/store/settings.ts index b4425f931..3ef87ba46 100644 --- a/src/store/settings.ts +++ b/src/store/settings.ts @@ -7,3 +7,4 @@ export const setSettings = (items: Record) => { }; export const getSetting = (key: string) => settings[key]; +export const getIconColor = () => getSetting("icon_color") || "#1890ff"; diff --git a/src/types/obj.ts b/src/types/obj.ts index ada95e2af..22e62d622 100644 --- a/src/types/obj.ts +++ b/src/types/obj.ts @@ -3,6 +3,6 @@ export interface Obj { size: number; is_dir: boolean; modified: string; - sign: string; + sign?: string; raw_url?: string; } diff --git a/src/types/user.ts b/src/types/user.ts index e840dd1b8..c2f8185b4 100644 --- a/src/types/user.ts +++ b/src/types/user.ts @@ -7,33 +7,49 @@ export enum UserRole { export interface User { id: number; username: string; + password: string; base_path: string; role: UserRole; - permissions: number; + permission: number; } +export const UserPermissions = [ + "see_hides", + "access_without_password", + "add_aria2", + "write", + "rename", + "move", + "copy", + "remove", + "webdav_read", + "webdav_manage", +]; + export const UserMethods = { is_guest: (user: User) => user.role === UserRole.GUEST, is_admin: (user: User) => user.role === UserRole.ADMIN, is_general: (user: User) => user.role === UserRole.GENERAL, - can_see_hides: (user: User) => - UserMethods.is_admin(user) || (user.permissions & 1) == 1, - can_access_without_password: (user: User) => - UserMethods.is_admin(user) || ((user.permissions >> 1) & 1) == 1, - can_add_aria2_tasks: (user: User) => - UserMethods.is_admin(user) || ((user.permissions >> 2) & 1) == 1, - can_write: (user: User) => - UserMethods.is_admin(user) || ((user.permissions >> 3) & 1) == 1, - can_rename: (user: User) => - UserMethods.is_admin(user) || ((user.permissions >> 4) & 1) == 1, - can_move: (user: User) => - UserMethods.is_admin(user) || ((user.permissions >> 5) & 1) == 1, - can_copy: (user: User) => - UserMethods.is_admin(user) || ((user.permissions >> 6) & 1) == 1, - can_remove: (user: User) => - UserMethods.is_admin(user) || ((user.permissions >> 7) & 1) == 1, - can_webdav_read: (user: User) => - UserMethods.is_admin(user) || ((user.permissions >> 8) & 1) == 1, - can_webdav_manage: (user: User) => - UserMethods.is_admin(user) || ((user.permissions >> 9) & 1) == 1, + can: (user: User, permission: number) => + UserMethods.is_admin(user) || ((user.permission >> permission) & 1) == 1, + // can_see_hides: (user: User) => + // UserMethods.is_admin(user) || (user.permission & 1) == 1, + // can_access_without_password: (user: User) => + // UserMethods.is_admin(user) || ((user.permission >> 1) & 1) == 1, + // can_add_aria2_tasks: (user: User) => + // UserMethods.is_admin(user) || ((user.permission >> 2) & 1) == 1, + // can_write: (user: User) => + // UserMethods.is_admin(user) || ((user.permission >> 3) & 1) == 1, + // can_rename: (user: User) => + // UserMethods.is_admin(user) || ((user.permission >> 4) & 1) == 1, + // can_move: (user: User) => + // UserMethods.is_admin(user) || ((user.permission >> 5) & 1) == 1, + // can_copy: (user: User) => + // UserMethods.is_admin(user) || ((user.permission >> 6) & 1) == 1, + // can_remove: (user: User) => + // UserMethods.is_admin(user) || ((user.permission >> 7) & 1) == 1, + // can_webdav_read: (user: User) => + // UserMethods.is_admin(user) || ((user.permission >> 8) & 1) == 1, + // can_webdav_manage: (user: User) => + // UserMethods.is_admin(user) || ((user.permission >> 9) & 1) == 1, }; diff --git a/src/utils/path_join.ts b/src/utils/path_join.ts index 24f45b268..42da6455c 100644 --- a/src/utils/path_join.ts +++ b/src/utils/path_join.ts @@ -11,3 +11,8 @@ export const joinRoot = (...paths: string[]) => { export const trimRoot = (path: string) => { return path.replace(root_path, ""); }; + +export const baseName = (path: string) => { + return path.split("/").pop(); +}; +