Skip to content

Commit

Permalink
Setup organizations (#4)
Browse files Browse the repository at this point in the history
* add org list and create page structure wip

* renames wip

* add orgs api module

* update imports

* fix after review #4

* add small orgs loading example

* refactored toasts manager

* hotfix

* hotfix

* refactored navtabs

* update orgs routes for orgs page
  • Loading branch information
lukachi authored Dec 27, 2023
1 parent b716de3 commit 9b53188
Show file tree
Hide file tree
Showing 34 changed files with 665 additions and 132 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"rsc": "node scripts/release-sanity-check.mjs"
},
"dependencies": {
"@distributedlab/jac": "^1.0.0-rc.9",
"@distributedlab/tools": "^1.0.0-rc.9",
"@distributedlab/w3p": "^1.0.0-rc.9",
"@emotion/react": "^11.11.1",
Expand Down
6 changes: 6 additions & 0 deletions src/api/clients/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { config } from '@config'
import { JsonApiClient } from '@distributedlab/jac'

export const api = new JsonApiClient({
baseUrl: config.API_URL,
})
2 changes: 2 additions & 0 deletions src/api/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './clients'
export * from './modules'
1 change: 1 addition & 0 deletions src/api/modules/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './orgs'
15 changes: 15 additions & 0 deletions src/api/modules/orgs/enums.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
export enum OrgsStatuses {
Unverified = '0',
Verified = '1',
}

export enum OrgsRequestFilters {
Owner = 'owner',
UserDid = 'user_did',
Status = 'status',
}

export enum OrgsIncludes {
organization = 'organization',
owner = 'owner',
}
54 changes: 54 additions & 0 deletions src/api/modules/orgs/helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import {
api,
type Organization,
type OrganizationCreate,
type OrgsRequestQueryParams,
type OrgUser,
type OrgVerificationCode,
} from '@/api'

export const loadOrgs = async (query: OrgsRequestQueryParams) => {
return api.get<Organization[]>('/v1/orgs', {
query,
})
}

export const loadOrgsAmount = async () => {
return api.get<number>('/v1/orgs/amount')
}

export const loadOrgById = async (id: string, query: OrgsRequestQueryParams) => {
return api.get<Organization>(`/v1/orgs/${id}`, {
query,
})
}

export const createOrg = async (body: OrganizationCreate) => {
return api.post<Organization>('/v1/orgs', {
body: {
data: {
id: body.id,
type: 'organizations-create',
attributes: {
owner_did: body.ownerDid,
domain: body.domain,
metadata: body.metadata,
},
},
},
})
}

export const verifyOrg = async (id: string) => {
return api.post<Organization>(`/v1/orgs/${id}`)
}

export const loadOrgUsers = async (id: string, query: OrgsRequestQueryParams) => {
return api.get<OrgUser[]>(`/v1/orgs/${id}/users`, {
query,
})
}

export const getOrgVerificationCode = async (id: string) => {
return api.get<OrgVerificationCode>(`/v1/orgs/${id}/verification-code`)
}
3 changes: 3 additions & 0 deletions src/api/modules/orgs/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './enums'
export * from './helpers'
export * from './types'
62 changes: 62 additions & 0 deletions src/api/modules/orgs/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import type { OrgsIncludes, OrgsRequestFilters, OrgsStatuses } from '@/api'

export type OrgMetadata = {
name: string
description: string
}

export type OrgUser = {
id: string
type: 'users'
did: string
role: {
name: string
value: number
}
org_id: string
created_at: string
updated_at: string
}

export type Organization = {
id: string
type: 'organizations'
did: string
owner?: OrgUser
domain: string
metadata: OrgMetadata
status: {
name: string
value: OrgsStatuses
}
verification_code: string
issued_claims_count: string
members_count: string
created_at: string
updated_at: string
}

export type OrganizationCreate = {
id: string
ownerDid: string
domain: string
metadata: OrgMetadata
}

export type OrgsRequestFiltersMap = {
[OrgsRequestFilters.Owner]?: string
[OrgsRequestFilters.UserDid]?: string
[OrgsRequestFilters.Status]?: OrgsStatuses
}

export type OrgsRequestQueryParams = {
include?: OrgsIncludes
filter?: OrgsRequestFiltersMap
// TODO: page, limit, sort, ...etc
}

export type OrgVerificationCode = {
id: string
type: string
code: string
}
66 changes: 66 additions & 0 deletions src/common/AppNavbar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { config } from '@config'
import { ButtonProps, Divider, Stack } from '@mui/material'
import { ReactNode, useMemo } from 'react'
import { NavLink, useLocation } from 'react-router-dom'

import { Icons, Routes } from '@/enums'
import { UiButton, UiIcon } from '@/ui'

interface NavbarLinkProps {
to: Routes
children: ReactNode
}

const NavbarLink = ({ children, to }: NavbarLinkProps) => {
const location = useLocation()

const linkProps = useMemo((): Partial<ButtonProps> => {
const locationRoot = location.pathname.split('/')[1]

const isRouteActive = to.includes(locationRoot)

return {
variant: isRouteActive ? 'contained' : 'text',
color: isRouteActive ? 'primary' : 'secondary',
}
}, [location.pathname, to])

return (
<NavLink to={to}>
<UiButton component='div' sx={{ p: 3 }} {...linkProps}>
{children}
</UiButton>
</NavLink>
)
}

const AppNavbar = () => {
const navbarItems = useMemo(
() => [
{ route: Routes.Profiles, iconComponent: <UiIcon name={Icons.Wallet} size={6} /> },
{ route: Routes.Orgs, iconComponent: <UiIcon componentName='work' size={6} /> },
],
[],
)

return (
<Stack spacing={4} py={1}>
<NavLink to={Routes.Profiles}>
<Stack alignItems='center'>
<img src='/branding/logo.svg' alt={config.APP_NAME} />
</Stack>
</NavLink>
<Divider />

<Stack py={6} gap={6}>
{navbarItems.map(({ route, iconComponent }, idx) => (
<NavbarLink to={route} key={idx}>
{iconComponent}
</NavbarLink>
))}
</Stack>
</Stack>
)
}

export default AppNavbar
41 changes: 41 additions & 0 deletions src/common/PageListFilters.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { InputAdornment, Stack, StackProps } from '@mui/material'
import debounce from 'lodash/debounce'
import type { ReactNode } from 'react'
import { useTranslation } from 'react-i18next'

import { UiIcon, UiNavTabs, UiTextField } from '@/ui'

interface Props extends StackProps {
tabs?: {
label: string
route: string
}[]
onSearchInput?: (value: string) => void
actionBar?: ReactNode
}

export default function PageListFilters({ tabs, onSearchInput, actionBar, ...rest }: Props) {
const { t } = useTranslation()

const handleSearchInput = debounce((value: string) => onSearchInput?.(value), 500)

return (
<Stack {...rest} direction='row' alignItems='center' gap={5}>
{tabs && <UiNavTabs tabs={tabs} />}

<UiTextField
InputProps={{
startAdornment: (
<InputAdornment position='start'>
<UiIcon componentName='search' />
</InputAdornment>
),
}}
placeholder={t('page-list-filters.search-input-placeholder')}
onChange={e => handleSearchInput(e.target.value)}
/>

<Stack flex={1}>{actionBar}</Stack>
</Stack>
)
}
23 changes: 23 additions & 0 deletions src/common/PageTitles.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { Box, BoxProps, Divider, Typography } from '@mui/material'

interface Props extends BoxProps {
title: string
subtitle?: string
}

export default function PageTitles({ title, subtitle, ...rest }: Props) {
return (
<Box {...rest}>
<Typography variant='h5' mb={2}>
{title}
</Typography>
<Typography variant='body2'>{subtitle}</Typography>

<Divider
sx={theme => ({
mt: theme.spacing(6),
})}
/>
</Box>
)
}
3 changes: 3 additions & 0 deletions src/common/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export { default as AppNavbar } from './AppNavbar'
export { default as PageListFilters } from './PageListFilters'
export { default as PageTitles } from './PageTitles'
28 changes: 0 additions & 28 deletions src/components/AppNavbar.tsx

This file was deleted.

16 changes: 14 additions & 2 deletions src/config.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import FALLBACK_SUPPORTED_CHAINS from '@/assets/fallback-supported-chains.json'
import { Config, SupportedChainsDetails } from '@/types'
import { SupportedChains, SupportedChainsDetails } from '@/types'

import packageJson from '../package.json'

Expand All @@ -8,9 +8,21 @@ const SUPPORTED_CHAINS_DETAILS: SupportedChainsDetails = {
...JSON.parse(import.meta.env.VITE_SUPPORTED_CHAINS_DETAILS || '{}'),
}

export type Config = {
APP_NAME: string
API_URL: string
BUILD_VERSION: string
SUPPORTED_CHAINS_DETAILS: SupportedChainsDetails
DEFAULT_CHAIN: SupportedChains
ROBOTORNOT_LINK: string
CHROME_METAMASK_ADDON_LINK: string
FIREFOX_METAMASK_ADDON_LINK: string
OPERA_METAMASK_ADDON_LINK: string
}

export const config: Config = {
MODE: import.meta.env.VITE_MODE,
APP_NAME: import.meta.env.VITE_APP_NAME,
API_URL: import.meta.env.VITE_API_URL,
BUILD_VERSION: packageJson.version || import.meta.env.VITE_APP_BUILD_VERSION,
SUPPORTED_CHAINS_DETAILS,
DEFAULT_CHAIN: import.meta.env.VITE_DEFAULT_CHAIN,
Expand Down
Loading

0 comments on commit 9b53188

Please sign in to comment.