diff --git a/src/components/ui/CopilotSelector.tsx b/src/components/ui/CopilotSelector.tsx
index d49623b..6101d6e 100644
--- a/src/components/ui/CopilotSelector.tsx
+++ b/src/components/ui/CopilotSelector.tsx
@@ -1,12 +1,12 @@
import { UserCompanySelector } from 'copilot-design-system'
-import { useUserChannel } from '@/features/sync/hooks/useUserChannel'
-import type { SelectorValue } from '@/features/sync/types'
+import type { SelectorClientsCompanies, SelectorValue } from '@/features/sync/types'
import type { UserCompanySelectorInputValue } from '@/lib/copilot/types'
type CopilotSelectorProps = {
name: string
initialValue?: SelectorValue[]
onChange: (val: UserCompanySelectorInputValue[]) => void
+ options: SelectorClientsCompanies
disabled?: boolean
}
@@ -15,9 +15,8 @@ export const CopilotSelector = ({
initialValue,
onChange,
disabled,
+ options,
}: CopilotSelectorProps) => {
- const { userChannelList } = useUserChannel()
-
if (typeof window !== 'undefined')
return (
@@ -25,8 +24,8 @@ export const CopilotSelector = ({
name={name}
initialValue={initialValue}
placeholder="Select File Channel"
- clientUsers={userChannelList.clients ?? []}
- companies={userChannelList.companies ?? []}
+ clientUsers={options.clients ?? []}
+ companies={options.companies ?? []}
grouped={true}
limitSelectedOptions={1}
onChange={onChange}
diff --git a/src/features/dropbox/lib/Dropbox.service.ts b/src/features/dropbox/lib/Dropbox.service.ts
index 1f00d5a..68dff2b 100644
--- a/src/features/dropbox/lib/Dropbox.service.ts
+++ b/src/features/dropbox/lib/Dropbox.service.ts
@@ -4,6 +4,7 @@ import type { NextRequest } from 'next/server'
import { MAX_FETCH_DBX_RESOURCES, MAX_FETCH_DBX_SEARCH_LIMIT } from '@/constants/limits'
import { ObjectType } from '@/db/constants'
import APIError from '@/errors/APIError'
+import { MapFilesService } from '@/features/sync/lib/MapFiles.service'
import type { Folder } from '@/features/sync/types'
import AuthenticatedDropboxService from '@/lib/dropbox/AuthenticatedDropbox.service'
import logger from '@/lib/logger'
@@ -30,7 +31,7 @@ export class DropboxService extends AuthenticatedDropboxService {
throw new APIError('Cannot fetch the folders', searchResponse.status)
}
- return this.formatSearchResults(searchResponse.result.matches)
+ return await this.formatSearchResults(searchResponse.result.matches)
}
// Now this call will be rooted in the Team Space
@@ -47,11 +48,15 @@ export class DropboxService extends AuthenticatedDropboxService {
}
logger.info('DropboxService#getFolderTree :: Fetched folder tree', entries)
- return this.buildFolderTree(entries)
+ return await this.buildFolderTree(entries)
}
- private buildFolderTree(entries: files.ListFolderResult['entries']): Folder[] {
+ private async buildFolderTree(entries: files.ListFolderResult['entries']): Promise {
const root: Folder[] = []
+ const mapService = new MapFilesService(this.user, this.connectionToken)
+ const mapList = (await mapService.getAllChannelMaps()).map(
+ (channelMap) => channelMap.dbxRootPath,
+ )
const findOrCreate = (children: Folder[], path: string, label: string) => {
let node = children.find((c) => c.path === path)
@@ -65,7 +70,10 @@ export class DropboxService extends AuthenticatedDropboxService {
logger.info('DropboxService#buildFolderTree :: Building folder tree', entries)
for (const item of entries) {
if (item['.tag'] === ObjectType.FOLDER) {
- const parts = item.path_display?.split('/').filter(Boolean)
+ // below condition is to make sure the map is one-to-one
+ if (!item.path_display || mapList.includes(item.path_display)) continue
+
+ const parts = item.path_display.split('/').filter(Boolean)
let currentChildren: Folder[] = root
let currentPath = ''
@@ -84,13 +92,22 @@ export class DropboxService extends AuthenticatedDropboxService {
return root
}
- private formatSearchResults(matches: files.SearchMatchV2[]) {
+ private async formatSearchResults(matches: files.SearchMatchV2[]) {
+ const mapService = new MapFilesService(this.user, this.connectionToken)
+ const mapList = (await mapService.getAllChannelMaps()).map(
+ (channelMap) => channelMap.dbxRootPath,
+ )
+
return matches
.map((match) => {
if (match.metadata['.tag'] === 'other') return null
const data = match.metadata.metadata
if (data['.tag'] !== ObjectType.FOLDER) return null
+
+ // below condition is to make sure the map is one-to-one
+ if (!data.path_display || mapList.includes(data.path_display)) return null
+
return {
path: data.path_display,
label: data.path_display,
diff --git a/src/features/sync/components/Table.tsx b/src/features/sync/components/Table.tsx
index 948cad4..bdab10f 100644
--- a/src/features/sync/components/Table.tsx
+++ b/src/features/sync/components/Table.tsx
@@ -8,7 +8,7 @@ import { cn } from '@/components/utils'
import LastSyncAt from '@/features/sync/components/LastSyncedAt'
import { getCompanySelectorValue } from '@/features/sync/helper/sync.helper'
import { useFolder } from '@/features/sync/hooks/useFolder'
-import { useTable } from '@/features/sync/hooks/useTable'
+import { useTable, useUpdateUserList } from '@/features/sync/hooks/useTable'
import { useUserChannel } from '@/features/sync/hooks/useUserChannel'
import { generateRandomString } from '@/utils/random'
@@ -55,8 +55,9 @@ const MappingTableRow = () => {
filteredValue,
onDropboxFolderChange,
} = useTable()
- const { tempMapList, userChannelList, syncedPercentage } = useUserChannel()
- const { folderTree, isFolderTreeLoading } = useFolder()
+ const { unselectedChannelList } = useUpdateUserList()
+ const { isFolderTreeLoading } = useFolder()
+ const { tempMapList, userChannelList, syncedPercentage, tempFolders } = useUserChannel()
if (isFolderTreeLoading) {
return (
@@ -82,13 +83,14 @@ const MappingTableRow = () => {
)}
onChange={(val) => onUserSelectorValueChange(val, index)}
disabled={!!mapItem.status}
+ options={unselectedChannelList || {}}
/>
|
onDropboxFolderChange(val, index)}
- options={folderTree}
+ options={tempFolders || []}
placeholder="Search Dropbox folder"
disabled={!!mapItem.status}
/>
diff --git a/src/features/sync/context/UserChannelContext.tsx b/src/features/sync/context/UserChannelContext.tsx
index bb1ec8d..bd2029a 100644
--- a/src/features/sync/context/UserChannelContext.tsx
+++ b/src/features/sync/context/UserChannelContext.tsx
@@ -1,13 +1,15 @@
'use client'
import { createContext, type ReactNode, useState } from 'react'
-import type { MapList, SelectorClientsCompanies } from '@/features/sync/types'
+import type { Folder, MapList, SelectorClientsCompanies } from '@/features/sync/types'
export type UserChannelContextType = {
userChannelList: SelectorClientsCompanies
mapList: MapList[]
tempMapList: MapList[]
syncedPercentage?: { [key: string]: number }
+ folders?: Folder[]
+ tempFolders?: Folder[]
}
export const UserChannelContext = createContext<
@@ -29,6 +31,8 @@ export const UserChannelContextProvider = ({
mapList,
tempMapList,
syncedPercentage: {},
+ folders: [],
+ tempFolders: [],
})
return (
diff --git a/src/features/sync/hooks/useFolder.ts b/src/features/sync/hooks/useFolder.ts
index efe64ba..97b52a7 100644
--- a/src/features/sync/hooks/useFolder.ts
+++ b/src/features/sync/hooks/useFolder.ts
@@ -1,10 +1,10 @@
import { useCallback, useEffect, useState } from 'react'
import { useAuthContext } from '@/features/auth/hooks/useAuth'
-import type { Folder } from '@/features/sync/types'
+import { useUserChannel } from '@/features/sync/hooks/useUserChannel'
export const useFolder = () => {
const { user, connectionStatus } = useAuthContext()
- const [folderTree, setFolderTree] = useState([])
+ const { setUserChannel } = useUserChannel()
const [isFolderTreeLoading, setIsFolderTreeLoading] = useState(true)
const getPathOptions = useCallback(async () => {
@@ -21,14 +21,14 @@ export const useFolder = () => {
},
})
const resp = await response.json()
- setFolderTree(resp.folders)
+ setUserChannel((prev) => ({ ...prev, folders: resp.folders, tempFolders: resp.folders }))
setIsFolderTreeLoading(false)
- }, [user.token, connectionStatus])
+ }, [user.token, connectionStatus, setUserChannel])
useEffect(() => {
// biome-ignore lint/nursery/noFloatingPromises: floating promises are fine here
getPathOptions()
}, [getPathOptions])
- return { folderTree, isFolderTreeLoading }
+ return { isFolderTreeLoading }
}
diff --git a/src/features/sync/hooks/useSubHeader.ts b/src/features/sync/hooks/useSubHeader.ts
index 249c8a0..240ee85 100644
--- a/src/features/sync/hooks/useSubHeader.ts
+++ b/src/features/sync/hooks/useSubHeader.ts
@@ -1,7 +1,8 @@
import { useUserChannel } from '@/features/sync/hooks/useUserChannel'
+import { getFreshFolders } from '@/helper/table.helper'
export const useSubHeader = () => {
- const { setUserChannel, tempMapList } = useUserChannel()
+ const { setUserChannel, tempMapList, folders } = useUserChannel()
const handleAddRule = () => {
const lastMap = tempMapList?.[tempMapList.length - 1]
@@ -24,6 +25,7 @@ export const useSubHeader = () => {
lastSyncedAt: null,
},
],
+ tempFolders: getFreshFolders(tempMapList, folders),
}))
}
}
diff --git a/src/features/sync/hooks/useTable.ts b/src/features/sync/hooks/useTable.ts
index 96a9e31..0698628 100644
--- a/src/features/sync/hooks/useTable.ts
+++ b/src/features/sync/hooks/useTable.ts
@@ -1,13 +1,14 @@
-import { useState } from 'react'
+import { useCallback, useState } from 'react'
import { useAuthContext } from '@/features/auth/hooks/useAuth'
import { useUserChannel } from '@/features/sync/hooks/useUserChannel'
import type { MapList } from '@/features/sync/types'
import { postFetcher } from '@/helper/fetcher.helper'
+import { getFreshFolders } from '@/helper/table.helper'
import { type UserCompanySelectorInputValue, UserCompanySelectorObject } from '@/lib/copilot/types'
export const useTable = () => {
const { user } = useAuthContext()
- const { tempMapList, setUserChannel, userChannelList } = useUserChannel()
+ const { tempMapList, setUserChannel, userChannelList, folders } = useUserChannel()
const [fileChannelIds, setFileChannelIds] = useState<{
[key: number]: string
}>()
@@ -58,6 +59,10 @@ export const useTable = () => {
setUserChannel((prev) => ({
...prev,
tempMapList: prev.tempMapList.filter((_, i) => i !== index),
+ tempFolders: getFreshFolders(
+ prev.tempMapList.filter((_, i) => i !== index),
+ folders,
+ ),
}))
setFilteredValue((prev) => ({ ...prev, [index]: null }))
}
@@ -115,3 +120,22 @@ export const useTable = () => {
handleSyncStatusChange,
}
}
+
+export const useUpdateUserList = () => {
+ const { userChannelList, tempMapList } = useUserChannel()
+ const selectedChannelIds = tempMapList.map((map) => map.fileChannelId)
+
+ const getNewChannelList = useCallback(() => {
+ const newClientList = userChannelList.clients?.filter(
+ (client) => client.fileChannelId && !selectedChannelIds.includes(client.fileChannelId),
+ )
+ const newCompanyList = userChannelList.companies?.filter(
+ (company) => company.fileChannelId && !selectedChannelIds.includes(company.fileChannelId),
+ )
+ const newChannelList = { clients: newClientList, companies: newCompanyList }
+
+ return newChannelList
+ }, [userChannelList, selectedChannelIds])
+
+ return { unselectedChannelList: getNewChannelList() }
+}
diff --git a/src/helper/table.helper.ts b/src/helper/table.helper.ts
new file mode 100644
index 0000000..6d4ff08
--- /dev/null
+++ b/src/helper/table.helper.ts
@@ -0,0 +1,7 @@
+import type { Folder, MapList } from '@/features/sync/types'
+
+export function getFreshFolders(mapList: MapList[], folders?: Folder[]) {
+ if (!folders) return []
+ const rootPathList = mapList.map((mapItem) => mapItem.dbxRootPath)
+ return folders.filter((folder) => !rootPathList.includes(folder.path))
+}
|