Skip to content

Ledger Page

  • File: src/pages/private/ledger/index.svelte

Overview

The Ledger page serves as a comprehensive event history viewer, providing access to past events, their details, attachments, logs, and chat history. It features a searchable interface with real-time updates and multiple viewing modes.

The page is structured as a grid, presenting a chronological list of past events elegantly showcased within the event card component. This component offers a concise overview of each event, providing users with essential details at a glance. Additionally, an overflow menu is available for each event, allowing users to access comprehensive information about the event and view all related logs.

Core Features

1. Event Management

  • View past events and their details
  • Search through event history
  • Access event attachments
  • View event logs
  • Access event chat history

2. File Management

  • View shared files
  • Download attachments
  • Preview file contents
  • Manage file shares

3. Real-time Updates

  • Live event title changes
  • Dynamic search results
  • Instant UI updates

UI Structure

1. Main Layout

svelte
<div class="flex-grow flex flex-col">
  <!-- Progress Indicator -->
  {#if busy}
    <Progress height={2} step={1} />
  {/if}

  <!-- Search Bar -->
  <div class="flex items-center justify-between">
    <div class="flex rounded-lg border-gray-300">
      {#if showSearch}
        <input
          type="text"
          class="px-4 py-2 w-80 rounded-l-lg outline-none"
          placeholder="Search..."
          bind:value={searchValue}
        />
      {/if}
      <button on:click={toggleSearch}>
        <Icon src={Search} size="20" />
      </button>
    </div>
  </div>

  <!-- Event Grid -->
  <div class="grid gap-3 mb-8 grid-cols-1 md:grid-cols-2">
    {#each events as data}
      <EventCard
        {data}
        showJoinButton={false}
        showAttachment={true}
        showdropDown={true}
        on:eventDetails={openDetails}
        on:eventLog={openLogs}
      />
    {/each}
  </div>
</div>

2. Modal Views

svelte
{#if activeEvent}
  <!-- File Viewer -->
  {#if activeAttachment}
    <SideModal>
      <FileViewer file={activeAttachment} on:refresh={refreshAttachments} />
    </SideModal>
  <!-- Chat Viewer -->
  {:else if showChat}
    <SideModal>
      <ChatViewer meetingId={activeEvent.id} />
    </SideModal>
  <!-- Log Viewer -->
  {:else if showLogs}
    <SideModal>
      <LogViewer event={activeEvent} />
    </SideModal>
  <!-- Event Details -->
  {:else}
    <SideModal>
      <Viewer
        event={activeEvent}
        on:fileSelected={({ detail }) => (activeAttachment = detail)}
        on:openChat={openChat}
        on:openLogs={(_) => openLogs({ detail: activeEvent })}
      />
    </SideModal>
  {/if}
{/if}

Data Structures

File interface

ts
interface IFile {
  id: string;
  name: string;
  url: string;
  type: string;
  size: number;
  notes: string;
  dateCreated: Date;
  downloadUrl: string;
  thumbnailUrl: string;
  ownerId: string;
  owner: {
    firstName: string;
    lastName: string;
  };
  shares: Array<{
    id: string;
    user: {
      firstName: string;
      lastName: string;
      profileImageUrl: string;
      id: string;
    };
  }>;
}

Event interface

ts
interface IEvent {
  id: string;
  startTime: Date;
  endTime: Date;
  startDate: Date;
  endDate: Date;
  title: string;
  allDay: boolean;
  notes: string;
  repeat: string;
  sessionId: number;
  hasChat: boolean;
  numberOfAttachments: number;
  ownerId: string;
  participants: Array<{
    contact?: {
      firstName: string;
      lastName: string;
      profileImageUrl: string;
      specialty: string;
    };
    username: string;
  }>;
  owner: {
    firstName: string;
    lastName: string;
  };
  isSecuredCameraSession: boolean;
}

Core Functions

1. File Management

svelte
async function getFile(sessionId: number, shareId: number) {
  try {
    const { data, error } = await query<{ xs: { nodes: any[] } }>(
      readFileQuery,
      { sessionId, shareId, _id: uniqueId("q") }
    );

    if (error) {
      return { success: false, message: error.message };
    }

    return processFileData(data.xs.nodes);
  } catch (e) {
    return { success: false, message: e };
  }
}

2. Event Retrieval

svelte
async function getEvents(skip: number, take: number, filter: string) {
  try {
    let where = filter ? `, where: { title: { contains: "${filter}" } }` : "";

    const { data, error } = await query<{ xs: any; sharedLedger: any }>(
      eventQuery,
      { _id: uniqueId("q") }
    );

    if (error) {
      return { success: false, message: error.message };
    }

    return processEventData(data);
  } catch (e) {
    return { success: false, message: e };
  }
}

State Management

1. Pagination State

svelte
let skip = 0;
let take = 20;
let delay = 500;

2. UI State

svelte
let showSearch = false;
let searchValue = '';
let events = [];
let filteredEvents = [];
let activeEvent = null;
let activeAttachment = null;
let showLogs = false;
let showChat = false;
let busy = true;

Event handlers

1. View Management

svelte
function openLogs({ detail: event }) {
  activeEvent = event;
  showLogs = true;
}

function openDetails({ detail: event }) {
  activeEvent = event;
  showLogs = false;
}

function openChat() {
  showChat = true;
}

2. Search Management

svelte
const dSearch = debounce(delay, search);

async function search(skip, take, searchValue) {
  busy = true;
  try {
    const ret = await getEvents(skip, take, searchValue);
    if (ret.success) {
      events = ret.data;
    }
  } finally {
    busy = false;
  }
}

Permission Management

svelte
onMount(async () => {
  if (!hasPermission(Permissions.viewLedger)) {
    $goto("/private/dashboard");
    return;
  }
  await dSearch(skip, take, searchValue);
});

Real-time updates

svelte
// Subscribe to event title changes
var eventTitleChangedSubscription = PubSub.subscribe(
  eventTitleChanged,
  (x: IEventTitleChangedArgs) => {
    dSearch(skip, take, searchValue);
  }
);

// Cleanup subscription
onDestroy(async () => {
  if (eventTitleChangedSubscription) {
    PubSub.unsubscribe(eventTitleChangedSubscription);
  }
});

Released under the MIT License.