Skip to main content

Member Database

Overview

Member Database (known on the backend as customer-database) is a repository of member records uploaded by administrators as CSV files. It acts as a reference table that other features can query to verify eligibility or confirm a person's identity.

The typical user is a CMS administrator whose existing member list lives in a CRM system or an Excel file and needs to be made available to the LINE platform. The primary consumer is Form Builder, which maps a database to a form so that submissions can be checked against real member records.

How it differs from Import Mapping — Member Database never overwrites LINE user profiles. It stores the CSV as a separate dataset that other features consult on demand, whereas Import Mapping writes values directly into friend profiles.

Each record contains:

FieldDescription
NameThe database label used for reference; required, up to 255 characters
DescriptionAdditional detail, up to 1,000 characters
ColumnsThe column names detected in the CSV file
Row countThe number of member records in the file
Mapped formsThe forms currently using this database for verification
Created dateWhen the database was uploaded

Known limitations:

  • Only .csv files are accepted, one file at a time.
  • There is no separate detail page; the table and the column popover are the only ways to inspect a database.
  • There is no export back to CSV.
  • The only filter is the search box — there is no filtering by status or mapped form, and no column sorting.
  • File validation happens entirely on the backend, with the results surfaced during the preview step.

Business Flow

Browsing the database list

  1. Opening /member-database checks the user's access rights first; without permission, no content is rendered.
  2. The page sets the breadcrumb, highlights the side-menu entry, and loads the list for the current page and search term.
  3. The table shows the name with the description as a secondary line, the column count, the row count, mapped forms, the creation date, and action buttons.
  4. Hovering or clicking the column count opens a popover listing every column name in that file.
  5. The mapped-forms column shows each form as a tag; databases not yet in use display a "not mapped" tag instead.
  6. The search box filters by name. Running a search always resets to the first page, and a clear button resets the criteria.

Uploading a new database (3-step wizard)

  1. The "Upload Database" button in the page header opens a three-step upload dialog.
  2. Step 1 — select a file. Drag a CSV file onto the drop zone or browse for it. Nothing is uploaded straight away; the file is sent to the backend for a preview read first.
  3. If the read succeeds, the system stores the result — column names, row count, sample rows, and any errors found — and advances to the next step. On failure, the error message returned by the backend is displayed.
  4. Step 2 — review the data. A warning banner appears if the file contains errors, alongside a summary line with the column and row counts, the column names as tags, and a table of sample rows.
  5. If the file has errors, the Next button is disabled and the user must go back and choose a different file. Going back clears the selected file and its preview entirely.
  6. Step 3 — enter details. Provide the database name (required) and an optional description.
  7. Clicking Create validates the form, then submits the name, description, and file to create the database. On success the dialog closes, its state is reset, and the table refreshes.

Editing or replacing a file

  1. The edit button in the table opens the edit dialog with the existing name and description pre-filled.
  2. If the database has mapped forms, a warning banner lists how many forms are affected and names each one, so the impact is clear before any change is made.
  3. To replace the data, select a new CSV file. It is previewed the same way as during upload, with a button to cancel the replacement.
  4. Clicking Save applies the change. If no new file was selected, only the name and description are updated and the existing data is left untouched.
  5. The Save button is disabled while the newly selected file has preview errors.

Deleting a database

  1. The delete button opens a confirmation dialog whose title names the database about to be removed.
  2. On confirmation the deletion is submitted, a success message is shown, and the table refreshes.
  3. If the backend rejects the request — for example when the database is still mapped to a form — the returned error message is displayed and the dialog closes.

Using a database in Form Builder

  1. Opening the form settings page preloads the list of member databases (up to 100 records).
  2. In the Profile Mapping section, the user picks a database, and its columns are listed so they can be paired with form fields, with a running count of mapped pairs.
  3. Those pairings are what populate the "mapped forms" column back on the Member Database list, and are the reason edits and deletions carry a warning.

Key Screens & Components

The feature has a single page, /member-database. All creation and editing happens in modals; there is no separate form page.

List page

  • Page header and upload button — the standard CMS section header with the Upload Database action.
  • Search bar — a single name search field with search and clear actions.
  • Record table — name with description, column count (with a popover listing the column names), row count, mapped-form tags, creation date, and edit/delete buttons.

Primary files: src/app/member-database/page.tsx, src/components/member-database/member-database.container.tsx, src/components/member-database/member-database-table.tsx, src/components/member-database/member-database-filter.tsx

Upload dialog

  • Step indicator — tracks the three stages: select file, review data, enter details.
  • Drop zone — accepts a single .csv file, with a hint about the size limit.
  • Preview panel — shared with the edit dialog; it combines the error banner, the summary line, column tags, and a sample-row table.

Primary files: src/components/member-database/upload-database-modal.tsx, src/components/member-database/csv-preview-detail.tsx

Edit dialog

  • Basic details form — name and description, repopulated each time the dialog opens.
  • Mapped-form warning banner — shown when forms depend on the database, listing each form by name.
  • File replacement section — select a new CSV with a preview, plus a button to cancel the replacement.

Primary file: src/components/member-database/edit-database-modal.tsx

API service

All calls live in src/services/customer-database.service.ts under the customer-database base path. Requests that carry a file are sent as multipart/form-data.

CapabilityEndpoint
List databasesGET /customer-database
Get a single databaseGET /customer-database/{id}
Preview a CSV before savingPOST /customer-database/preview
Create a databasePOST /customer-database
Update details or replace the filePUT /customer-database/{id}
Delete a databaseDELETE /customer-database/{id}

Dependencies

  • Permissions — the page is gated by the view permission on the customer-database module, mapped to the member-database menu. The side-menu entry appears only for permitted users, and a Quick Access shortcut is also available.
  • Form Builder — the main consumer of this feature. Its Profile Mapping settings use member databases as the source for verifying respondents, and it links back to Member Database for data management.
  • Import Mapping — a separate feature that updates friend profiles directly and does not read from these datasets.
  • Shared CMS components — the section header, the standard confirmation dialog, and the table scroll area used across other list pages.
  • Shared infrastructure — the CMS authentication and HTTP client (automatic token attachment and sign-out on expiry), plus the breadcrumb and side-menu system.

Backend Details (CMS API)

The backend lives in internal/modules/customerdatabase/, with two responsibilities split into their own files: the CSV reader and a column-order helper that keeps the returned column order identical to the source file.

Required permissions

  • Every route is wrapped in the module gate for the customer-database module.
  • Policies are split by action: readAll for listing, read for a single record, create for creation, and update / delete for their operations.
  • Note — the CSV preview endpoint is gated on the create policy rather than read, because it is treated as a step in creating a database. A user with view-only permission therefore cannot drop a file in for preview.

The CSV reader and the behaviour it inherits

This module's CSV reader was written to replicate a JavaScript CSV library's behaviour exactly, so that results match the previous system. That means it also inherits that library's quirks.

  • A CSV file with only one column is rejected with an error saying the column delimiter could not be detected, even though the file may look perfectly valid to the user. If single-column data really must be imported, add a second throwaway column.
  • When an error is found, the returned row data comes back completely empty rather than containing the rows that did parse. The preview screen showing an error therefore has no sample data to display alongside it.
  • All file validation happens on the backend; the UI merely renders whatever the backend reports.

What gets stored, and the side effects

  • The database summary (name, description, column names, row count) is stored in the customer_database table, while the individual rows live separately in customer_database_row.
  • Creation inserts rows in bulk, 1,000 at a time, all inside a single transaction. A mid-way failure rolls the whole thing back rather than leaving half the data behind.
  • An edit that attaches a new file replaces every row rather than merging. The existing rows are deleted and rewritten within the same transaction.
  • The original CSV file is retained on object storage alongside the table data.
  • The "mapped forms" column is not a stored relationship — it is computed on the fly during listing, by looking inside the profile mapping configuration on the form_builder table to find which forms reference this database id.

Edge cases worth knowing

  • Deletion is a status-based soft delete that flips the row's status to deleted, rather than stamping a separate deletion timestamp as other modules do. Listing queries must filter that status out themselves every time.
  • Because the form relationship is derived from the form_builder side rather than stored as a real relation, changes here are not propagated back to the forms. A mapped form can end up pointing at a dataset whose contents have silently changed.
  • Every query is scoped to the currently selected LINE OA; another OA's databases are never returned, even when the id is supplied directly.
  • Column ordering is deliberately preserved, because Form Builder's column mapping depends on names and order matching the source file.
  • This module publishes no queue work and does no background processing, so uploading a large file keeps the single request open until every batch of rows has been inserted.