Skip to content

Sharing Page ​

File Information ​

  • Path: app/pages/[brand_name]/sharing.vue
  • Route: /{brand_name}/sharing
  • Purpose: Manage all share links (active and revoked) with filtering, sorting, and bulk operations
  • Layout: collage-layout
  • Middleware: check-url

Overview ​

The Sharing page displays all share links created by the user, including both active and revoked links. It provides comprehensive share link management features including filtering, sorting, bulk selection, revoking, and deleting share links.

Key Features ​

  1. Share Link List

    • Table view of all share links
    • Active and revoked filter tabs
    • Share link details (title, description, created date, creator, source)
    • Clickable share URLs
  2. Filtering

    • Filter by Active URLs
    • Filter by Revoked URLs
    • Filter dropdown with options
  3. Sorting

    • Sort by Created Date (default)
    • Sort by Created By
    • Sort by Created Source
    • Ascending/descending toggle
    • Client-side sorting
  4. Selection & Bulk Actions

    • Single and multi-select (Shift+Click support)
    • Select all functionality (for revoked URLs)
    • Bulk revoke active URLs
    • Bulk delete revoked URLs
    • Selection count display
  5. Share Link Actions

    • Copy share URL
    • Revoke active share URL
    • Delete revoked share URL
    • View share link details
  6. Infinite Scroll Pagination

    • Automatic loading on scroll
    • Skeleton loaders
    • Scroll position management

Components Used ​

  • ShareTableHeader - Table header with sorting
  • ShareTableListItem - Table row component
  • ConfirmationDialog - Confirmation dialog for delete/revoke
  • AsyncIcon - Async icon component
  • v-skeleton-loader - Skeleton loaders

Composables Used ​

  • useSharing() - Sharing functionality (app/composables/core/pages/useSharing.ts)
  • useSharingApi() - Sharing API (app/composables/api/useSharingApi.ts)
  • useSnackbar() - Snackbar notifications
  • useHelpers() - Helper functions

Data Properties ​

Reactive Data (from useSharing composable) ​

typescript
{
  pageScrolling: boolean,              // Page scrolling state
  contentLoading: boolean,             // Initial content loading
  select_all_share: boolean,          // Select all checkbox state
  filter_by: string,                  // Current filter ('active' | 'revoked')
  share_data: ShareItem[],             // Share links array
  loadMore: boolean,                  // Load more pagination state
  loading: boolean,                   // Loading state
  selectedFile: ShareItem | null,     // Selected share item for actions
  delete_dialog: boolean,            // Delete dialog visibility
  deleteMsg: string,                  // Delete confirmation message
  deleteTitle: string,                // Delete dialog title
  delete_flag: string,                // Delete operation flag
  sort_by_share: string,              // Sort direction ('ASC' | 'DESC')
  sort_value_share: string,           // Sort field name
  shareHeader: ShareHeader[],         // Table column definitions
  shareMenu: ShareMenuItem[],        // Share item menu options
  sharedUrlFilter: ShareFilterOption[], // Filter options
  shareFiterSelect: ShareFilterOption, // Selected filter
  showArrowSortShare: ShareShowArrowSort, // Sort arrow visibility
  fileCardKey: number,                // File card key for reactivity
  scrollContainer: HTMLElement | null, // Scroll container ref
  shareContainer: HTMLElement | null,  // Share container ref
  current_page: number,               // Current page number
  last_page: number,                  // Last page number
  per_page: number,                   // Items per page
  total: number                        // Total share links count
}

Column Definitions ​

typescript
[
  { name: "Title", slug: "title", enable_sort: false },
  { name: "Description", slug: "description", enable_sort: false },
  { name: "Created Date", slug: "created_at", enable_sort: true },
  { name: "Created By", slug: "user_name", enable_sort: true },
  { name: "Created Source", slug: "generated_source", enable_sort: true }
]

Computed Properties ​

selectedItems ​

Returns array of selected share items.

typescript
computed(() => 
  share_data.value.filter((item) => item.is_selected === true)
)

customDomain ​

Returns custom domain URL if configured.

typescript
computed(() => {
  const withHttp = (url: string): string =>
    /^https?:\/\//.test(url) ? url : `https://${url}`
  const { url: brandUrl, is_domain } = brand || {}
  return is_domain && brandUrl ? withHttp(brandUrl) : ""
})

Methods ​

fetchShareUrls(flag = "no-sort") ​

Fetches share links from API.

typescript
async function fetchShareUrls(flag: string = "no-sort"): Promise<void> {
  try {
    if (
      loadMore.value ||
      loading.value ||
      (current_page.value >= last_page.value && flag === "no-sort")
    )
      return
    
    if (flag !== "sort") {
      loadMore.value = current_page.value >= 1
      loading.value = current_page.value === 0
      current_page.value += 1
    }
    
    if (flag === "sort" && scrollContainer.value) {
      scrollContainer.value.scrollTop = 0
    }
    
    const params = {
      workspace_id: workspaceId.value,
      page: current_page.value,
      total_record: per_page.value || 12,
      filter_by: filter_by.value,
    }
    
    const { data } = await fetchShareUrlsApi(params)
    
    last_page.value = data.last_page
    total.value = data.total
    
    if (["filter", "sort"].includes(flag) && current_page.value === 1) {
      share_data.value = []
      select_all_share.value = false
    }
    
    share_data.value.push(
      ...data.data.map((item: any) => ({
        ...item,
        is_selected: select_all_share.value,
      }))
    )
  } catch (error) {
    console.log("fetchShareUrls", error)
    snackbar.showError(getErrorMessage(error))
  } finally {
    loadMore.value = false
    loading.value = false
  }
}

Parameters:

  • flag: string - Operation flag: "no-sort", "filter", "sort"

Flow:

  1. Checks pagination limits
  2. Sets loading states
  3. Builds API parameters
  4. Fetches share URLs
  5. Updates share data array
  6. Handles errors

handleScroll(flag, scrollContainerRef, shareContainerRef) ​

Handles scroll events for infinite scroll.

typescript
async function handleScroll(
  flag: string = "main",
  scrollContainerRef?: HTMLElement | null,
  shareContainerRef?: HTMLElement | null
): Promise<void> {
  try {
    const container =
      flag === "main"
        ? scrollContainerRef || scrollContainer.value
        : shareContainerRef || shareContainer.value
    
    if (!container) return
    
    const scrollTop = container.scrollTop
    const containerHeight = container.scrollHeight - container.clientHeight
    const scrollPercent = (scrollTop / (containerHeight || 1)) * 100
    
    const hasData = share_data.value.length > 0
    
    if (scrollPercent >= 85 && hasData) {
      await fetchShareUrls()
    } else if (scrollTop >= 36 && hasData) {
      pageScrolling.value = true
    } else {
      pageScrolling.value = false
    }
  } catch (error) {
    console.log("Scroll handler error:", error)
  }
}

Triggers: Loads more share links when scroll reaches 85% of container height.

toggleSelect(payload) ​

Toggles individual share item selection.

typescript
function toggleSelect(payload: ShareTogglePayload): void {
  const { event, file, index } = payload
  try {
    const isSelected = !file.is_selected
    const found = share_data.value.find((obj: any) => obj.id === file.id)
    if (found) found.is_selected = isSelected
    toggleSelectSharing(
      { share_data: share_data.value },
      {
        ...file,
        is_selected: isSelected,
      },
      index,
      event
    )
  } catch (error) {
    console.error("Toggle select error:", error)
    lastIndexShare.value = -1
  }
}

togleSelectAll(val: boolean) ​

Toggles select all state.

typescript
function togleSelectAll(val: boolean): void {
  select_all_share.value = val
  share_data.value.forEach((obj) => {
    obj.is_selected = val
  })
  lastIndexShare.value = -1
}

getSubItems(file, flag) ​

Returns sub-items for table row display.

typescript
function getSubItems(file: ShareItem, flag: string): string[] {
  if (flag === "share") {
    return [
      file.description || "-",
      formatDate(file.created_at) || "-",
      file.user.name || "-",
      file.generated_source === "admin" ? "Backend" : "Frontend",
    ]
  }
  return []
}

getShareUrl(file) ​

Generates share URL from share item.

typescript
function getShareUrl(file: ShareItem): string | null {
  try {
    const DAM_FRONTEND_URL = config.public.baseUrl as string
    let currentSource = ""
    
    if (import.meta.client) {
      currentSource = window.location.origin
    }
    
    const queryString = file.share_url.split("?").pop() || ""
    const [type, status] = queryString.split("&")
    const typeValue = type.replace("type=", "")
    
    const baseUrl =
      file.generated_source === "admin"
        ? currentSource
        : customDomain.value || DAM_FRONTEND_URL
    
    let finalUrl = `${baseUrl}/shared-assets/${typeValue}?${status}`
    finalUrl = finalUrl.replace(/([^:]\/)\/+/g, "$1")
    return finalUrl
  } catch (error) {
    console.log("getShareUrl", error, file.share_url)
    snackbar.showError("Failed to generate share URL")
    return null
  }
}

openRevokeDialog(file) ​

Opens revoke confirmation dialog.

typescript
function openRevokeDialog(file: ShareItem): void {
  selectedFile.value = file
  delete_flag.value = "revoke-file"
  deleteTitle.value = "Revoke Share URL"
  deleteMsg.value = "Are you sure you want to revoke shared URL?"
  delete_dialog.value = true
}

openDeleteAll() ​

Opens bulk delete/revoke confirmation dialog.

typescript
function openDeleteAll(): void {
  const multiple = selectedItems.value.length > 1 ? "s" : ""
  const isActiveFilter = shareFiterSelect.value.name === "active"
  
  delete_flag.value = isActiveFilter ? "revoke-all" : "delete-share-all"
  
  deleteTitle.value = isActiveFilter
    ? `Revoke Share URL${multiple}`
    : "Confirm Delete"
  
  deleteMsg.value = `Are you sure you want to ${
    isActiveFilter ? "revoke" : "delete"
  } Shared URL${multiple}?`
  
  delete_dialog.value = true
}

openDelShare(file) ​

Opens delete confirmation dialog for revoked share.

typescript
function openDelShare(file: any) {
  selectedFile.value = file
  delete_flag.value = "delete-share"
  deleteTitle.value = `Confirm Delete`
  deleteMsg.value = `Are you sure you want to delete the Shared URL?`
  delete_dialog.value = true
}

deleteFn() ​

Handles delete/revoke operations.

typescript
async function deleteFn(): Promise<void> {
  try {
    const isBulkDelete = ["delete-share-all", "revoke-all"].includes(
      delete_flag.value
    )
    
    if (
      ![
        "revoke-file",
        "revoke-all",
        "delete-share",
        "delete-share-all",
      ].includes(delete_flag.value)
    )
      return
    
    const selectedIds = isBulkDelete
      ? selectedItems.value.map(({ id }) => id)
      : [selectedFile.value!.id]
    
    const params: Record<string, any> = {
      workspace_id: workspaceId.value,
    }
    
    if (!isBulkDelete) params.share_url_id = selectedIds
    else params.share_url_ids = selectedIds
    
    if (!isBulkDelete) {
      const response = await revokeShareUrlsApi(params)
      snackbar.showSuccess(response.message)
    } else {
      const response = await deleteShareUrlsApi(params)
      snackbar.showSuccess(response.message)
    }
    
    share_data.value = share_data.value.filter(
      (e) => !selectedIds.includes(e.id)
    )
    
    if (!share_data.value.length) {
      await fetchShareUrls()
    }
  } catch (error) {
    console.log("deleteFn", error)
    snackbar.showError(getErrorMessage(error))
  } finally {
    delete_dialog.value = false
    delete_flag.value = ""
  }
}

shareFilterUpdate(payload) ​

Handles filter change.

typescript
async function shareFilterUpdate(payload: ShareFilterOption): Promise<void> {
  if (!payload) return
  
  try {
    filter_by.value = payload.name
    
    // Reset sort indicators
    Object.keys(showArrowSortShare.value).forEach((key) => {
      showArrowSortShare.value[key as keyof ShareShowArrowSort] = false
    })
    
    current_page.value = 0
    sort_by_share.value = "DESC"
    sort_value_share.value = "created_at"
    shareHeader.value.forEach((item) => (item.togle_sort = false))
    showArrowSortShare.value.created_at = true
    
    await fetchShareUrls("filter")
    lastIndexShare.value = -1
  } catch (error) {
    console.error("Filter update error:", error)
  }
}

shareEmbedSort(payload) ​

Handles column sorting (client-side).

typescript
async function shareEmbedSort(payload: any) {
  const field = payload.field
  
  try {
    if (payload.flag === "share-url") {
      for (const key in showArrowSortShare.value) {
        showArrowSortShare.value[key] = false
      }
      showArrowSortShare.value[field] = true
      sort_value_share.value = field
      sort_by_share.value = payload.toggle ? "ASC" : "DESC"
      
      // Perform client-side sorting
      share_data.value.sort((a: any, b: any) => {
        let fieldA = a[field]
        let fieldB = b[field]
        
        // Handle date fields
        if (field === "modified_at" || field === "created_at") {
          fieldA = new Date(fieldA as string)
          fieldB = new Date(fieldB as string)
        }
        
        // Handle source field
        if (field === "generated_source") {
          fieldA = fieldA === "user" ? "Frontend" : "Admin"
          fieldB = fieldB === "user" ? "Frontend" : "Admin"
        }
        
        // Handle user name field
        if (field === "user_name") {
          fieldA = a?.user?.name || ""
          fieldB = b?.user?.name || ""
        }
        
        // Compare based on sort direction
        if (sort_by_share.value === "ASC") {
          return fieldA > fieldB ? 1 : fieldA < fieldB ? -1 : 0
        } else {
          return fieldA < fieldB ? 1 : fieldA > fieldB ? -1 : 0
        }
      })
    }
  } catch (error) {
    console.log(error)
  }
}

cancelDelDialog() ​

Closes delete dialog.

typescript
function cancelDelDialog(): void {
  delete_dialog.value = false
  delete_flag.value = ""
}

User Interactions ​

  1. Filter Management

    • Filter by Active URLs
    • Filter by Revoked URLs
    • Filter dropdown selection
  2. Sorting

    • Sort by Created Date (default)
    • Sort by Created By
    • Sort by Created Source
    • Click column headers to sort (client-side)
    • Visual sort indicators
  3. Selection

    • Single click to select
    • Shift+Click for range selection
    • Select all functionality (for revoked URLs)
    • Selection count display
  4. Share Link Actions

    • Copy share URL
    • Revoke active share URL
    • Delete revoked share URL
    • View share link details
  5. Bulk Actions

    • Bulk revoke active URLs
    • Bulk delete revoked URLs
    • Confirmation dialogs for destructive actions

Real-time Features ​

  • Share link data loaded on demand
  • No real-time updates (manual refresh required)

Styling ​

  • Uses custom CSS from assets/scss/custom.scss
  • Responsive breakpoints for mobile/tablet/desktop
  • Skeleton loaders for loading states
  • Table view styling
  • Custom scrollbar styling
  • Filter dropdown styling

Watchers ​

None - uses event handlers and lifecycle hooks.

Lifecycle Hooks ​

onMounted ​

Initializes share links data.

typescript
onMounted(async () => {
  await fetchShareUrls()
  contentLoading.value = false
})

API Endpoints ​

Fetch Share URLs ​

  • Endpoint: GET /share-urls (via fetchShareUrlsApi())
  • Parameters:
    typescript
    {
      workspace_id: string,
      page: number,
      total_record: number,
      filter_by: 'active' | 'revoked'
    }
  • Response:
    typescript
    {
      data: ShareItem[],
      last_page: number,
      total: number
    }

Revoke Share URLs ​

  • Endpoint: POST /share-urls/revoke (via revokeShareUrlsApi())
  • Parameters:
    typescript
    {
      workspace_id: string,
      share_url_id?: number | number[],
      share_url_ids?: number[]
    }
  • Response: Success message

Delete Share URLs ​

  • Endpoint: DELETE /share-urls (via deleteShareUrlsApi())
  • Parameters:
    typescript
    {
      workspace_id: string,
      share_url_id?: number | number[],
      share_url_ids?: number[]
    }
  • Response: Success message

User Flow ​

  1. User navigates to sharing page
  2. Page fetches share links (default: active)
  3. User can:
    • Filter by Active or Revoked URLs
    • Sort by columns (client-side)
    • Select share links (single or multi-select)
    • Copy share URL
    • Revoke active share URL
    • Delete revoked share URL
    • Bulk revoke/delete selected links
    • Scroll for more links
  4. Infinite scroll loads more share links automatically
  5. Confirmation dialogs for destructive actions

Performance Optimizations ​

  1. Pagination: Loads share links in pages (36 per page)
  2. Client-Side Sorting: Sorts loaded links without API call
  3. Scroll Throttling: Scroll events handled efficiently
  4. Lazy Loading: Data loaded on demand

Notes for Development ​

  • Active URLs can be revoked (made inactive)
  • Revoked URLs can be deleted permanently
  • Select all only available for revoked URLs
  • Share URLs generated based on source (admin vs user)
  • Custom domain support for share URLs
  • Client-side sorting only affects loaded links
  • Confirmation required for all destructive actions