Skip to main content

My Files Component

My Files Component

Component File: src/features/tools/components/DropBox.tsx Location: My Workspace → My Files Route / navigation: Browser path `/my-workspace/my-files` (canonical); Zustand workspaceTool = `dropbox` (internal id). Legacy /my-workspace/dropbox still resolves. See ../../workspace/00-WORKSPACE-HUB.md. Usage: File sharing between ministries and parent org Access Level: All authenticated users (parent_org and fund_user) Platform: Desktop and Mobile Last Updated: April 16, 2026 Status: ✅ Production Ready

---

Recent Updates (April 16, 2026)

Folder drag-and-drop imports (structure-preserving)

  • New: Dragging a folder from the OS into My Files now uploads all nested files in one batch and preserves folder hierarchy inside My Files.
  • Hierarchy behavior: During upload, the UI resolves or creates matching folders under the current location using existing folder records when names already exist.
  • Depth guard: My Files still enforces Root -> Folder -> Subfolder max depth. Files deeper than that are skipped with a toast instead of failing the entire batch.
  • Batch dialog: Upload dialog now supports multi-file batches (including dropped folders), shows file count/total size, and applies one category/description/visibility choice to all selected files.
  • Hover overlay fix: Drag overlay now uses a stable drag-depth counter and a stronger backdrop layer to prevent flicker/text bleed while hovering over the upload area.

Import Summary Dialog (batch uploads)

  • New shared component: ImportSummaryDialog (src/components/shared/ImportSummaryDialog.tsx) displays structured import/upload results.
  • Batch UX: After batch uploads (2+ files), a summary dialog shows counts for: files uploaded, folders created, failed uploads, and files skipped due to folder depth limits.
  • Single-file UX: Single-file uploads continue to use lightweight toast notifications.
  • Reusable: The component accepts generic ImportSummaryItem[] for any import workflow (CSV imports, bulk actions, etc.).

Recent Updates (March 25, 2026)

My Files RLS org-tree + visibility (evening update)

  • RLS: dropbox_files INSERT/SELECT use jwt_is_in_org_tree(organization_id) so parent-org staff can upload and list files for a child fund selected in the workspace header even when that fund UUID is not listed in jwt_org_ids() (direct organization_users rows only). SELECT keeps visibility = 'shared' for teammates; `internal` rows are visible to the uploader, parent org admins, and not to other fund users.
  • Folders: dropbox_folders org-member INSERT/SELECT aligned with the same tree + visibility pattern (shared vs created_by for internal).
  • Shares: dropbox_shares INSERT checks jwt_is_in_org_tree(df.organization_id) for the linked file.
  • UI: Upload dialog includes Who can see this file? (default: everyone in the fund). Row menu: Visible to fund / Only visible to me (uploader or parent org admin). Private files show a Private badge.
  • API: uploadDropboxFile(..., visibility?) defaults to 'shared'; updateDropboxFileVisibility(fileId, visibility) updates the row.

Organization ID on upload (RLS / JWT migration)

  • Fix: DropBox.tsx resolves selectedEntitygetActualOrgId() and passes organization_id into uploadDropboxFile() and createDropboxFolder(). Fund users were failing inserts with 42501 when organization_id was missing on the row; policies also require the org to pass JWT checks (now org-tree aware).
  • UX: Non–parent-org users see a toast if the workspace header has no resolvable fund/org, or while entity mapping is still loading; drag-and-drop uses the same guard via useFileDropZone. RLS denials show an extra hint to re-login after membership changes.
  • Storage: uploadDropboxFile() sets Supabase contentType from file.type (fallback application/octet-stream) instead of forcing image/jpeg.

Recent Updates (February 14, 2026)

Preview Expansion — Office Document Support

  • New: Word (.doc/.docx), Excel (.xls/.xlsx), and PowerPoint files now preview in-dialog using Microsoft Office Online Viewer
  • Fix: All file types now open the preview dialog (previously, Office docs called window.open() which triggered a download)
  • Fix: handleDownload now uses fetch+blob pattern for reliable cross-origin downloads from Supabase signed URLs
  • Removed: "Preview" dropdown menu item (redundant — row click already opens preview)
  • UX: Preview dialog enlarged from max-w-5xl to max-w-7xl with min-h-[500px] content area for better readability

Share Dialog Consolidation

  • Merged: "Share" and "Manage Shares" dialogs consolidated into a single dialog
  • UX: Existing share links shown at top of dialog, create form below — no more bouncing between two dialogs
  • UX: Newly created shares appear at top of list with highlight animation (border-primary bg-primary/5)
  • UX: Share link auto-copied to clipboard on creation
  • Removed: Separate "Manage Shares" dropdown menu item and "Link Created" success dialog
  • State reduction: Eliminated ~8 state variables, replaced with ~4

Code Quality Fixes

  • P1: Added textValue prop to rich SelectItem components in share type selector (Playbook Section 24 bug)
  • P1: Updated "fiscal sponsor" → "parent org" in 5 locations (comments, tooltips, select items)
  • P1: Removed unused imports (Globe, Key, Settings)
  • P2: placeholderData now uses keepPreviousData import (was (prev: any) => prev)
  • P2: Preview DialogFooter and Share DialogFooter now have responsive stacking (flex-col sm:flex-row)
  • P2: Text file iframe uses bg-card instead of hardcoded bg-white
  • P2: Filename truncation widened from max-w-[200px] to max-w-[300px] lg:max-w-[400px]

ShareFilePage Updates

  • New: Office document preview via Microsoft Office Online Viewer (matches main My Files preview)
  • New: Text/CSV file preview support added

CSS Utilities Added (src/index.css)

  • .border-primary
  • .min-h-[500px]
  • .h-[75vh]
  • .max-w-[300px]
  • .lg:max-w-[400px]

Previous Updates (February 12, 2026)

Drag-and-Drop File Upload

  • New: Users can drag files from their OS file explorer directly onto the My Files card to upload
  • Shared Hook: Created useFileDropZone hook (src/hooks/useFileDropZone.ts) — reusable across components
  • OS vs Internal Drag Detection: Uses dataTransfer.types.includes('Files') to distinguish OS file drags from internal row reordering drags
  • Visual Overlay: Desktop-only dashed border overlay appears when dragging a file over the card (border-primary bg-primary/10)
  • Validation: File type and size validated on drop (same rules as Upload button: 50MB max, PDF/Word/Excel/Images/CSV/Text)
  • Disabled Guard: Shows toast error if no organization is selected
  • Empty State: Updated to say "Drag a file here or click Upload to get started" when org is selected

Minor Fixes

  • P2: Added placeholderData to both queries (prevents flash-empty on entity switch)
  • P2: Removed table-fixed from desktop table (auto layout per styling guide)
  • P2: Fixed Category column alignment for folder rows (text-center to match header)
  • P2: Added bg-primary/10 CSS utility to index.css

Previous Updates (January 27, 2026)

File Move Bug Fix

  • Fixed: Drag-and-drop file move was showing success toast but not actually moving files
  • Root Cause: Missing UPDATE RLS policy on dropbox_files table
  • Solution: Added UPDATE policies for parent org admins and file uploaders

Previous Updates (January 26, 2026)

Folder Support Added

  • Create Folders - Organize files into folders (max 2 levels deep: Root → Folder → Subfolder)
  • Breadcrumb Navigation - Navigate folder hierarchy with clickable breadcrumbs
  • Drag-and-Drop - Drag files/folders onto other folders or breadcrumbs to move them (desktop)
  • Move To Dialog - Mobile-friendly "Move to..." action for organizing files
  • Folder Actions - Rename, delete (with option to move files to root or delete), move folders
  • Independent Visibility - Each folder has its own visibility setting (shared/internal)

Previous Updates (January 13, 2026)

  • Storage RLS Policy Fix - Updated storage policies to check for parent_org role
  • Parent org admins can now properly upload, download, and delete files from the dropbox storage bucket

---

Overview

The My Files component provides a secure file sharing system for internal document exchange between ministries and their parent org. Both user types have access:

  • Fund Users (fund_user): Upload documents for their parent org to review (financial statements, receipts, tax forms, etc.)
  • Parent Org Admins (parent_org): Share documents back to funds, view all organization files, set visibility controls

UI Features

Header Section

  • Page title "My Files"
  • Contextual subtitle based on user role
  • Back button to return to My Workspace

Actions Bar

  • Search: Filter files by name, description, or uploader
  • Category Filter: Dropdown to filter by file category
  • Upload Files Button: Opens upload dialog (multi-file capable)

File Categories

  • Financial Documents - Financial statements, budgets, reports
  • Tax Forms - W-9s, 1099s, tax-related documents
  • Receipts - Expense receipts, purchase documentation
  • Contracts - Agreements, MOUs, legal documents
  • Other - Miscellaneous documents

Files Table

  • File name with icon (based on file type)
  • Category badge (color-coded)
  • Uploaded by name
  • File size
  • Upload date
  • Visibility status (parent org only)
  • Actions dropdown (Preview, Download, Delete)

Upload Dialog

  • Selected file (or batch) preview with icon, count, and total size
  • Shows dropped relative paths for folder imports when multiple files are selected
  • Category selector
  • Description field (optional)
  • Visibility selector (parent org only):
  • Shared: Visible to fund and parent org
  • Internal: Visible to parent org only

Folder Drop Import Behavior

  • Dragging a folder into My Files imports all nested files in one pass.
  • Folder hierarchy is recreated/reused relative to the current folder.
  • Existing folders with matching names are reused; missing folders are created.
  • Maximum depth remains Root -> Folder -> Subfolder. Files beyond that depth are skipped with a user-facing toast.

Delete Confirmation

  • AlertDialog confirmation before deletion
  • Shows filename being deleted

Access Control

Fund Users (fund_user)

  • Can upload files to their organization
  • Can view files with "shared" visibility
  • Can delete their own uploads
  • Cannot see "internal" visibility files

Parent Org Admins (parent_org)

  • Can upload files to any organization
  • Can view all files (shared and internal)
  • Can delete any file
  • Can set visibility on uploads

Allowed File Types

| Type | Extensions | MIME Types |
|------|------------|------------|
| PDF | .pdf | `application/pdf` |
| Word | .doc, .docx | `application/msword`, `application/vnd.openxmlformats-officedocument.wordprocessingml.document` |
| Excel | .xls, .xlsx | `application/vnd.ms-excel`, `application/vnd.openxmlformats-officedocument.spreadsheetml.sheet` |
| Images | .jpg, .png, .gif | `image/jpeg`, `image/png`, `image/gif` |
| Text | .txt, .csv | `text/plain`, `text/csv` |

Maximum File Size: 50MB

Download & Preview

  • Download: Creates a signed URL (1 hour expiry), fetches as blob, and triggers browser download with original filename
  • Preview: Click any file row to open an in-dashboard preview dialog:
  • PDF — Direct iframe embed
  • Images — Direct <img> with object-contain
  • Text/CSV — Direct iframe embed
  • Word/Excel/PowerPoint — Microsoft Office Online Viewer (view.officeapps.live.com) iframe
  • Other types — File info card with Download button

Share Links

Single "Share" dialog combines link creation and management:

  • Existing Links section at top (copy, revoke, view stats)
  • Create New Link form below (type, password, expiration, access limits, download toggle)
  • Internal: Requires login (team members only)
  • External: Requires password (bcrypt-hashed server-side)
  • Public: Disabled

Edge Functions:

  • create-dropbox-share (token generation + password hashing)
  • validate-dropbox-share (validation + signed URL + access count)

Troubleshooting upload error 42501 (RLS)

PostgreSQL code 42501 on dropbox_files INSERT means the row failed RLS WITH CHECK (permission denied), not a storage bucket error.

Verify data (SQL Editor, service role or postgres):

1. Staff membership for the affected auth.users id:

   select organization_id, role, position
   from public.organization_users
   where user_id = 'USER_UUID_HERE';

2. Workspace org for the entity slug (e.g. infocus):

   select id, slug, parent_organization_id
   from public.organizations
   where slug = 'infocus';

3. Consistency: The organization_id passed from the app (getActualOrgId(selectedEntity)) must be an org the user may access. After fixing organization_users, the user should sign out and back in so the custom access token hook refreshes org_ids / org_tree_ids in the JWT (see DEVELOPER-PLAYBOOK §46).

Compare with mailing lists: See ../fundraising/09-MAILING-LISTS.md (My Files / CSV import note).

---

Database Schema

dropbox_files Table

| Column | Type | Description |
|--------|------|-------------|
| `id` | UUID | Primary key |
| `organization_id` | UUID | Organization the file belongs to |
| `uploaded_by` | UUID | User who uploaded the file |
| `uploaded_by_name` | TEXT | Display name of uploader |
| `filename` | TEXT | Stored filename (with timestamp) |
| `original_filename` | TEXT | Original filename |
| `file_path` | TEXT | Path in Supabase Storage |
| `file_size` | BIGINT | File size in bytes |
| `file_type` | TEXT | MIME type |
| `category` | TEXT | File category |
| `description` | TEXT | Optional description |
| `visibility` | TEXT | 'shared' or 'internal' |
| `folder_id` | UUID | Reference to parent folder (null = root) |
| `created_at` | TIMESTAMPTZ | Upload timestamp |
| `updated_at` | TIMESTAMPTZ | Last update timestamp |

dropbox_folders Table

| Column | Type | Description |
|--------|------|-------------|
| `id` | UUID | Primary key |
| `organization_id` | UUID | Organization the folder belongs to |
| `parent_folder_id` | UUID | Parent folder (null = root level) |
| `name` | VARCHAR(255) | Folder name |
| `created_by` | UUID | User who created the folder |
| `created_by_name` | TEXT | Display name of creator |
| `visibility` | TEXT | 'shared' or 'internal' |
| `created_at` | TIMESTAMPTZ | Creation timestamp |
| `updated_at` | TIMESTAMPTZ | Last update timestamp |

Constraints:

  • Maximum 2 levels of nesting (Root → Folder → Subfolder)
  • Unique folder names within same parent folder and organization
  • Cascade delete: deleting a folder can move files to root or delete them

Supabase Storage

Bucket: dropbox

  • Private bucket (not publicly accessible)
  • Object paths (app): {userId}/{timestamp}_{sanitized_filename} — user-scoped first segment per playbook; metadata organization_id lives on dropbox_files rows.
  • Signed URLs generated for download/preview (1 hour expiry)
  • File size limit: 50MB
  • Allowed MIME types: PDF, Word, Excel, images (JPEG/PNG/GIF), text/CSV

Storage Policies (storage.objects):

  • SELECT: Parent org admins (parent_org user type) can access all files; users can access their org's folder
  • INSERT: Parent org admins can upload anywhere; users can upload to their org's folder
  • DELETE: Parent org admins can delete any file; users can delete files in their org's folder

Database Functions

File Functions

fetchDropboxFiles(category?, folderId?)

Fetches files (RLS-scoped to the current user). Optional category and folder filters.

  • folderId = null → root level files only
  • folderId = undefined → all files regardless of folder
  • folderId = "uuid" → files in specific folder

uploadDropboxFile(file, userId, userName, category, description?, folderId?, organizationId?, visibility?)

Uploads a file to Supabase Storage and creates a database record. Pass resolved organizationId from the workspace header for fund users (RLS). Optional visibility: 'shared' (default, everyone in the fund with My Files access) or 'internal' (uploader + parent org admins only).

When importing dropped folders, the UI resolves/creates each folder segment first, then calls uploadDropboxFile once per file with the resolved folderId.

updateDropboxFileVisibility(fileId, visibility)

Updates dropbox_files.visibility for share vs private-to-uploader behavior (RLS: uploader or parent org admin).

deleteDropboxFile(fileId, filePath)

Deletes file from storage and database.

getDropboxFileUrl(filePath)

Generates a signed URL for file download/preview.

moveDropboxFile(fileId, targetFolderId)

Moves a file to a different folder (or root if null).

Folder Functions

fetchDropboxFolders(parentFolderId?)

Fetches folders (RLS-scoped) at a specific level. Also used during folder-import uploads to find existing folders by name under each parent.

createDropboxFolder(name, parentFolderId, userId, userName, organizationId?)

Creates a new folder. Enforces max 2 levels of nesting. Pass organizationId from the workspace header for fund users (RLS). Used by folder-import uploads when a matching folder does not already exist.

renameDropboxFolder(folderId, newName)

Renames a folder. Validates unique name at same level.

deleteDropboxFolder(folderId, moveFilesToRoot)

Deletes a folder. If moveFilesToRoot is true, files are moved to root; otherwise deleted.

moveDropboxFolder(folderId, targetParentId)

Moves a folder to another parent (or root if null). Prevents circular references.

getDropboxFolderPath(folderId)

Returns array of folders from root to specified folder (for breadcrumb navigation).

getAllDropboxFolders(organizationId?)

Returns all folders for an organization (for move-to dialog).

State Management

React Query

  • Query keys: ['dropbox-files', organizationId, categoryFilter, currentFolderId], ['dropbox-folders', organizationId, currentFolderId] (organizationId from getActualOrgId(selectedEntity) for cache segmentation on entity switch)
  • Stale time: 30 seconds
  • Automatic refetch on upload/delete

Local State

  • uploadDialogOpen - Upload dialog visibility
  • deleteDialogOpen - Delete confirmation visibility
  • searchQuery - Search filter text
  • categoryFilter - Selected category filter
  • selectedUploads - Pending upload batch (file + normalized relative path)
  • uploadForm - Upload form data (category, description, visibility)
  • uploadSummaryOpen - Import Summary dialog visibility (batch uploads)
  • uploadSummaryResults - Structured counts (UploadResultsSummary) for the summary dialog

Notifications

| Event | Notification Type | Recipient |
|-------|------------------|-----------|
| File uploaded | `file_uploaded` | Parent org (when fund uploads) |

Row Level Security (RLS)

SELECT Policy

  • Parent org admins can view all files
  • Fund users can view files for their organization with "shared" visibility

INSERT Policy

  • Current (JWT helpers, batch7+): jwt_is_parent_org_admin() OR organization_id = ANY(jwt_org_ids()). Fund users must send a non-null organization_id that appears in their JWT org set; NULL fails RLS.
  • Parent org admins may insert with policies that do not require a matching org on every path; the app still passes the resolved header org when available for correct attribution.
  • Client: uploadDropboxFile / createDropboxFolder receive organizationId from getActualOrgId(selectedEntity) in DropBox.tsx.

UPDATE Policy (Added Jan 27, 2026)

  • Parent org admins can update/move any file
  • Users can update/move their own uploads

DELETE Policy

  • Users can delete their own uploads
  • Parent org admins can delete any file

Setup Instructions

1. Run SQL Migration

The migration 20251230_dropbox_storage_policies creates:

  • The dropbox storage bucket (if not exists)
  • Storage RLS policies for upload/download/delete
  • Proper MIME type and size restrictions

2. Verify Table Migration

Execute scripts/CREATE-DROPBOX-TABLE.sql in Supabase SQL Editor if the dropbox_files table doesn't exist.

3. Verify RLS Policies

Ensure RLS is enabled on both:

  • dropbox_files table (for database records)
  • storage.objects (for file storage - bucket_id = 'dropbox')

Related Documentation


Synced from IFMmvp-Frontend documentation: pages/tools/06-MY-FILES.md

Ready to Get Started?

See how Alignmint can simplify your nonprofit's operations. Schedule a free demo with our team and we'll walk you through everything.

Questions? Email us at steven@getalignmint.org

Ready to get started?Start Plus Trial