Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ Added selected Org and Ledger persistence #167

Merged
merged 8 commits into from
Feb 24, 2025
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@ NEXTAUTH_CASDOOR_ORGANIZATION_NAME=lerian
NEXTAUTH_CASDOOR_APPLICATION_NAME=app-midaz

# Midaz API Configuration
MIDAZ_API_HOST='ledger'
MIDAZ_API_HOST='onboarding'
MIDAZ_API_PORT=3000
MIDAZ_BASE_PATH='http://${MIDAZ_API_HOST}:${MIDAZ_API_PORT}/v1'
MIDAZ_TRANSACTION_BASE_HOST='transaction'
MIDAZ_TRANSACTION_BASE_PORT=3002
MIDAZ_TRANSACTION_BASE_PORT=3001
MIDAZ_TRANSACTION_BASE_PATH='http://${MIDAZ_API_HOST}:${MIDAZ_TRANSACTION_BASE_PORT}/v1'

ENABLE_DEBUG=true
Expand Down
2 changes: 1 addition & 1 deletion .env.local.example
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,5 @@ MIDAZ_API_HOST='localhost'
MIDAZ_API_PORT=3000
MIDAZ_BASE_PATH='http://${MIDAZ_API_HOST}:${MIDAZ_API_PORT}/v1'
MIDAZ_TRANSACTION_BASE_HOST='localhost'
MIDAZ_TRANSACTION_BASE_PORT=3002
MIDAZ_TRANSACTION_BASE_PORT=3001
MIDAZ_TRANSACTION_BASE_PATH='http://${MIDAZ_API_HOST}:${MIDAZ_TRANSACTION_BASE_PORT}/v1'
38 changes: 23 additions & 15 deletions src/components/ledger-selector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,10 @@ const LedgerCommand = ({
return (
<Command>
<CommandInput
placeholder="Search ledger..."
placeholder={intl.formatMessage({
id: 'common.search',
defaultMessage: 'Search...'
})}
value={query}
onValueChange={setQuery}
className="border-b px-2 py-1 pr-10"
Expand Down Expand Up @@ -81,23 +84,22 @@ const LedgerCommand = ({
export const LedgerSelector = () => {
const intl = useIntl()
const [openCommand, setOpenCommand] = React.useState(false)
const { currentOrganization, currentLedgerId, setLedgerId } =
useOrganization()
const { currentOrganization, currentLedger, setLedger } = useOrganization()
const { data: ledgers } = useListLedgers({
organizationId: currentOrganization?.id!
})

React.useEffect(() => {
if (
ledgers?.items?.length &&
(!currentLedgerId ||
(!currentLedger?.id ||
!ledgers.items.some(
(ledger: ILedgerType) => ledger.id === currentLedgerId
(ledger: ILedgerType) => ledger.id === currentLedger.id
))
) {
setLedgerId(ledgers.items[0].id)
setLedger(ledgers.items[0])
}
}, [currentOrganization, ledgers, currentLedgerId, setLedgerId])
}, [currentOrganization, ledgers, currentLedger?.id, setLedger])

const hasLedgers = !!ledgers?.items?.length
const totalLedgers = ledgers?.items?.length ?? 0
Expand Down Expand Up @@ -125,14 +127,23 @@ export const LedgerSelector = () => {
)
}

const handleSelectChange = (id: string) => {
setLedger(ledgers?.items.find((ledger) => ledger.id === id)!)
}

const handleCommandChange = (id: string) => {
setLedger(ledgers?.items.find((ledger) => ledger.id === id)!)
setOpenCommand(false)
}

return (
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<div>
<Select
value={currentLedgerId ?? undefined}
onValueChange={(newLedgerId) => setLedgerId(newLedgerId)}
value={currentLedger?.id ?? undefined}
onValueChange={handleSelectChange}
onOpenChange={(open) => !open && setOpenCommand(false)}
disabled={!hasLedgers}
>
Expand Down Expand Up @@ -160,11 +171,11 @@ export const LedgerSelector = () => {
</SelectLabel>
<SelectItem
disabled
value={currentLedgerId ?? ''}
value={currentLedger?.id ?? ''}
className="font-medium text-zinc-800 data-[disabled]:opacity-100"
>
{ledgers?.items?.find(
(ledger: any) => ledger.id === currentLedgerId
(ledger: any) => ledger.id === currentLedger?.id
)?.name ||
intl.formatMessage({
id: 'ledger.selector.placeholder',
Expand Down Expand Up @@ -195,10 +206,7 @@ export const LedgerSelector = () => {
>
<LedgerCommand
ledgers={ledgers!.items}
onSelect={(id: string) => {
setLedgerId(id)
setOpenCommand(false)
}}
onSelect={handleCommandChange}
/>
</div>
)}
Expand Down
16 changes: 1 addition & 15 deletions src/context/organization-provider/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,23 +31,9 @@ export const OrganizationProvider = async ({
const orgResult = await serverFetcher(
async () => await fetchAllOrganizationsUseCase.execute(10, 1)
)
const firstOrganization = orgResult?.items?.[0]

let defaultLedgerId: string | null = null
if (firstOrganization?.id) {
const ledgersResult = await serverFetcher(
async () =>
await fetchAllLedgersUseCase.execute(firstOrganization.id, 10, 1)
)

defaultLedgerId = ledgersResult?.items?.[0]?.id ?? null
}

return (
<OrganizationProviderClient
organizations={orgResult?.items ?? []}
defaultLedgerId={defaultLedgerId}
>
<OrganizationProviderClient organizations={orgResult?.items ?? []}>
{children}
</OrganizationProviderClient>
)
Expand Down
39 changes: 24 additions & 15 deletions src/context/organization-provider/organization-provider-client.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,16 @@ import {
} from 'react'
import { OrganizationEntity } from '@/core/domain/entities/organization-entity'
import { usePathname, useRouter } from 'next/navigation'
import { useListLedgers } from '@/client/ledgers'
import { ILedgerType } from '@/types/ledgers-type'
import { useDefaultOrg } from './use-default-org'
import { useDefaultLedger } from './use-default-ledger'

type OrganizationContextProps = {
currentOrganization: OrganizationEntity
setOrganization: (organization: OrganizationEntity) => void
currentLedgerId: string | null
setLedgerId: (ledgerId: string | null) => void
currentLedger: ILedgerType
setLedger: (ledger: ILedgerType) => void
}

const OrganizationContext = createContext<OrganizationContextProps>(
Expand All @@ -25,12 +29,10 @@ export const useOrganization = () => useContext(OrganizationContext)

export type OrganizationProviderClientProps = PropsWithChildren & {
organizations: OrganizationEntity[]
defaultLedgerId?: string | null
}

export const OrganizationProviderClient = ({
organizations: organizationsProp,
defaultLedgerId,
children
}: OrganizationProviderClientProps) => {
const router = useRouter()
Expand All @@ -42,6 +44,11 @@ export const OrganizationProviderClient = ({
organizationsProp ?? []
)

const [currentLedger, setCurrentLedger] = useState<ILedgerType>(
{} as ILedgerType
)
const { data: ledgers } = useListLedgers({ organizationId: current?.id! })

useEffect(() => {
// Do nothing if the user is already at the onboarding
if (pathname.includes('/onboarding')) {
Expand All @@ -59,24 +66,26 @@ export const OrganizationProviderClient = ({
}
}, [current?.id, organizations.length])

// Initialize a current organization
useEffect(() => {
if (organizations.length > 0) {
setCurrent(organizations[0])
}
}, [])
useDefaultOrg({
organizations,
current,
setCurrent
})

const [currentLedgerId, setLedgerId] = useState<string | null>(
defaultLedgerId ?? null
)
useDefaultLedger({
current,
ledgers: ledgers?.items ?? [],
currentLedger,
setCurrentLedger
})

return (
<OrganizationContext.Provider
value={{
currentOrganization: current,
setOrganization: setCurrent,
currentLedgerId,
setLedgerId
currentLedger: currentLedger,
setLedger: setCurrentLedger
}}
>
{children}
Expand Down
63 changes: 63 additions & 0 deletions src/context/organization-provider/use-default-ledger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
'use client'

import { OrganizationEntity } from '@/core/domain/entities/organization-entity'
import { getStorageObject } from '@/lib/storage'
import { ILedgerType } from '@/types/ledgers-type'
import { useReducer, useEffect } from 'react'

type UseDefaultLedgerProps = {
current: OrganizationEntity
ledgers: ILedgerType[]
currentLedger: ILedgerType
setCurrentLedger: (ledger: ILedgerType) => void
}

const storageKey = 'defaultLedgers'

export function useDefaultLedger({
current,
ledgers,
currentLedger,
setCurrentLedger
}: UseDefaultLedgerProps) {
const [defaultLedgers, setDefaultLedgers] = useReducer(
(state: Record<string, string>, newState: Record<string, string>) => ({
...state,
...newState
}),
getStorageObject(storageKey, {})
)

const save = (key: string, value: string) => {
localStorage.setItem(
storageKey,
JSON.stringify({ ...defaultLedgers, [key]: value })
)
setDefaultLedgers({ [key]: value })
}

useEffect(() => {
// Check if is there a organization selected
if (current?.id) {
// Check if there is a default ledger saved onto local storage
const ledger = ledgers?.find(
({ id }) => defaultLedgers[current.id!] === id
)

if (ledger) {
// If the ledger is found, set it as the current ledger
setCurrentLedger(ledger)
} else if (ledgers?.length > 0) {
// If the ledger is not found, set the first ledger as the current ledger
setCurrentLedger(ledgers?.[0]!)
}
}
}, [current?.id, ledgers?.length])

useEffect(() => {
// Update storage according to the current ledger
if (currentLedger?.id) {
save(current.id!, currentLedger.id!)
}
}, [currentLedger?.id])
}
56 changes: 56 additions & 0 deletions src/context/organization-provider/use-default-org.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
'use client'

import { OrganizationEntity } from '@/core/domain/entities/organization-entity'
import { getStorage } from '@/lib/storage'
import { useEffect, useState } from 'react'

type UseDefaultOrgProps = {
organizations: OrganizationEntity[]
current: OrganizationEntity
setCurrent: (organization: OrganizationEntity) => void
}

const storageKey = 'defaultOrg'

export function useDefaultOrg({
organizations,
current,
setCurrent
}: UseDefaultOrgProps) {
const [defaultOrg, setDefaultOrg] = useState<string | null>(
getStorage(storageKey, null)
)

const save = (id: string) => {
localStorage.setItem(storageKey, id)
setDefaultOrg(id)
}

// Initialize a current organization
useEffect(() => {
// Check if there is a default organization saved onto local storage
if (defaultOrg) {
// Search for the organization with the id
const org = organizations.find(({ id }) => defaultOrg === id)

// If the organization is found, set it as the current organization
if (org) {
setCurrent(org)
return
}
}

// If there is no default organization saved or the organization is not found
if (organizations.length > 0) {
// Set the first organization as the current one
setCurrent(organizations[0])
}
}, [])

useEffect(() => {
// Update storage according to the current organization
if (current?.id) {
save(current.id)
}
}, [current?.id])
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,10 @@ export class CompleteOnboardingUseCase implements CompleteOnboarding {
await this.fetchOrganizationByIdRepository.fetchById(organizationId)

await this.updateOrganizationRepository.updateOrganization(organizationId, {
legalName: organization.legalName,
doingBusinessAs: organization.doingBusinessAs,
metadata: omit(organization.metadata, 'onboarding')
metadata: {
...organization.metadata,
onboarding: null
}
})

const ledgerEntity: LedgerEntity = LedgerMapper.toDomain(ledger)
Expand Down
11 changes: 11 additions & 0 deletions src/lib/storage/get-storage-object.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { getStorage } from './get-storage'

export function getStorageObject(key: string, defaultValue: any) {
try {
const dataString = getStorage(key, defaultValue)
return JSON.parse(dataString)
} catch (error) {
console.error(error)
return defaultValue
}
}
15 changes: 15 additions & 0 deletions src/lib/storage/get-storage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
const isServer = typeof window === 'undefined'

export function getStorage(key: string, defaultValue: any) {
if (isServer) {
return defaultValue
}

let value
try {
value = localStorage.getItem(key) || undefined
} catch (e) {
// Unsupported
}
return value || defaultValue
}
2 changes: 2 additions & 0 deletions src/lib/storage/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './get-storage'
export * from './get-storage-object'
Loading