Skip to main content

Built-in Board Operator Guide

This guide covers the operational procedures for the built-in board (Notices / FAQ / 1:1 Inquiry). Be sure to review Getting Started first for the concepts and access paths.

Permission prerequisites

Creating/deleting board containers is only possible for L1 SaaS and L2 Tenant operators. L3 organization operators can only manage posts and respond to inquiries for the boards in their own subtree. Sub-admins must be granted the boards / inquiries feature permissions for the menus to be visible.

1. Managing Board Containers​

You manage board containers under Site Management β†’ Boards (BoardResource, slug site-boards) in the SaaS, Tenant, and Org panels.

Creating a New Board (L1/L2)​

The board form consists of two sections.

Basic Information section

FieldDescriptionNotes
kindType (Notice/FAQ/Inquiry/General)Cannot be changed for is_system boards
nameBoard display nameMax 150 characters, required
keyInternal identifier keyMax 64 characters, cannot be changed for is_system boards
audience_scopeVisibility scope (SaaS/Tenant/Org)Default tenant
audience_org_idTarget organizationShown only when audience_scope = org
site_keyStandalone site keyLeave empty for shared (null); specify to expose only on a specific standalone site

Visibility/Placement section

FieldDescriptionDefault
placement.app_navToggle for menu visibility in the member App paneltrue
placement.sortSort order within App navigation0
is_activeBoard activationtrue
sortSort order in the management list0
  • Within a single tenant, the (site_key, key) combination is unique (partial unique index; a NULL site_key is normalized to '').
  • If audience_scope is org, the board is exposed to the specified organization and its entire subtree below it (regardless of depth).

List and Actions​

The board list shows the kind (badge), name, audience_scope, post count (posts_count), is_active, and is_system columns. The default sort is ascending by sort.

  • Edit: Performed per row via EditAction by any operator (based on view permission).
  • Bulk delete: DeleteBulkAction is shown only to those with level <= 2 (L1/L2).

is_system Protection​

The default boards seeded by CoreSeeder (Notice/FAQ/Inquiry) are protected with is_system=true.

  • In the form, the kind and key fields are disabled β€” the type and identifier key cannot be changed.
  • Fields such as name, audience_scope, and visibility settings can be edited.
  • Deletion is only possible via the L1/L2 bulk delete, and it is recommended to preserve system boards for operational continuity.

2. Writing Notice / FAQ / General Posts​

Post management is performed per BoardPost in the Posts RelationManager (PostsRelationManager) of the board detail view. The main fields of BoardPost are as follows.

FieldDescription
title / bodyTitle / Body
excerptSummary
categoryCategory (used for FAQ category grouping)
statusdraft / published
is_pinnedPin to top (shown at the top of the notice list)
published_atPublication time
starts_at / ends_atVisibility start/end period

Publication and Visibility Period​

The posts visible to members are determined by BoardPost::scopePublished(). A post is exposed only if it satisfies all of the following conditions.

  • status = published
  • published_at is empty or at or before the current time
  • starts_at is empty or at or before the current time
  • ends_at is empty or at or after the current time

In other words, you can schedule a time-limited notice using starts_at / ends_at, and you can pin a post to the top of the notice list using is_pinned.

3. Operating 1:1 Inquiries​

Inquiry responses are handled under Customer Inquiries β†’ 1:1 Inquiry (InquiryResource) in the SaaS, Tenant, and Org panels. Since inquiries are created by members, operators cannot create new ones (canCreate() = false).

Hierarchical Visibility (upper organizations view lower inquiries)​

Which inquiries can be viewed is determined by BoardInquiry::scopeVisibleTo() based on the viewer's level.

Viewer levelVisibility scope
L0 / L1 / L2All Tenant and SaaS inquiries (global scope)
L3–L5 organization operatorsTheir own inquiries + inquiries with an origin (organization_id) or assignment (assigned_org_id) in their organization subtree (ViewerScopeResolver::orgSubtreeIds())
L6 membersOnly their own inquiries (inquiries from colleagues in the same organization are not visible)

The key point is that an upper-organization operator can view the inquiries of lower organizations. An inquiry has two organization fields.

  • organization_id β€” the member's organization at the time of creation (the basis for the visibility scope)
  • assigned_org_id β€” the organization currently handling it (moves according to escalation)

The inquiry list shows the title, author (author.name), organization, assigned organization (assignedOrg.name), status (badge), and the time of the latest reply, and it provides a status filter (open/answered/closed).

Writing Replies (Operator)​

You write replies in the Replies RelationManager (RepliesRelationManager) of the inquiry response screen (EditInquiry).

  • When a reply is written, the inquiry status automatically changes to answered and last_reply_at is updated.
  • Turning on the is_internal toggle makes it an internal note. Internal notes are not exposed to members (BoardInquiryReply::scopePublic() returns only is_internal=false).
  • The operator can directly change the inquiry status (open / answered / closed) in the top form. The title and category are read-only.

Escalation​

The Escalate action in the header of the response screen (EditInquiry) raises an unresolved inquiry up one level to the parent organization. This action is shown only when config('board.inquiry.escalation_enabled', true) (CORE_BOARD_INQUIRY_ESCALATION) is true.

The behavior is handled by BoardInquiry::escalate().

  1. It moves assigned_org_id to the parent organization (organizations.parent_id) of the current handler (assigned_org_id).
  2. If there is no parent (parent_id = null), it sets assigned_org_id to null β€” this means it has reached the Tenant/SaaS queue (the top level).
  3. After the move, the status is reverted back to open.
  4. If it is already at the top level (assigned_org_id = null), it does not move and sends a "top level reached" notification (no organization_id fallback β€” to prevent re-escalation).

The escalation action automatically records the hierarchical context in the audit log via the shared logging module (->audited('board.inquiry.escalate')).

4. Usage from the Member's Perspective​

Members use the following pages in the App panel (/app) (shown only when a board is active).

Notices (NoticesPage)​

Members view the published posts of notice boards exposed to their own hierarchy (the entire SaaS + their Tenant + the organization ancestor chain). Pinned posts (is_pinned) come at the top, and clicking a post shows its body in a modal. For XSS prevention, the body is escaped and only line breaks are allowed (nl2br(e($body))).

FAQ (FaqPage)​

Members view the posts of exposed faq boards grouped by category (category). Clicking a post shows its body in a modal.

My Inquiries (MyInquiriesPage)​

Members view only the inquiries they created themselves (user_id = auth()->id()).

  • Creating a new inquiry: The member selects an exposed inquiry board and enters a title and content to create it. At creation time, both organization_id and assigned_org_id are set to the member's organization, so that organization (and its upper organizations) becomes the response entry point. The creation transaction creates one inquiry plus one initial reply (is_internal=false) together.
  • Replying: When the member opens an inquiry and adds a reply, the status returns to open and last_reply_at is updated. Members see only replies excluding internal notes (is_internal=false).

5. Internationalization (i18n)​

All labels and messages of the built-in board are provided in four languages via lang/{locale}/board.php.

localeLanguage
koKorean (default)
enEnglish
jaJapanese
zh-CNChinese (Simplified)

The Filament Resources, member pages, and notifications all use __('board.*') translation keys, so menu names (e.g., board.navigation_group_site β†’ "Site Management", board.navigation_group_inquiry β†’ "Customer Inquiries") and guidance text switch automatically according to the user's locale.

Reference: Summary of Key Classes​

ComponentClass / Location
Filament pluginApp\Filament\Board\BuiltinBoardFilamentPlugin (id builtin-board)
Board container ResourceApp\Filament\Board\Resources\BoardResource (slug site-boards)
Post RelationManager...\BoardResource\RelationManagers\PostsRelationManager
Inquiry ResourceApp\Filament\Board\Resources\InquiryResource
Inquiry response page...\InquiryResource\Pages\EditInquiry (escalation action)
Reply RelationManager...\InquiryResource\RelationManagers\RepliesRelationManager
Member pagesApp\Filament\App\Pages\{NoticesPage, FaqPage, MyInquiriesPage}
ModelsApp\Models\{Board, BoardPost, BoardInquiry, BoardInquiryReply}