Skip to main content

All Friends Report

Overview

The All Friends Report is the database view of every LINE user attached to the OA. It is used to search and filter the follower base, inspect individual records, edit profile details and custom attributes, trigger a follower sync from LINE, export the data as CSV, and import LINE User IDs from a CSV file.

It suits teams who need a full picture of their follower base, want to verify individual records before running a campaign, or need to reconcile customer data collected through other channels.

The module is organised as three screens: a list with two tabs (records in the system, and import history), a full detail page, and a CSV import form.

Business Flow

Records in the system

  1. Opening the report checks the viewer's permission, then loads the user list along with two supporting datasets: the latest follower-sync status, and the set of attributes flagged as reportable.
  2. The active tab is read from the query string, so a specific tab can be bookmarked or shared. Switching tabs always resets pagination.
  3. Filters use a staged pattern. Typed and selected values are held inside the table component and only pushed upward when the user presses Search or hits Enter, so no request fires per keystroke.
  4. Three filters are available on this tab: user type (member or guest), activity status (active or inactive), and last activity expressed in days (7, 30, 90, 180, or 365), which the screen converts into a date range before querying.
  5. Standard columns are the row number, the user (avatar, display name, and a truncated copyable user ID), type, status, follow state, last activity date, and registration date.
  6. Custom attribute columns are generated automatically from the reportable attribute list, with rendering that adapts to each attribute's data type: booleans become tags, dates are formatted for readability, arrays and objects are summarised, and empty values render as a dash.
  7. The action button at the end of each row opens the user detail modal. The right side of the toolbar shows the total friend count, the last sync time, and the Sync and Export buttons.

Follower sync

  1. Pressing Sync dispatches a background job that pulls the follower list from LINE.
  2. On success the screen reloads both the table and the sync status.
  3. If the request is rejected because a sync job is already in flight, the screen shows a warning carrying the server's message rather than a generic error.
  4. If the page loads while a sync is still processing, the button enters its loading state on its own without any user action.

Export

The Export button asks the server to build the report. The server assembles the CSV body as a string along with a filename and returns both, and the browser then saves it as a file. The endpoint shape is designed so other report types can reuse the same pattern.

User detail

  1. The modal opened from the table is the primary path most users take. It loads the individual user record together with the audience list and the custom attribute schema.
  2. The header shows the avatar, name, and tags for type, status, and follow state.
  3. The personal information block is editable in place and covers first and last name, email, and mobile number, with email format validation and a rule requiring 9 to 10 digits for mobile numbers after spaces and hyphens are stripped.
  4. The LINE OA block shows the copyable user ID, the audiences the user belongs to (resolved from IDs to names), and the unfollow date if one exists.
  5. The custom attribute block reads the schema from the attribute configuration and flattens it into a list. Object and array values are shown read-only, and keys present in the user's data but missing from the schema are appended as read-only entries so nothing disappears from view.
  6. Edit mode selects the input control per attribute data type. On a successful save the modal reloads and the parent table refreshes too.
  7. The full detail page is a secondary path with two tabs: Profile (a summary card, quick stats, and editable contact details) and Website journey. Today the only links into this page come from the audience module, and it does not include the custom attribute block.

Website journey

Pulls the user's behavioural data from Google Analytics. The time window can be set to 7, 14, 30, or 90 days, and events are grouped by session on a timeline with per-event-type icons such as page view, session start, scroll, purchase, and click. If GA has not been configured on the server, the tab shows an empty state with an explanation.

CSV import

  1. The import history tab lists previously dispatched jobs with the original filename, status (new, processing, failed, or completed), and timestamps in Bangkok time. The result-file download becomes available only once the job has completed.
  2. The import form has a job name, a drag-and-drop area accepting CSV only, and file preparation guidance with an example image and a template download.
  3. Validation runs in several layers on the client. It starts with the file extension and MIME type, then checks that the record count stays within 10,000, that a LINE User ID column exists, and that at least one data row is present. It then walks every row checking for blanks, duplicates, and malformed LINE User IDs. All problems are collected with their row numbers and presented in a summary dialog.
  4. Confirming the import uploads the file with its metadata and returns the user to the history tab to track progress.
  5. Server error codes are handled by meaning rather than generically: codes indicating invalid user IDs in the file are expanded into per-row error lists, permission-related codes surface as errors, and codes that map back to specific fields are displayed inline beneath those fields.

Key Screens & Components

The three routes — the list (src/app/report/all-friends/page.tsx), the full detail page (.../detail/page.tsx), and the import form (.../form/page.tsx). The first two are wrapped in the permission guard; the form is not.

List container (src/components/report/report-all-friends-table.container.tsx) — holds all list logic: tab switching, filtering, sync, export, and dynamic column generation. The table component (report-all-friends-table.tsx) owns the toolbar and the staged filters.

Detail modal (src/components/report/all-friends-detail-modal.tsx) — the primary path for viewing and editing an individual record, including flattening the nested attribute schema for display.

GA timeline (src/components/report/user-ga-journey.tsx) — groups events by session and highlights the point at which the web identity was matched to the LINE user.

Import container (src/components/report/csv-form.container.tsx) — houses every validation layer and the server error-code handling, reusing the confirmation and error-summary dialogs from the audience module.

Key endpoints — the user list and import history live under /report/*, while individual records, profile edits, attribute edits, follower sync, and the GA journey live under /line-users/*, shared with the member database module.

Dependencies

  • Permissions — the subject report/all-friends maps from the backend report-all-friends module. The only action enforced in the UI is view; export authorisation is enforced server-side.
  • Attribute Master — the source of both the automatic table columns and the attribute form in the detail modal. With no attributes flagged as reportable, the table simply gains no extra columns.
  • Audience — resolves audience IDs to names in the detail modal, provides the only links into the full detail page, and shares its CSV import dialogs with this module.
  • Google Analytics — must be configured server-side, otherwise the Website journey tab renders an empty state.
  • Background jobs — both follower sync and CSV import are processed asynchronously by the worker. The web layer dispatches the job and then tracks progress through the sync status and the import history table respectively.
  • Shared conventions — table page sizes, date formats, and filter control styling all come from the same CMS-wide constants used by other list screens.

Backend Details (CMS API)

The module lives in internal/modules/report/, registered under the /api/report group.

Required permissions

  • This module is genuinely guarded by ModuleGate("report-all-friends"), unlike many modules that declare policy metadata without enforcing it. If the organisation has not enabled the report-all-friends module, requests are rejected at the gate before reaching a handler.
  • The global JwtAuth applies on top of the gate.
  • Export uses a different permission from viewing. GET /api/report/all-friend declares the readAll policy, while GET /api/report/all-friend/export declares a separate export policy. The intent is to let someone read the report without being able to pull the entire base out of the system. Since the UI only checks the view action, that distinction exists purely server-side.

The backend reuses the LINE user module's query builder rather than writing a second set of conditions. Consequences:

  • The filters on GET /api/report/all-friend match GET /api/line-users/search exactly, so filtering behaves identically on both screens with no duplicated maintenance.
  • Any new or changed filter condition added to the LINE user module is inherited here automatically.
  • Displayable attribute columns come from GET /api/attribute-master/reportable; with no attribute flagged as reportable, the table simply gains no extra columns.

CSV export

GET /api/report/all-friend/export uses the same filter set as the list view, but without pagination, streaming results straight into the response instead of assembling the whole file in memory first — the same mechanism the audience module's export uses. Worth knowing:

  • The broader the filter, the larger the file and the longer the request hangs; volume is governed entirely by the filter.
  • Because it streams, a mid-transfer failure may produce a truncated file rather than an error message.

Import history

GET /api/report/import-history reads line_user_import together with line_user_import_detail, so it covers every import round, whether triggered from LINE user management or from the column-mapping import screen. If the read fails, the backend responds with a 500 and an import-lookup failure message rather than an empty list — so the UI should distinguish "no history" from "history unreadable".

Tables involved

line_user, line_user_import, line_user_import_detail, attribute_master, system_attribute, and line_oa.