Skip to content

useDownloadActivityStore

A Pinia store for tracking download progress and status for file exports (Excel, PDF, CSV, Print). This store automatically tracks downloads initiated from FtxGrid components and can be used to display download progress in your application UI (e.g., sidebar download activity).

Features

  • Automatic Tracking - Downloads are automatically tracked when initiated via FtxGrid
  • Progress Updates - Real-time progress tracking (if supported by API)
  • Status Management - Tracks downloading, completed, and error states
  • Metadata Storage - Stores filename, format, table title, timestamps, and error messages
  • Reactive Updates - Pinia store provides reactive updates for UI components

Installation

The store is exported from @ftx/ui:

javascript
import { useDownloadActivityStore } from '@ftx/ui';

Note: The store requires Pinia to be installed and configured in your application. Make sure you have a Pinia instance set up before using this store.

Automatic Tracking

Downloads are automatically tracked when initiated through FtxGrid export/print functionality. No additional setup is required - the download helper integrates with the store automatically.

How It Works

When you use FtxGrid to export data (Excel, PDF, CSV, or Print), the download helper:

  1. Creates a unique download ID
  2. Adds the download to the store with downloading status
  3. Updates progress percentage as data downloads (if API supports progress events)
  4. Marks as completed on success or error on error
  5. Automatically removes completed downloads after a delay (10 seconds for PDF/Print, 2 seconds for others)

Disabling Automatic Tracking

If you need to disable tracking for a specific download, you can pass trackDownload: false to the startDownload function. However, this is an internal API and typically not needed for normal usage.

Usage

Basic Example: Display Active Downloads

vue
<template>
  <div class="download-activity">
    <div v-if="hasActiveDownloads">
      <h3>Active Downloads</h3>
      <div v-for="download in activeDownloads" :key="download.id" class="download-item">
        <div class="download-info">
          <div class="filename">{{ download.filename }}</div>
          <div class="table-title">{{ download.tableTitle }}</div>
        </div>
        <q-linear-progress 
          v-if="download.status === 'downloading'" 
          :value="download.progress" 
          color="primary"
        />
        <q-icon 
          v-if="download.status === 'completed'" 
          name="check_circle" 
          color="positive" 
        />
        <q-icon 
          v-if="download.status === 'error'" 
          name="error" 
          color="negative" 
        />
      </div>
    </div>
  </div>
</template>

<script setup>
import { computed } from 'vue'
import { useDownloadActivityStore } from '@ftx/ui'

const downloadStore = useDownloadActivityStore()

// Reactive getters
const activeDownloads = computed(() => downloadStore.getActiveDownloads)
const hasActiveDownloads = computed(() => downloadStore.hasActiveDownloads)
</script>

Complete Sidebar Example

vue
<template>
  <q-list class="download-activity-sidebar">
    <q-item-label header>Download Activity</q-item-label>
    
    <q-item v-if="!hasActiveDownloads" class="text-grey-6">
      <q-item-section>
        <q-item-label>No active downloads</q-item-label>
      </q-item-section>
    </q-item>

    <q-item 
      v-for="download in activeDownloads" 
      :key="download.id"
      class="download-item"
    >
      <q-item-section avatar>
        <q-icon 
          :name="getStatusIcon(download.status)" 
          :color="getStatusColor(download.status)"
          size="24px"
        />
      </q-item-section>

      <q-item-section>
        <q-item-label>{{ download.filename }}</q-item-label>
        <q-item-label caption>{{ download.tableTitle }}</q-item-label>
        
        <q-linear-progress 
          v-if="download.status === 'downloading' && download.progress !== null"
          :value="download.progress" 
          color="primary"
          class="q-mt-sm"
        />
        
        <div v-if="download.error" class="text-negative text-caption q-mt-xs">
          {{ download.error }}
        </div>
      </q-item-section>

      <q-item-section side>
        <q-btn 
          flat 
          round 
          dense 
          icon="close" 
          size="sm"
          @click="removeDownload(download.id)"
        />
      </q-item-section>
    </q-item>
  </q-list>
</template>

<script setup>
import { computed } from 'vue'
import { useDownloadActivityStore } from '@ftx/ui'

const downloadStore = useDownloadActivityStore()

const activeDownloads = computed(() => downloadStore.getActiveDownloads)
const hasActiveDownloads = computed(() => downloadStore.hasActiveDownloads)

function getStatusIcon(status) {
  const icons = {
    'downloading': 'download',
    'completed': 'check_circle',
    'error': 'error'
  }
  return icons[status] || 'help'
}

function getStatusColor(status) {
  const colors = {
    'downloading': 'primary',
    'completed': 'positive',
    'error': 'negative'
  }
  return colors[status] || 'grey'
}

function removeDownload(id) {
  downloadStore.removeDownload(id)
}
</script>

View All Downloads (Including Completed)

vue
<template>
  <div>
    <h3>All Downloads</h3>
    <q-list>
      <q-item v-for="download in allDownloads" :key="download.id">
        <q-item-section>
          <q-item-label>{{ download.filename }}</q-item-label>
          <q-item-label caption>
            Status: {{ download.status }}
            <span v-if="download.completedAt">
              - Completed: {{ formatDate(download.completedAt) }}
            </span>
          </q-item-label>
        </q-item-section>
      </q-item>
    </q-list>
  </div>
</template>

<script setup>
import { computed } from 'vue'
import { useDownloadActivityStore } from '@ftx/ui'

const downloadStore = useDownloadActivityStore()

const allDownloads = computed(() => downloadStore.getAllDownloads)

function formatDate(date) {
  return new Date(date).toLocaleString()
}
</script>

Auto-Cleanup Completed Downloads

You can automatically remove completed or failed downloads after a delay:

vue
<script setup>
import { watch } from 'vue'
import { useDownloadActivityStore } from '@ftx/ui'

const downloadStore = useDownloadActivityStore()

// Note: Completed downloads are automatically removed by the download helper
// after a delay (10 seconds for PDF/Print, 2 seconds for others)
// Error downloads remain visible until manually dismissed
</script>

API Reference

State

PropertyTypeDescription
downloadsObjectObject mapping download IDs to download objects

Download Object Structure

Each download object contains:

PropertyTypeDescription
idstringUnique download identifier
filenamestringFilename for the download (e.g., "export.xlsx")
statusstringStatus: 'downloading', 'completed', or 'error'
progressnumber | nullProgress value (0-1 for downloads with total, 0 for downloads without total), or null if not available
formatnumber | nullFormat: 0=PDF, 1=Excel, 2=CSV, 3=Print, or null
tableTitlestring | nullTable title from which the export was generated
errorstring | nullError message if download failed
startedAtDateTimestamp when download was initiated
completedAtDate | nullTimestamp when download completed or failed

Getters

getActiveDownloads

Returns an array of all active downloads (status is downloading).

Returns: Array<Download> - Array of active download objects

Example:

javascript
const downloadStore = useDownloadActivityStore()
const activeDownloads = downloadStore.getActiveDownloads
// Returns: [{ id: '...', filename: 'export.xlsx', status: 'in-progress', ... }, ...]

getAllDownloads

Returns an array of all downloads, including completed and failed ones.

Returns: Array<Download> - Array of all download objects

Example:

javascript
const downloadStore = useDownloadActivityStore()
const allDownloads = downloadStore.getAllDownloads
// Returns: [{ id: '...', status: 'completed', ... }, { id: '...', status: 'failed', ... }, ...]

getDownload(id)

Returns a specific download by ID.

Parameters:

  • id (string, required) - Download ID

Returns: Download | null - Download object or null if not found

Example:

javascript
const downloadStore = useDownloadActivityStore()
const download = downloadStore.getDownload('download_1234567890_abc123')
// Returns: { id: 'download_1234567890_abc123', filename: 'export.xlsx', ... } or null

hasActiveDownloads

Checks if there are any active downloads.

Returns: boolean - true if there are active downloads, false otherwise

Example:

javascript
const downloadStore = useDownloadActivityStore()
if (downloadStore.hasActiveDownloads) {
  console.log('Downloads in progress')
}

Actions

addDownload(download)

Adds or updates a download in the store. This is automatically called by the download helper, but can be used manually if needed.

Parameters:

  • download (Object, required) - Download information object
    • id (string, required) - Unique download identifier
    • filename (string, optional) - Filename for the download
    • status (string, optional) - Status ('downloading', 'completed', 'error')
    • progress (number, optional) - Progress value (0-1 for downloads with total, 0 for downloads without total)
    • hasTotal (boolean, optional) - Whether the download has a total size for progress calculation
    • format (number, optional) - Format: 0=PDF, 1=Excel, 2=CSV, 3=Print
    • tableTitle (string, optional) - Table title
    • error (string, optional) - Error message
    • startedAt (Date, optional) - Start timestamp (defaults to current time)
    • completedAt (Date, optional) - Completion timestamp

Example:

javascript
const downloadStore = useDownloadActivityStore()
downloadStore.addDownload({
  id: 'my-download-123',
  filename: 'report.xlsx',
  status: 'pending',
  format: 1, // Excel
  tableTitle: 'Sales Report'
})

updateDownload(id, status, updates)

Updates an existing download's status and optional additional properties.

Parameters:

  • id (string, required) - Download ID
  • status (string, required) - New status
  • updates (Object, optional) - Additional updates (progress, error, etc.)

Example:

javascript
const downloadStore = useDownloadActivityStore()

// Update progress
downloadStore.updateDownload('my-download-123', 'downloading', { progress: 0.5, hasTotal: true })

// Mark as completed
downloadStore.updateDownload('my-download-123', 'completed', { progress: 1 })

// Mark as error
downloadStore.updateDownload('my-download-123', 'error', { error: 'Network error' })

removeDownload(id)

Removes a download from tracking.

Parameters:

  • id (string, required) - Download ID

Example:

javascript
const downloadStore = useDownloadActivityStore()
downloadStore.removeDownload('my-download-123')

clearCompletedDownloads()

Removes all completed and error downloads from tracking.

Example:

javascript
const downloadStore = useDownloadActivityStore()
downloadStore.clearCompletedDownloads()

clearAllDownloads()

Removes all downloads from tracking.

Example:

javascript
const downloadStore = useDownloadActivityStore()
downloadStore.clearAllDownloads()

Integration with FtxGrid

The download activity store is automatically integrated with FtxGrid export functionality. When users export data from a grid:

  1. The download is automatically added to the store
  2. Progress is tracked during the download
  3. Status updates are emitted automatically
  4. Your UI components can react to these changes

No additional configuration is needed - just subscribe to the store in your download activity component.

Best Practices

  1. Display Active Downloads Only: Use getActiveDownloads to show only downloading downloads in your UI
  2. Auto-Cleanup: Completed downloads are automatically removed after a delay (10s for PDF/Print, 2s for others)
  3. Error Handling: Always display error messages from error downloads - they remain visible until manually dismissed
  4. Progress Indicators: Show progress bars for downloading downloads when available (progress is 0-1, not 0-100)
  5. User Actions: Provide a way for users to dismiss downloads manually

Example: Complete Sidebar Component

Here's a complete example of a sidebar download activity component:

vue
<template>
  <q-list bordered class="rounded-borders">
    <q-item-label header>
      <div class="row items-center justify-between">
        <span>Download Activity</span>
        <q-btn 
          v-if="hasActiveDownloads || hasCompletedDownloads"
          flat 
          dense 
          size="sm" 
          label="Clear"
          @click="clearCompleted"
        />
      </div>
    </q-item-label>

    <!-- No downloads message -->
    <q-item v-if="!hasActiveDownloads && !hasCompletedDownloads">
      <q-item-section>
        <q-item-label class="text-grey-6">No downloads</q-item-label>
      </q-item-section>
    </q-item>

    <!-- Active downloads -->
    <q-item 
      v-for="download in activeDownloads" 
      :key="download.id"
      class="download-item q-mb-xs"
    >
      <q-item-section avatar>
        <q-spinner 
          v-if="download.status === 'downloading'"
          color="primary" 
          size="24px"
        />
      </q-item-section>

      <q-item-section>
        <q-item-label>{{ download.filename }}</q-item-label>
        <q-item-label caption>{{ download.tableTitle }}</q-item-label>
        
        <q-linear-progress 
          v-if="download.status === 'downloading' && download.progress !== null"
          :value="download.progress" 
          color="primary"
          class="q-mt-xs"
        />
      </q-item-section>

      <q-item-section side>
        <q-btn 
          flat 
          round 
          dense 
          icon="close" 
          size="sm"
          @click="removeDownload(download.id)"
        />
      </q-item-section>
    </q-item>

    <!-- Completed/failed downloads -->
    <q-separator v-if="hasActiveDownloads && hasCompletedDownloads" />
    
    <q-item 
      v-for="download in completedDownloads" 
      :key="download.id"
      class="download-item"
    >
      <q-item-section avatar>
        <q-icon 
          :name="download.status === 'completed' ? 'check_circle' : 'error'"
          :color="download.status === 'completed' ? 'positive' : 'negative'"
          size="24px"
        />
      </q-item-section>

      <q-item-section>
        <q-item-label>{{ download.filename }}</q-item-label>
        <q-item-label caption>
          <span v-if="download.error" class="text-negative">{{ download.error }}</span>
          <span v-else>Download completed</span>
        </q-item-label>
      </q-item-section>

      <q-item-section side>
        <q-btn 
          flat 
          round 
          dense 
          icon="close" 
          size="sm"
          @click="removeDownload(download.id)"
        />
      </q-item-section>
    </q-item>
  </q-list>
</template>

<script setup>
import { computed } from 'vue'
import { useDownloadActivityStore } from '@ftx/ui'

const downloadStore = useDownloadActivityStore()

const activeDownloads = computed(() => downloadStore.getActiveDownloads)
const hasActiveDownloads = computed(() => downloadStore.hasActiveDownloads)

const completedDownloads = computed(() => {
  return downloadStore.getAllDownloads.filter(
    (d) => d.status === 'completed' || d.status === 'error'
  )
})

const hasCompletedDownloads = computed(() => completedDownloads.value.length > 0)

function removeDownload(id) {
  downloadStore.removeDownload(id)
}

function clearCompleted() {
  downloadStore.clearCompletedDownloads()
}
</script>

<style scoped>
.download-item {
  padding: 8px 16px;
}
</style>

Notes

  • The store automatically handles download lifecycle events
  • Downloads are tracked automatically when using FtxGrid export/print features
  • Progress tracking depends on your API's support for download progress events
  • Progress values are 0-1 (not 0-100) - divide by 100 only if your progress component expects percentage
  • Completed downloads are automatically removed after a delay (10 seconds for PDF/Print, 2 seconds for others)
  • Error downloads remain visible until manually dismissed
  • The store uses Pinia's reactive system, so components automatically update when downloads change
  • Hot Module Replacement (HMR) is supported during development

Released under the MIT License.