Skip to content

Commit

Permalink
Edit organization public info (#31)
Browse files Browse the repository at this point in the history
* Update org tabs

* Update drawer & UI components

* Add no data viewer

* Update links block

* Update edit links drawer

* Add draggable context

* Update link forms

* Update form

* Update org links

* Add org owner restriction to edit links

* Fix review issues

* Update no data icon
  • Loading branch information
ardier16 authored Feb 2, 2024
1 parent 6e9c635 commit ed25114
Show file tree
Hide file tree
Showing 25 changed files with 758 additions and 167 deletions.
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@
"@distributedlab/jac": "^1.0.0-rc.9",
"@distributedlab/tools": "^1.0.0-rc.9",
"@distributedlab/w3p": "^1.0.0-rc.9",
"@dnd-kit/core": "^6.1.0",
"@dnd-kit/modifiers": "^7.0.0",
"@dnd-kit/sortable": "^8.0.0",
"@emotion/react": "^11.11.1",
"@emotion/styled": "^11.11.0",
"@hookform/resolvers": "^3.3.2",
Expand Down
13 changes: 11 additions & 2 deletions src/api/modules/orgs/helpers/orgs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { api } from '@/api/clients'
import {
type Organization,
type OrganizationCreate,
type OrgMetadata,
type OrgsRequestQueryParams,
OrgsStatuses,
type OrgUser,
Expand Down Expand Up @@ -102,8 +103,8 @@ export const loadOrgs = async (query: OrgsRequestQueryParams) => {
return data
}

export const loadOrgsAmount = async () => {
const { data } = await api.get<number>(`${ApiServicePaths.Orgs}/v1/orgs/amount`)
export const loadOrgsCount = async () => {
const { data } = await api.get<number>(`${ApiServicePaths.Orgs}/v1/orgs/count`)

return data
}
Expand Down Expand Up @@ -139,6 +140,14 @@ export const verifyOrg = async (id: string) => {
return data
}

export const updateOrgMetadata = async (id: string, metadata: Partial<OrgMetadata>) => {
const { data } = await api.patch<Partial<OrgMetadata>>(`${ApiServicePaths.Orgs}/v1/orgs/${id}`, {
body: { data: metadata },
})

return data
}

export const loadOrgUsers = async (id: string, query: OrgsRequestQueryParams) => {
const { data } = await api.get<OrgUser[]>(`${ApiServicePaths.Orgs}/v1/orgs/${id}/users`, {
query,
Expand Down
57 changes: 57 additions & 0 deletions src/common/NoDataViewer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { Stack, StackProps, Typography, useTheme } from '@mui/material'
import { ReactNode } from 'react'

import { UiIcon } from '@/ui'

interface Props extends StackProps {
icon?: ReactNode
title?: string
description?: string
action?: ReactNode
}

export default function NoDataViewer({
icon = <UiIcon componentName='folderOff' />,
title = 'No data',
description,
action,
...rest
}: Props) {
const { palette, spacing } = useTheme()

return (
<Stack
spacing={4}
alignItems={'center'}
width={'100%'}
justifyContent={'center'}
p={8}
border={1}
borderColor={palette.action.hover}
borderRadius={2}
sx={{ borderStyle: 'dashed', ...rest.sx }}
{...rest}
>
<Stack
alignItems={'center'}
justifyContent={'center'}
bgcolor={palette.action.active}
color={palette.text.secondary}
borderRadius={250}
width={spacing(12)}
height={spacing(12)}
>
{icon}
</Stack>
<Stack spacing={1} textAlign={'center'}>
<Typography variant='body3'>{title}</Typography>
{description && (
<Typography variant='body3' color={palette.text.secondary}>
{description}
</Typography>
)}
</Stack>
{action}
</Stack>
)
}
1 change: 1 addition & 0 deletions src/common/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export { default as AppNavbar } from './AppNavbar'
export { default as FillRequestForm } from './FillRequestForm'
export { default as NoDataViewer } from './NoDataViewer'
export { default as PageListFilters } from './PageListFilters'
export { default as PageTitles } from './PageTitles'
export { default as ProfileMenu } from './ProfileMenu'
Expand Down
53 changes: 53 additions & 0 deletions src/contexts/vertical-draggable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import {
closestCenter,
DndContext,
PointerSensor,
TouchSensor,
UniqueIdentifier,
useSensor,
useSensors,
} from '@dnd-kit/core'
import { restrictToParentElement, restrictToVerticalAxis } from '@dnd-kit/modifiers'
import { arrayMove, SortableContext, verticalListSortingStrategy } from '@dnd-kit/sortable'
import { PropsWithChildren } from 'react'

type SortableId = UniqueIdentifier | { id: UniqueIdentifier }

interface Props<T extends SortableId> extends PropsWithChildren {
items: T[]
onItemsChange?: (items: T[]) => void
onItemsMove?: (oldIndex: number, newIndex: number) => void
}

export default function VerticalDraggableContext<T extends SortableId>({
items,
onItemsMove,
onItemsChange,
children,
}: Props<T>) {
const sensors = useSensors(useSensor(PointerSensor), useSensor(TouchSensor))

return (
<DndContext
sensors={sensors}
collisionDetection={closestCenter}
modifiers={[restrictToParentElement, restrictToVerticalAxis]}
onDragEnd={({ active, over }) => {
if (!over?.id) return

if (active.id !== over.id) {
const itemIds = items.map(item => (typeof item === 'object' ? item.id : item))
const oldIndex = itemIds.indexOf(active.id)
const newIndex = itemIds.indexOf(over.id)

onItemsMove?.(oldIndex, newIndex)
onItemsChange?.(arrayMove(items, oldIndex, newIndex))
}
}}
>
<SortableContext items={items} strategy={verticalListSortingStrategy}>
{children}
</SortableContext>
</DndContext>
)
}
66 changes: 37 additions & 29 deletions src/enums/icons.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,32 @@
import { default as AccountCircleIcon } from '@mui/icons-material/AccountCircle'
import { default as Add } from '@mui/icons-material/Add'
import { default as AddPhotoAlternateOutlined } from '@mui/icons-material/AddPhotoAlternateOutlined'
import { default as CheckIcon } from '@mui/icons-material/Check'
import { default as ChevronLeft } from '@mui/icons-material/ChevronLeft'
import { default as Close } from '@mui/icons-material/Close'
import { default as ContentCopy } from '@mui/icons-material/ContentCopy'
import { default as DarkModeOutlined } from '@mui/icons-material/DarkModeOutlined'
import { default as DeleteIcon } from '@mui/icons-material/Delete'
import { default as ErrorOutlineIcon } from '@mui/icons-material/ErrorOutline'
import { default as InfoIcon } from '@mui/icons-material/Info'
import { default as InfoOutlinedIcon } from '@mui/icons-material/InfoOutlined'
import { default as KeyboardArrowDownOutlinedIcon } from '@mui/icons-material/KeyboardArrowDownOutlined'
import { default as Layers } from '@mui/icons-material/Layers'
import { default as LightModeOutlined } from '@mui/icons-material/LightModeOutlined'
import { default as Link } from '@mui/icons-material/Link'
import { default as Logout } from '@mui/icons-material/Logout'
import { default as Notifications } from '@mui/icons-material/Notifications'
import { default as OpenInNew } from '@mui/icons-material/OpenInNew'
import { default as QrCode } from '@mui/icons-material/QrCode'
import { default as Search } from '@mui/icons-material/Search'
import { default as Tune } from '@mui/icons-material/Tune'
import { default as Verified } from '@mui/icons-material/Verified'
import { default as WarningAmberIcon } from '@mui/icons-material/WarningAmber'
import { default as Work } from '@mui/icons-material/Work'
import AccountCircleIcon from '@mui/icons-material/AccountCircle'
import Add from '@mui/icons-material/Add'
import AddPhotoAlternateOutlined from '@mui/icons-material/AddPhotoAlternateOutlined'
import CheckIcon from '@mui/icons-material/Check'
import ChevronLeft from '@mui/icons-material/ChevronLeft'
import Close from '@mui/icons-material/Close'
import ContentCopy from '@mui/icons-material/ContentCopy'
import DarkModeOutlined from '@mui/icons-material/DarkModeOutlined'
import DeleteIcon from '@mui/icons-material/Delete'
import DeleteOutlined from '@mui/icons-material/DeleteOutlined'
import DragIndicator from '@mui/icons-material/DragIndicator'
import DriveFileRenameOutlineOutlined from '@mui/icons-material/DriveFileRenameOutlineOutlined'
import ErrorOutlineIcon from '@mui/icons-material/ErrorOutline'
import FolderOff from '@mui/icons-material/FolderOff'
import InfoIcon from '@mui/icons-material/Info'
import InfoOutlinedIcon from '@mui/icons-material/InfoOutlined'
import KeyboardArrowDownOutlinedIcon from '@mui/icons-material/KeyboardArrowDownOutlined'
import Layers from '@mui/icons-material/Layers'
import LightModeOutlined from '@mui/icons-material/LightModeOutlined'
import Link from '@mui/icons-material/Link'
import Logout from '@mui/icons-material/Logout'
import Notifications from '@mui/icons-material/Notifications'
import OpenInNew from '@mui/icons-material/OpenInNew'
import QrCode from '@mui/icons-material/QrCode'
import Search from '@mui/icons-material/Search'
import Tune from '@mui/icons-material/Tune'
import Verified from '@mui/icons-material/Verified'
import WarningAmberIcon from '@mui/icons-material/WarningAmber'
import Work from '@mui/icons-material/Work'

export enum Icons {
Metamask = 'metamask',
Expand All @@ -44,20 +48,24 @@ export const ICON_COMPONENTS = {
close: Close,
darkModeOutlined: DarkModeOutlined,
delete: DeleteIcon,
deleteOutlined: DeleteOutlined,
dragIndicator: DragIndicator,
driveFileRenameOutlineOutlined: DriveFileRenameOutlineOutlined,
errorOutline: ErrorOutlineIcon,
qrCode: QrCode,
folderOff: FolderOff,
info: InfoIcon,
infoOutlined: InfoOutlinedIcon,
keyboardArrowDownOutlined: KeyboardArrowDownOutlinedIcon,
openInNew: OpenInNew,
layers: Layers,
lightModeOutlined: LightModeOutlined,
link: Link,
logOut: Logout,
notifications: Notifications,
openInNew: OpenInNew,
qrCode: QrCode,
search: Search,
tune: Tune,
verified: Verified,
warningAmber: WarningAmberIcon,
work: Work,
layers: Layers,
notifications: Notifications,
}
29 changes: 25 additions & 4 deletions src/hooks/form.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,33 @@
import { yupResolver } from '@hookform/resolvers/yup'
import { useState } from 'react'
import { DefaultValues, FieldErrorsImpl, Merge, useForm as useFormHook } from 'react-hook-form'
import {
Control,
DefaultValues,
FieldErrorsImpl,
FieldPath,
FieldValues,
useForm as useFormHook,
UseFormHandleSubmit,
UseFormRegister,
} from 'react-hook-form'
import * as Yup from 'yup'

export const useForm = <T extends Yup.AnyObjectSchema, R extends object>(
export type Form<T extends FieldValues> = {
isFormDisabled: boolean
getErrorMessage: (fieldName: FieldPath<T>) => string
enableForm: () => void
disableForm: () => void
formState: T
formErrors: FieldErrorsImpl<T>
register: UseFormRegister<T>
handleSubmit: UseFormHandleSubmit<T>
control: Control<T>
}

export const useForm = <T extends Yup.AnyObjectSchema, R extends FieldValues>(
defaultValues: R,
schemaBuilder: (yup: typeof Yup) => T,
) => {
): Form<R> => {
const [isFormDisabled, setIsFormDisabled] = useState(false)

const {
Expand All @@ -24,7 +45,7 @@ export const useForm = <T extends Yup.AnyObjectSchema, R extends object>(
resolver: yupResolver(schemaBuilder(Yup)),
})

const getErrorMessage = (fieldName: keyof Merge<R, FieldErrorsImpl<R>>): string => {
const getErrorMessage = (fieldName: FieldPath<R>): string => {
return errors[fieldName]?.message?.toString() ?? ''
}

Expand Down
2 changes: 1 addition & 1 deletion src/pages/Orgs/pages/OrgsId/contexts/org-details.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export const OrgDetailsContextProvider = ({ children }: { children: ReactNode })
isExact: true,
},
{
label: 'Private',
label: 'Internal',
route: generatePath(RoutePaths.OrgsIdGroups, { id: org.id }),
},
]}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ export default function List({ filter, ...rest }: Props) {
))}

{drawerContent && (
<UiDrawer open={isDrawerShown} onClose={() => setIsDrawerShown(false)} anchor='right'>
<UiDrawer open={isDrawerShown} onClose={() => setIsDrawerShown(false)}>
<Stack>
<Stack direction='row' alignItems='center' justifyContent='space-between' p={5}>
<Typography>Member Details</Typography>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,11 +166,7 @@ export default function OrgGroupsId() {
/>
<Stack flex={1}>{routes}</Stack>

<UiDrawer
open={isInviteDrawerShown}
anchor='right'
onClose={() => setIsInviteDrawerShown(false)}
>
<UiDrawer open={isInviteDrawerShown} onClose={() => setIsInviteDrawerShown(false)}>
<InviteMemberForm
onMemberInvitationCreated={handleMemberInvitationCreated}
maxWidth={theme => theme.spacing(100)}
Expand Down
Loading

0 comments on commit ed25114

Please sign in to comment.