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 -> Subfoldermax 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_filesINSERT/SELECT usejwt_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 injwt_org_ids()(directorganization_usersrows only). SELECT keepsvisibility = 'shared'for teammates; `internal` rows are visible to the uploader, parent org admins, and not to other fund users. - Folders:
dropbox_foldersorg-member INSERT/SELECT aligned with the same tree + visibility pattern (sharedvscreated_byfor internal). - Shares:
dropbox_sharesINSERT checksjwt_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.tsxresolvesselectedEntity→getActualOrgId()and passesorganization_idintouploadDropboxFile()andcreateDropboxFolder(). Fund users were failing inserts with42501whenorganization_idwas 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 SupabasecontentTypefromfile.type(fallbackapplication/octet-stream) instead of forcingimage/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:
handleDownloadnow 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-5xltomax-w-7xlwithmin-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
textValueprop to richSelectItemcomponents 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:
placeholderDatanow useskeepPreviousDataimport (was(prev: any) => prev) - P2: Preview
DialogFooterand ShareDialogFooternow have responsive stacking (flex-col sm:flex-row) - P2: Text file iframe uses
bg-cardinstead of hardcodedbg-white - P2: Filename truncation widened from
max-w-[200px]tomax-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
useFileDropZonehook (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
placeholderDatato both queries (prevents flash-empty on entity switch) - P2: Removed
table-fixedfrom desktop table (auto layout per styling guide) - P2: Fixed Category column alignment for folder rows (
text-centerto match header) - P2: Added
bg-primary/10CSS utility toindex.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_filestable - 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_orgrole - 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; metadataorganization_idlives ondropbox_filesrows. - 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_orguser 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 onlyfolderId = undefined→ all files regardless of folderfolderId = "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 fromgetActualOrgId(selectedEntity)for cache segmentation on entity switch) - Stale time: 30 seconds
- Automatic refetch on upload/delete
Local State
uploadDialogOpen- Upload dialog visibilitydeleteDialogOpen- Delete confirmation visibilitysearchQuery- Search filter textcategoryFilter- Selected category filterselectedUploads- 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-nullorganization_idthat appears in their JWT org set;NULLfails 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/createDropboxFolderreceiveorganizationIdfromgetActualOrgId(selectedEntity)inDropBox.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
dropboxstorage 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_filestable (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