📁 feat: Integrate SharePoint File Picker and Download Workflow (#8651)

* feat(sharepoint): integrate SharePoint file picker and download workflow
Introduces end‑to‑end SharePoint import support:
* Token exchange with Microsoft Graph and scope management (`useSharePointToken`)
* Re‑usable hooks: `useSharePointPicker`, `useSharePointDownload`,
  `useSharePointFileHandling`
* FileSearch dropdown now offers **From Local Machine** / **From SharePoint**
  sources and gracefully falls back when SharePoint is disabled
* Agent upload model, `AttachFileMenu`, and `DropdownPopup` extended for
  SharePoint files and sub‑menus
* Blurry overlay with progress indicator and `maxSelectionCount` limit during
  downloads
* Cache‑flush utility (`config/flush-cache.js`) supporting Redis & filesystem,
  with dry‑run and npm script
* Updated `SharePointIcon` (uses `currentColor`) and new i18n keys
* Bug fixes: placeholder syntax in progress message, picker event‑listener
  cleanup
* Misc style and performance optimizations

* Fix ESLint warnings

---------

Co-authored-by: Atef Bellaaj <slalom.bellaaj@external.daimlertruck.com>
This commit is contained in:
Danny Avila 2025-07-25 00:03:23 -04:00
parent b6413b06bc
commit a955097faf
No known key found for this signature in database
GPG key ID: BF31EEB2C5CA0956
40 changed files with 2500 additions and 123 deletions

View file

@ -29,7 +29,8 @@ interface DropdownProps {
type MenuProps = Omit<
DropdownProps,
'trigger' | 'isOpen' | 'setIsOpen' | 'focusLoop' | 'mountByState'
>;
> &
Ariakit.MenuProps;
const DropdownPopup: React.FC<DropdownProps> = ({
trigger,
@ -70,7 +71,9 @@ const Menu: React.FC<MenuProps> = ({
finalFocus,
unmountOnHide,
preserveTabOrder,
...props
}) => {
const menuStore = Ariakit.useMenuStore();
const menu = Ariakit.useMenuContext();
return (
<Ariakit.Menu
@ -83,13 +86,53 @@ const Menu: React.FC<MenuProps> = ({
unmountOnHide={unmountOnHide}
preserveTabOrder={preserveTabOrder}
className={cn('popover-ui z-50', className)}
{...props}
>
{items
.filter((item) => item.show !== false)
.map((item, index) => {
const { subItems } = item;
if (item.separate === true) {
return <Ariakit.MenuSeparator key={index} className="my-1 h-px bg-white/10" />;
}
if (subItems && subItems.length > 0) {
return (
<Ariakit.MenuProvider
store={menuStore}
key={`${keyPrefix ?? ''}${index}-${item.id ?? ''}-provider`}
>
<Ariakit.MenuButton
className={cn(
'group flex w-full cursor-pointer items-center justify-between gap-2 rounded-lg px-3 py-3.5 text-sm text-text-primary outline-none transition-colors duration-200 hover:bg-surface-hover focus:bg-surface-hover md:px-2.5 md:py-2',
itemClassName,
)}
disabled={item.disabled}
id={item.id}
render={item.render}
ref={item.ref}
// hideOnClick={item.hideOnClick}
>
<span className="flex items-center gap-2">
{item.icon != null && (
<span className={cn('mr-2 size-4', iconClassName)} aria-hidden="true">
{item.icon}
</span>
)}
{item.label}
</span>
<Ariakit.MenuButtonArrow className="stroke-1 text-base opacity-75" />
</Ariakit.MenuButton>
<Menu
items={subItems}
menuId={`${menuId}-${index}`}
key={`${keyPrefix ?? ''}${index}-${item.id ?? ''}`}
gutter={12}
portal={true}
/>
</Ariakit.MenuProvider>
);
}
return (
<Ariakit.MenuItem
key={`${keyPrefix ?? ''}${index}-${item.id ?? ''}`}