diff --git a/src/components/Modal/Modal/Modal.tsx b/src/components/Modal/Modal/Modal.tsx index 825023c..0b316da 100644 --- a/src/components/Modal/Modal/Modal.tsx +++ b/src/components/Modal/Modal/Modal.tsx @@ -3,7 +3,14 @@ import { Dialog, Transition } from '@headlessui/react'; import { XIcon } from '@heroicons/react/solid'; import { ModalProps } from './types'; -export const Modal: React.FC = ({ open, onClose, onSave, children, title = '' }) => { +export const Modal: React.FC = ({ + open, + onClose, + onSave, + children, + title = '', + useCancelButton = false, +}) => { const cancelButtonRef = useRef(null); return ( @@ -36,18 +43,22 @@ export const Modal: React.FC = ({ open, onClose, onSave, children, t leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95" >
-
-
{title}
- -
+ {!useCancelButton && ( +
+
{title}
+ +
+ )} +
{children}
+ {onSave && (
+ {useCancelButton && ( + + )}
)}
diff --git a/src/components/Modal/Modal/types.ts b/src/components/Modal/Modal/types.ts index a673cdb..9fd3eee 100644 --- a/src/components/Modal/Modal/types.ts +++ b/src/components/Modal/Modal/types.ts @@ -3,4 +3,5 @@ export type ModalProps = { onClose: () => void; title?: string; onSave?: () => void; + useCancelButton?: boolean; }; diff --git a/src/modules/apps/AppSingle.tsx b/src/modules/apps/AppSingle.tsx index 2e10a27..ab8096d 100644 --- a/src/modules/apps/AppSingle.tsx +++ b/src/modules/apps/AppSingle.tsx @@ -3,9 +3,7 @@ import { Link, RouteComponentProps, RouterProps } from '@reach/router'; import { ChevronRightIcon } from '@heroicons/react/solid'; import { XCircleIcon } from '@heroicons/react/outline'; import { Tabs } from 'src/components'; -import { GeneralTab } from './GeneralTab'; -import { Secrets } from './Secrets'; -import { Advanced } from './Advanced'; +import { AdvancedTab, GeneralTab } from './components'; type AppSingleProps = RouteComponentProps & RouterProps; @@ -16,8 +14,7 @@ const pages = [ const tabs = [ { name: 'General', component: }, - { name: 'Secrets & Passwords', component: }, - { name: 'Advanced Configuration', component: }, + { name: 'Advanced Configuration', component: }, ]; export const AppSingle: React.FC = () => { diff --git a/src/modules/apps/Apps.tsx b/src/modules/apps/Apps.tsx index 6797e86..6c28eb4 100644 --- a/src/modules/apps/Apps.tsx +++ b/src/modules/apps/Apps.tsx @@ -1,4 +1,5 @@ -import React, { useState, useCallback } from 'react'; +/* eslint-disable react-hooks/exhaustive-deps */ +import React, { useState, useCallback, useMemo } from 'react'; import { RouteComponentProps, navigate, Link } from '@reach/router'; import { ChevronRightIcon, SearchIcon, PlusIcon } from '@heroicons/react/solid'; import { CogIcon, TrashIcon } from '@heroicons/react/outline'; @@ -12,11 +13,50 @@ const pages = [{ name: 'Apps', href: '#', current: true }]; export const Apps: React.FC = () => { const [selectedRowsIds, setSelectedRowsIds] = useState({}); const [deleteModal, setDeleteModal] = useState(false); + const [search, setSearch] = useState(''); const deleteModalOpen = () => setDeleteModal(true); const deleteModalClose = () => setDeleteModal(false); - const columns: any = React.useMemo( + const handleSearch = useCallback((event: any) => { + setSearch(event.target.value); + }, []); + + const data: any[] = useMemo( + () => [ + { + id: 1, + name: 'Nextcloud', + status: 'Active for everyone', + assetSrc: './assets/nextcloud.svg', + }, + { + id: 2, + name: 'Wekan', + status: 'Active for everyone', + assetSrc: './assets/wekan.svg', + }, + { + id: 3, + name: 'Rocketchat', + status: 'Active for everyone', + assetSrc: './assets/rocketchat.svg', + }, + { + id: 4, + name: 'Wordpress', + status: 'Active for everyone', + assetSrc: './assets/wordpress.svg', + }, + ], + [], + ); + + const filterSearch = useMemo(() => { + return data.filter((item: any) => item.name?.toLowerCase().includes(search.toLowerCase())); + }, [search]); + + const columns: any = useMemo( () => [ { Header: 'Name', @@ -70,36 +110,6 @@ export const Apps: React.FC = () => { [], ); - const data: any[] = React.useMemo( - () => [ - { - id: 1, - name: 'Nextcloud', - status: 'Active for everyone', - assetSrc: './assets/nextcloud.svg', - }, - { - id: 2, - name: 'Wekan', - status: 'Active for everyone', - assetSrc: './assets/wekan.svg', - }, - { - id: 3, - name: 'Rocketchat', - status: 'Active for everyone', - assetSrc: './assets/rocketchat.svg', - }, - { - id: 4, - name: 'Wordpress', - status: 'Active for everyone', - assetSrc: './assets/wordpress.svg', - }, - ], - [], - ); - const selectedRows = useCallback((rows: Record) => { setSelectedRowsIds(rows); }, []); @@ -146,8 +156,8 @@ export const Apps: React.FC = () => {
-
-
+
+ {/*
@@ -162,9 +172,9 @@ export const Apps: React.FC = () => { -
+
*/} -
+
@@ -179,6 +189,7 @@ export const Apps: React.FC = () => { id="email" className="focus:ring-primary-dark focus:border-primary-dark block w-full rounded-md pl-10 sm:text-sm border-gray-200" placeholder="Search Apps" + onChange={handleSearch} />
@@ -201,7 +212,7 @@ export const Apps: React.FC = () => {
- +
diff --git a/src/modules/apps/Advanced.tsx b/src/modules/apps/components/AdvancedTab/AdvancedTab.tsx similarity index 94% rename from src/modules/apps/Advanced.tsx rename to src/modules/apps/components/AdvancedTab/AdvancedTab.tsx index ce1bc52..a3b7c34 100644 --- a/src/modules/apps/Advanced.tsx +++ b/src/modules/apps/components/AdvancedTab/AdvancedTab.tsx @@ -6,17 +6,21 @@ import { highlight, languages } from 'prismjs'; import 'prismjs/components/prism-clike'; import 'prismjs/components/prism-yaml'; import 'prismjs/themes/prism.css'; -import { initialEditorYaml } from './consts'; +import { initialEditorYaml } from '../../consts'; +import { Secrets } from './components'; function classNames(...classes: any) { return classes.filter(Boolean).join(' '); } -export const Advanced = () => { +export const AdvancedTab = () => { const [code, setCode] = React.useState(initialEditorYaml); return ( <> +
+

Configuration

+
@@ -153,6 +157,8 @@ search: Save changes
+ + ); }; diff --git a/src/modules/apps/ChangeSecretModal.tsx b/src/modules/apps/components/AdvancedTab/components/ChangeSecretModal.tsx similarity index 100% rename from src/modules/apps/ChangeSecretModal.tsx rename to src/modules/apps/components/AdvancedTab/components/ChangeSecretModal.tsx diff --git a/src/modules/apps/Secrets.tsx b/src/modules/apps/components/AdvancedTab/components/Secrets.tsx similarity index 72% rename from src/modules/apps/Secrets.tsx rename to src/modules/apps/components/AdvancedTab/components/Secrets.tsx index 1eaa1ad..3d54324 100644 --- a/src/modules/apps/Secrets.tsx +++ b/src/modules/apps/components/AdvancedTab/components/Secrets.tsx @@ -1,11 +1,12 @@ import React, { useState } from 'react'; -import { ClipboardIcon, KeyIcon } from '@heroicons/react/outline'; +import { ChevronDownIcon, ChevronRightIcon, ClipboardIcon, KeyIcon } from '@heroicons/react/outline'; import { showToast } from 'src/common/util/show-toast'; import { Table } from 'src/components'; import { ChangeSecretModal } from './ChangeSecretModal'; export const Secrets = () => { const [secretModal, setSecretModal] = useState(false); + const [openDangerZone, setOpenDangerZone] = useState(false); const secretModalOpen = () => setSecretModal(true); const secretModalClose = () => setSecretModal(false); @@ -15,6 +16,8 @@ export const Secrets = () => { setSecretModal(false); }; + const toggleDangerZone = () => setOpenDangerZone((prevState) => !prevState); + const columns: any = React.useMemo( () => [ { @@ -65,7 +68,7 @@ export const Secrets = () => {
- - +
+

Danger zone

+ + +
+ + {openDangerZone && ( +
+
+
- + )} diff --git a/src/modules/apps/components/AdvancedTab/components/index.ts b/src/modules/apps/components/AdvancedTab/components/index.ts new file mode 100644 index 0000000..85b796b --- /dev/null +++ b/src/modules/apps/components/AdvancedTab/components/index.ts @@ -0,0 +1,2 @@ +export { Secrets } from './Secrets'; +export { ChangeSecretModal } from './ChangeSecretModal'; diff --git a/src/modules/apps/components/AdvancedTab/index.ts b/src/modules/apps/components/AdvancedTab/index.ts new file mode 100644 index 0000000..ed08df8 --- /dev/null +++ b/src/modules/apps/components/AdvancedTab/index.ts @@ -0,0 +1 @@ +export { AdvancedTab } from './AdvancedTab'; diff --git a/src/modules/apps/GeneralTab.tsx b/src/modules/apps/components/GeneralTab/GeneralTab.tsx similarity index 100% rename from src/modules/apps/GeneralTab.tsx rename to src/modules/apps/components/GeneralTab/GeneralTab.tsx diff --git a/src/modules/apps/components/GeneralTab/index.ts b/src/modules/apps/components/GeneralTab/index.ts new file mode 100644 index 0000000..59d763e --- /dev/null +++ b/src/modules/apps/components/GeneralTab/index.ts @@ -0,0 +1 @@ +export { GeneralTab } from './GeneralTab'; diff --git a/src/modules/apps/components/index.ts b/src/modules/apps/components/index.ts new file mode 100644 index 0000000..77f3731 --- /dev/null +++ b/src/modules/apps/components/index.ts @@ -0,0 +1,2 @@ +export { GeneralTab } from './GeneralTab'; +export { AdvancedTab } from './AdvancedTab'; diff --git a/src/modules/users/Users.tsx b/src/modules/users/Users.tsx index dd0d184..9c6da4e 100644 --- a/src/modules/users/Users.tsx +++ b/src/modules/users/Users.tsx @@ -1,7 +1,8 @@ -import React, { useState, useCallback, useEffect } from 'react'; +/* eslint-disable react-hooks/exhaustive-deps */ +import React, { useState, useCallback, useEffect, useMemo } from 'react'; import { RouteComponentProps, Link } from '@reach/router'; import { ChevronRightIcon, SearchIcon, PlusIcon } from '@heroicons/react/solid'; -import { CogIcon, KeyIcon, TrashIcon } from '@heroicons/react/outline'; +import { CogIcon, TrashIcon } from '@heroicons/react/outline'; import { useUsers } from 'src/services/users'; import { Table } from 'src/components'; import { ConfirmationModal } from 'src/components/Modal'; @@ -13,8 +14,13 @@ export const Users: React.FC = () => { const [selectedRowsIds, setSelectedRowsIds] = useState({}); const [deleteModal, setDeleteModal] = useState(false); const [configureModal, setConfigureModal] = useState(false); + const [search, setSearch] = useState(''); const { loadUsers, users } = useUsers(); + const handleSearch = useCallback((event: any) => { + setSearch(event.target.value); + }, []); + // TODO: Check why it needs any const allUsers: any = users.items; @@ -31,6 +37,10 @@ export const Users: React.FC = () => { /* eslint-disable-next-line react-hooks/exhaustive-deps */ }, []); + const filterSearch = useMemo(() => { + return allUsers.filter((item: any) => item.name?.toLowerCase().includes(search.toLowerCase())); + }, [search, allUsers]); + const deleteModalOpen = () => setDeleteModal(true); const deleteModalClose = () => setDeleteModal(false); @@ -117,6 +127,7 @@ export const Users: React.FC = () => {

Users

-
-
-
+
+
+ {/*
@@ -143,9 +154,9 @@ export const Users: React.FC = () => { -
+
*/} -
+
@@ -160,6 +171,7 @@ export const Users: React.FC = () => { id="email" className="focus:ring-primary-500 focus:border-primary-500 block w-full rounded-md pl-10 sm:text-sm border-gray-200" placeholder="Search Users" + onChange={handleSearch} />
@@ -168,14 +180,6 @@ export const Users: React.FC = () => { {selectedRowsIds && Object.keys(selectedRowsIds).length !== 0 && (
- -
+
diff --git a/src/modules/users/components/UserModal/UserModal.tsx b/src/modules/users/components/UserModal/UserModal.tsx index 6f4144b..b594a1d 100644 --- a/src/modules/users/components/UserModal/UserModal.tsx +++ b/src/modules/users/components/UserModal/UserModal.tsx @@ -1,85 +1,122 @@ -import React, { Fragment, useRef } from 'react'; -import { Dialog, Transition } from '@headlessui/react'; -import { Tabs } from 'src/components'; -import { UserDetailsTab } from './components/UserDetailsTab'; -import { AdvancedTab } from './components/AdvancedTab'; -import { AppAccessTab } from './components/AppAccess'; - -type UserModalProps = { - open: boolean; - onClose: () => void; - onSave?: () => void; -}; - -const tabs = [ - { name: 'User Details', component: }, - { name: 'Advanced', component: }, - { name: 'App Access', component: }, -]; +import React from 'react'; +import { Modal } from 'src/components'; +import { appAccessList } from './consts'; +import { UserModalProps } from './types'; export const UserModal = ({ open, onClose, onSave = () => {} }: UserModalProps) => { - const cancelButtonRef = useRef(null); - return ( - - -
- - - + +
+
+
+
+

Personal Information

+
- {/* This element is to trick the browser into centering the modal contents. */} - - -
-
- +
+
+ +
+ +
-
- - + +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
- +
+ +
+
+

App Access

+
+ +
+
+
    + {appAccessList.map((app: any) => { + return ( +
  • +
    +
    + {app.name} +

    {app.name}

    +
    +
    + +
    +
    +
  • + ); + })} +
+
+
+
-
-
+ + ); }; diff --git a/src/modules/users/components/UserModal/components/AdvancedTab.tsx b/src/modules/users/components/UserModal/components/AdvancedTab.tsx deleted file mode 100644 index 1a6a726..0000000 --- a/src/modules/users/components/UserModal/components/AdvancedTab.tsx +++ /dev/null @@ -1,113 +0,0 @@ -import React, { useState } from 'react'; -import { InformationCircleIcon } from '@heroicons/react/solid'; -import { Switch } from '@headlessui/react'; - -function classNames(...classes: any) { - return classes.filter(Boolean).join(' '); -} - -export const AdvancedTab = () => { - const [enabled, setEnabled] = useState(false); - - return ( -
-
-
-
-
-
-

You can give or revoke app access for this user

-
-
-
- -
- - - - User - - - Gives this user access only to assigned apps. - - - - - -
- -
- - - - Admin - - - Gives this user access to all apps and permission to change other users roles - - - - - -
- -
- - - - Super Admin - - - Will transfer all Super Admin rights to this user - - - - - -
-
- ); -}; diff --git a/src/modules/users/components/UserModal/components/AppAccess.tsx b/src/modules/users/components/UserModal/components/AppAccess.tsx deleted file mode 100644 index dd42fc0..0000000 --- a/src/modules/users/components/UserModal/components/AppAccess.tsx +++ /dev/null @@ -1,138 +0,0 @@ -import React, { useState } from 'react'; -import { Switch } from '@headlessui/react'; - -function classNames(...classes: any) { - return classes.filter(Boolean).join(' '); -} - -export const AppAccessTab = () => { - const [enabled, setEnabled] = useState(false); - - return ( -
-
- -
-
- -
-
- - Wekan - -
-
- - -
-
- -
- -
-
- -
-
- - Wordpress - -
-
- - -
-
- -
- -
-
- -
-
- - Nextcloud - -
-
- - -
-
- -
- -
-
- -
-
- - Rocketchat - -
-
- - -
-
-
- ); -}; diff --git a/src/modules/users/components/UserModal/components/UserDetailsTab.tsx b/src/modules/users/components/UserModal/components/UserDetailsTab.tsx deleted file mode 100644 index 007baa0..0000000 --- a/src/modules/users/components/UserModal/components/UserDetailsTab.tsx +++ /dev/null @@ -1,90 +0,0 @@ -import React from 'react'; - -export const UserDetailsTab = () => { - return ( -
-
-
-

Personal Information

-
- -
-
- -
- -
-
- -
- -
- -
-
- -
- -
- -
-
- -
- -
- -
-
- -
- -
- -
-
-
-
-
- ); -}; diff --git a/src/modules/users/components/UserModal/consts.ts b/src/modules/users/components/UserModal/consts.ts new file mode 100644 index 0000000..795971d --- /dev/null +++ b/src/modules/users/components/UserModal/consts.ts @@ -0,0 +1,18 @@ +export const appAccessList = [ + { + image: '/assets/wekan.svg', + name: 'Wekan', + }, + { + image: '/assets/wordpress.svg', + name: 'Wordpress', + }, + { + image: '/assets/nextcloud.svg', + name: 'NextCloud', + }, + { + image: '/assets/rocketchat.svg', + name: 'RocketChat', + }, +]; diff --git a/src/modules/users/components/UserModal/types.ts b/src/modules/users/components/UserModal/types.ts new file mode 100644 index 0000000..a7a0c5b --- /dev/null +++ b/src/modules/users/components/UserModal/types.ts @@ -0,0 +1,5 @@ +export type UserModalProps = { + open: boolean; + onClose: () => void; + onSave?: () => void; +};