Skip to main content
Back to ScopeForged

ScopeForged Documentation

Technical documentation, guides, and feature references for the ScopeForged client portal.

Search & Analytics/Client Portal Search

Client Portal Search Guide

Last Updated: 2026-01-17 Status: Implemented Plan Reference: 131-client-portal-search.md


Overview

The Client Portal Search feature provides a global search capability for client users to quickly find projects, files, invoices, conversations, and documents across their entire account. The search is accessible from the header bar and supports keyboard navigation.


Table of Contents

  1. Accessing Search
  2. Search Features
  3. Searchable Content
  4. How to Use
  5. Technical Architecture
  6. Security
  7. Related Features

Access PointLocationURLRole
Search BarHeader (navigation)/portal-search (API)Client

Note: The search feature is only available to client users (non-admin). Admin users have separate admin search functionality.

Keyboard Shortcuts

ShortcutAction
Ctrl+K / Cmd+KFocus search input
/ Navigate results
EnterSelect/navigate to result
EscapeClose search results

Search Features

  • Results appear as you type (debounced 300ms)
  • Minimum 2 characters required
  • Up to 5 results per category

Result Grouping

Results are organized by type:

  • Projects - Active and completed projects
  • Files - Uploaded project files
  • Invoices - All invoices
  • Conversations - Message threads
  • Document Requests - Pending and completed requests

Recent Searches

  • Last 5 search queries stored in localStorage
  • Displayed when search input is empty
  • Click to re-run previous search

Result Format

Each result displays:

[Icon] Title
       Subtitle (type, date, status)

Examples:

[Folder] Website Redesign
         Project · Active · Due Jan 30

[File] Logo_Final_v3.png
       File · Website Redesign · Uploaded Jan 15

[Invoice] INV-2026-0042
          Invoice · $5,000 · Paid

[Chat] API Integration Discussion
       Conversation · 3 messages · Jan 12

Searchable Content

Search Scope by Entity

EntitySearchable Fields
Projectsname, description
Filesoriginal_filename, description
Invoicesnumber, notes, total (as string)
Conversationssubject
Document Requeststitle, description

Search Behavior

  • Case-insensitive matching
  • Partial matching supported
  • Results filtered by user's client associations
  • Non-visible files excluded (is_client_visible = false)

How to Use

  1. Click the search bar or press Ctrl+K / Cmd+K
  2. Type your search query (minimum 2 characters)
  3. Results appear grouped by type
  4. Click a result or use arrow keys + Enter to navigate

Filtering Results

Currently, results are filtered automatically by:

  • User's associated clients only
  • Active/visible items
  • File visibility settings

Understanding Results

Projects:

  • Shows project name and status
  • Displays due date if set
  • Links to project detail page

Files:

  • Shows filename and parent project
  • Displays upload date
  • Links directly to file download or preview

Invoices:

  • Shows invoice number and amount
  • Displays payment status
  • Links to invoice detail page

Conversations:

  • Shows subject line
  • Displays message count
  • Links to conversation thread

Document Requests:

  • Shows request title
  • Displays status and due date
  • Links to request detail page

Technical Architecture

Service

Location: app/Services/Portal/PortalSearchService.php

class PortalSearchService
{
    public function search(User $user, string $query, int $limit = 5): SearchResults
    {
        $clients = $user->clients;

        return new SearchResults(
            projects: $this->searchProjects($clients, $query, $limit),
            files: $this->searchFiles($clients, $query, $limit),
            invoices: $this->searchInvoices($clients, $query, $limit),
            conversations: $this->searchConversations($clients, $query, $limit),
            documentRequests: $this->searchDocumentRequests($clients, $query, $limit),
        );
    }
}

Controller

Location: app/Http/Controllers/Portal/PortalSearchController.php

MethodRouteDescription
__invoke()GET /portal-searchJSON API endpoint

Request Parameters:

ParameterTypeRequiredDescription
qstringYesSearch query (min 2 chars)
limitintNoResults per category (default 5)

Response Format:

{
  "projects": [
    {"id": 1, "name": "Website Redesign", "status": "active", "url": "/portal/projects/1"}
  ],
  "files": [...],
  "invoices": [...],
  "conversations": [...],
  "documentRequests": [...]
}

DTO

Location: app/DataTransferObjects/SearchResults.php

class SearchResults implements JsonSerializable
{
    public function __construct(
        public Collection $projects,
        public Collection $files,
        public Collection $invoices,
        public Collection $conversations,
        public Collection $documentRequests,
    ) {}

    public function jsonSerialize(): array
    {
        return [
            'projects' => $this->projects,
            'files' => $this->files,
            'invoices' => $this->invoices,
            'conversations' => $this->conversations,
            'documentRequests' => $this->documentRequests,
        ];
    }
}

Component

Location: resources/views/components/portal/search.blade.php

Alpine.js component with:

  • Debounced input handling
  • Keyboard navigation
  • Recent searches in localStorage
  • Result grouping and display

Routes

// In routes/portal-routes.php
Route::get('/portal-search', PortalSearchController::class)
    ->name('portal.search')
    ->middleware('auth');

Database Indexes

Migration adds performance indexes:

  • projects.name
  • invoices.number
  • project_files.original_filename
  • conversations.subject
  • document_requests.title

Security

Data Isolation

  • Search only returns items the authenticated user has access to
  • Results filtered by user's client associations ($user->clients)
  • Non-visible files excluded (is_client_visible = false)

Input Validation

  • Query sanitization via Laravel validation
  • Minimum 2 character query length
  • Maximum limit parameter enforced

Admin Access

  • Admin users return empty results (no client associations)
  • Admin search uses separate admin functionality

Testing

Test Coverage

Location: tests/Feature/Portal/SearchTest.php

All 12 tests passing:

  • Authentication required
  • Minimum query length validation
  • Returns results for each entity type
  • Client data isolation (only own data)
  • File visibility filtering
  • Empty results handling
  • Limit parameter support
  • Admin access returns empty results

Running Tests

php artisan test tests/Feature/Portal/SearchTest.php

Dependencies

FeatureRelationship
AuthenticationUser login required
AuthorizationClient data scoping

Complementary Features

FeatureDescription
Client DashboardPortal home with quick access
Project ManagementProject viewing
File SharingFile access
InvoicingInvoice viewing
MessagingConversation threads

Future Enhancements

  • Full-text search with relevance ranking
  • Filters (date range, status, type)
  • Search within file contents (PDF text extraction)
  • Saved searches
  • Search analytics dashboard
  • Voice search on mobile

Troubleshooting

IssueSolution
No results appearingVerify minimum 2 character query
Missing expected resultsCheck user-client associations
Files not showingVerify is_client_visible flag
Slow searchCheck database indexes exist
Keyboard shortcuts not workingEnsure focus is not in another input field

See Also