フロントエンド (JS アセット)
Lase は Vue 3 ベースのフロントエンドアセットを NPM パッケージ @codebase-jp/lase として提供します。
インストール
npm install @codebase-jp/laseピア依存関係も合わせてインストールしてください:
npm install vue axios bootstrap bootstrap-icons dayjs vue-router @codebase-jp/vaseモジュール構成
パッケージはコンテキスト別にモジュールが分かれています。
@codebase-jp/lase
├── api/
│ ├── admin/ 管理者 API クライアント
│ ├── tenant/ テナント API クライアント
│ ├── common/ 共通 API クライアント
│ └── public/ パブリック API クライアント
├── components/
│ ├── admin/ 管理画面コンポーネント
│ ├── tenant/ テナント画面コンポーネント
│ └── common/ 共通コンポーネント
├── composables/
│ ├── admin/ 管理者向け composable
│ └── common/ 共通 composable
├── views/
│ ├── admin/ 管理画面ビュー(Vue SFC)
│ ├── tenant/ テナント画面ビュー
│ ├── public/ パブリック画面ビュー
│ └── common/ 共通ビュー
├── routes/
│ ├── admin/ 管理者ルート定義
│ ├── tenant/ テナントルート定義
│ └── public/ パブリックルート定義
├── models/ TypeScript 型定義モデル
├── enums/ TypeScript Enum
├── filters/ フィルター関数
├── utils/ ユーティリティ関数
└── types/ 共通型定義Composables
共通
| composable | 説明 |
|---|---|
useApp | アプリケーション情報の取得・管理 |
useAuth | 認証状態・ログイン・ログアウト |
useAiChat | AI チャット |
useConfig | フロントエンド設定値の取得(詳細) |
useLogger | クライアントログの送信 |
useRecaptcha | reCAPTCHA v3 トークン取得 |
テナント
| composable | 説明 |
|---|---|
useEntitlement | テナントの有効なエンタイトルメント(機能・利用上限・利用量)の取得と判定 |
管理者
| composable | 説明 |
|---|---|
useSenter | Senter 課金サービスとの連携 |
useConfig
useConfig は、サーバーサイドの設定値をフロントエンドで参照するための composable です。
仕組み
Lase は GET /config エンドポイントを提供しており、Laravel の設定値(config('lase.front') と config('front'))をマージして window.__CONFIG__ としてブラウザに渡します。Blade テンプレートで以下のように読み込みます:
<script src="/config"></script>useConfig はこの window.__CONFIG__ の値を読み取ります。
設定値の定義
設定値は以下の 2 箇所で定義できます。アプリ側(config/front.php)の値が優先されます。
| ファイル | 説明 |
|---|---|
config/lase.php の front キー | パッケージ側のデフォルト値 |
config/front.php | アプリ側で追加・上書き |
// config/front.php
return [
'recaptcha_site_key' => env('FRONT_RECAPTCHA_SITE_KEY'),
'docs_url' => env('FRONT_DOCS_URL', '/docs/'),
];WARNING
/config のレスポンスはブラウザに公開されます。秘密鍵やパスワードなどの機密情報は絶対に含めないでください。
API
import useConfig from '@codebase-jp/lase/composables';
const { config, get, has } = useConfig();| プロパティ / メソッド | 型 | 説明 |
|---|---|---|
config | Record<string, ConfigValue> | 設定オブジェクト全体 |
get(key, defaultValue?) | <T>(key: string, defaultValue?: T) => T | undefined | ドット記法で値を取得 |
has(key) | (key: string) => boolean | キーが存在するか判定 |
使用例
const { get, has } = useConfig();
// 値を取得
const siteKey = get<string>('recaptcha_site_key');
// デフォルト値付き
const docsUrl = get<string>('docs_url', '/docs/');
// ネストされた値をドット記法で取得
const value = get<string>('nested.deep.key');
// 存在チェック
if (has('recaptcha_site_key')) {
// ...
}AiChat
RAG(AiEmbeddingService)と連携する AI チャット UI です。
<script lang="ts" setup>
import { AiChat } from '@codebase-jp/lase/components';
</script>
<template>
<AiChat />
</template>Props
| prop | 既定値 | 説明 |
|---|---|---|
placeholder | メッセージを入力... | 入力欄のプレースホルダ |
welcomeMessage | こんにちは!… | 初回表示時のメッセージ |
suggestions | ログイン/パスワード/問い合わせの3件 | 質問例のボタン。[] で非表示 |
showSources | true | 回答の参照元ドキュメントを表示するか |
autofocus | true | 表示直後に入力欄へフォーカスするか |
挙動
- Markdown 描画 — 回答は Markdown として描画されます。LLM の出力はドキュメント経由のプロンプトインジェクションを含み得るため、
ChatMarkdownUtilが許可リスト方式でサニタイズします(script/iframe/img/ イベントハンドラ属性 /javascript:リンクは除去)。ユーザーの発言は Markdown として解釈せずそのまま表示します。 - 参照元の表示 — バックエンドが RAG で参照したドキュメントを AI SDK の
source-documentパートとして通知します。docs_urlを基に閲覧用リンクへ変換して表示します。 - スクロール追従 — 最下部付近にいる間だけ自動追従します。読み返し中に引き戻されません。
- 入力 — Enter で送信、Shift+Enter で改行、日本語入力の変換確定中は送信しません。高さは入力量に応じて自動調整されます。応答中も入力は可能です(送信のみ抑止)。
- 中断・再試行・リセット — 応答中は停止ボタン、エラー時は再試行ボタン、会話のリセットボタンを表示します。
TIP
AiChat 自体はチャットの中身(メッセージ一覧・入力欄)のみを提供します。画面右下に浮かべる起動ボタン・パネルごと欲しい場合は、下記の AiChatWidget を使ってください。
AiChatWidget
AiChat を画面右下の起動ボタン(FAB)とパネルで包んだ、すぐ使える形の共通ウィジェットです。アプリのルートコンポーネントに1つ置くだけで、全画面共通のAIアシスタントを提供します。
<script lang="ts" setup>
import { AiChatWidget } from '@codebase-jp/lase/components';
</script>
<template>
<AiChatWidget />
</template>Props
AiChat の placeholder / welcomeMessage / suggestions / showSources をそのまま受け取り、AiChat へ引き渡します(autofocus は非表示中のフォーカスを避けるため常に false で固定)。加えて以下を持ちます。
| prop | 既定値 | 説明 |
|---|---|---|
label | AIアシスタント | パネルのタイトル・起動ボタンの aria-label |
挙動
- 起動ボタンはクリックでパネルの開閉をトグルします。パネルは
Escキーでも閉じられます。 - パネルは
v-showで保持されるため、閉じても会話は消えません。 - 幅576px未満の画面では、パネルが画面幅いっぱいに近い大きさになります。
TIP
チャット枠を独自にカスタマイズしたい場合(サイドバーに埋め込む等)は、AiChatWidget ではなく AiChat を直接使ってください。
管理画面ビュー
管理画面の Vue コンポーネントが提供されています。
| ディレクトリ | 説明 |
|---|---|
views/admin/dashboard/ | ダッシュボード |
views/admin/tenants/ | テナント管理 |
views/admin/users/ | ユーザー管理 |
views/admin/plans/ | プラン管理 |
views/admin/announcements/ | お知らせ管理 |
views/admin/admin-users/ | 管理者ユーザー管理 |
views/admin/admin-roles/ | 管理者ロール管理 |
views/admin/admin-mails/ | メール管理 |
views/admin/admin-images/ | 画像管理 |
views/admin/inquiries/ | お問い合わせ管理 |
views/admin/blocked-domains/ | 禁止ドメイン管理 |
views/admin/tenant-roles/ | テナントロール管理 |
ヘッダーのメニュー
管理画面のヘッダーのメニューは、/admin の子のルートの meta.menu から作ります。ダッシュボードのほかは、まとまり(meta.menu.group)ごとのドロップダウンにまとめ、項目が増えてもヘッダーが1行に収まるようにしています。
| まとまり | 項目 |
|---|---|
| 顧客 | テナント・ユーザー・パートナー |
| 申込・契約 | プラン・オプション・追加機能・申込の質問・利用規約・登録審査 |
| 分析 | 流入分析 |
| コンテンツ | お知らせ・画像 |
| サポート | メール・お問い合わせ |
| セキュリティ | 禁止ドメイン・禁止テナントコード・禁止IPアドレス・操作ログ |
| システム | 管理者 |
- 開いている画面の項目(まとまりなら、まとまりとその中の項目)に印を付けます。ルート名の1番目の区切りで比べるので、
tenants.showやtenants.usersでもテナントに印が付きます - メニューに無い画面は、
meta.activeMenuで印を付ける項目を指せます(例: テナントロールのtenant-roles.indexはactiveMenu: 'tenants')
テナント詳細のサイドバー
テナント詳細(/admin/tenants/:id)のサイドバーには、lase の項目(基本情報・テナントユーザー・許可IPアドレス・利用状況・サブスクリプション・請求履歴)の後ろに、アプリが足した子のルートのうち meta.menu(name・icon)を持つものを並べます。開いている画面の項目には印を付けます(ルート名の前方一致)。子の画面には、テナント(tenant)が props で渡り、update を emit すると読み直します。
import { AdminRoutes } from '@codebase-jp/lase/routes/admin';
const tenantRoute = AdminRoutes.find((r) => r.path === '/admin')!.children!.find((r) => r.path === 'tenants/:id')!;
tenantRoute.children!.push({
path: 'customer-data',
name: 'tenants.customer-data',
component: CustomerDataPurge,
meta: { menu: { name: '顧客データの削除', icon: 'bi bi-trash' } },
});ルート名は tenants. で始めてください(ヘッダーで「テナント」に印が付きます)。
ルーティング
Lase のルートをアプリの Vue Router に組み込む例:
import { createRouter, createWebHistory } from 'vue-router';
import { AuthRoutes, ExpiredRoutes, AnnouncementRoutes, InquiryRoutes, createSettingsRoutes, createTenantAccountRoutes } from '@codebase-jp/lase/routes/tenant';
import { ErrorRoutes } from '@codebase-jp/lase/routes/common';
const router = createRouter({
history: createWebHistory(),
routes: [
...AuthRoutes,
...ErrorRoutes,
...ExpiredRoutes,
{
path: '',
component: Layout, // アプリのレイアウト(ヘッダー)
children: [
// アプリの画面
createSettingsRoutes(), // 組織の設定(/settings)
createTenantAccountRoutes(), // 個人の設定(/account)
...AnnouncementRoutes, // 運営からのお知らせ
...InquiryRoutes, // お問い合わせ
],
},
],
});組織の設定と個人の設定
Lase がテナント向けに用意する機能は、次の画面にまとめます。アプリが変わっても、共通の部分は同じ並び・同じ名前になります。
| まとまり | ルート | 中身 | 使う人 |
|---|---|---|---|
| 組織の設定 | createSettingsRoutes()(/settings) | 組織(基本情報・営業日と休日・セキュリティ・ユーザー・エージェント・操作ログ)、アプリの設定、契約とお支払い(ご利用プラン・追加機能・ご利用状況・請求履歴・お支払い方法・解約の申し込み) | 管理する人 |
| 個人の設定 | createTenantAccountRoutes()(/account) | プロフィール・メールアドレスの変更・連携中のアプリ(MCP の OAuth 連携) | 全員 |
| ヘルプ | AnnouncementRoutes・InquiryRoutes・GuideRoutes | 運営からのお知らせ・ガイド・お問い合わせ | 全員 |
どちらも MenuLayout(サイドバーのメニュー付きのレイアウト。スマホではオフキャンバス)を使い、サイドバーは子のルートの meta.menu から作ります。項目は meta.menu.permission(いずれかを持っていれば出す)と meta.menu.visible で出し分け、項目が無くなった区切りは出しません。項目を選ばずに開くと、出せる最初の項目へ移ります。
アプリの画面を足す
createSettingsRoutes({
// アプリの区切り(order を省くと、組織と契約とお支払いのあいだに並ぶ)
sections: [{ key: 'operations', name: '業務の設定' }],
routes: [
{
path: 'tags',
name: 'settings.tags',
component: TagIndex,
meta: { menu: { name: 'タグ', icon: 'bi bi-tags', section: 'operations', order: 10, permission: TenantPermission.TAG_WRITE } },
},
],
// ユーザー(人とエージェント)の編集に足すタブ
userTabs: [{ key: 'affiliation', name: '所属・担当', component: AffiliationTab }],
// 契約を Lase で扱わないアプリは false
billing: true,
});
createTenantAccountRoutes({
routes: [{ path: 'notifications', name: 'account.notifications', component: NotificationSettings, meta: { menu: { name: '通知', icon: 'bi bi-bell', order: 40 } } }],
});meta.menu | 説明 |
|---|---|
name / icon | 見出しとアイコン |
section | 区切りの key(組織は ORGANIZATION_SECTION、契約とお支払いは BILLING_SECTION) |
order | 並び順(小さいほど上) |
permission | いずれかを持っていれば出すテナントの権限 |
visible | 権限以外で出し分けるとき |
match | この名前で始まるルートでも選択中にする(詳細の画面など) |
組織の設定を使わずに置く
組織の設定・個人の設定のまとまりを使わず、これまでの画面の並びのままにしたいアプリは、画面の部品をアプリのルートとレイアウトに置けます。
| 書き出し | 部品 |
|---|---|
@codebase-jp/lase/views/tenant | TenantProfile(基本情報)・BusinessCalendar(営業日と休日)・Security(2段階認証の決まりとIPアドレス制限)・ActivityLogIndex(操作ログ)、契約とお支払いの Plan・AddonIndex・Usage・InvoiceIndex・InvoiceShow・Payment・Cancel |
@codebase-jp/lase/views/common | AccountProfile・AccountEmailChange・TwoFactorSettings・NotificationSettings(個人の設定の各画面) |
Lase の部品は、組織の設定・個人の設定に置いたときのルート名で、ほかの画面へリンクします。アプリのルートにも同じ名前を付けてください(パスとレイアウトは自由です)。
| ルート名 | リンクしている部品 |
|---|---|
settings.billing.plan | Expired・ログイン後の契約の無い組織の移動先(useAuth) |
settings.billing.payment | Plan・AddonIndex(カードの登録の案内)、Payment(Stripe からの戻り先) |
settings.billing.invoices.index・settings.billing.invoices.show | InvoiceShow(パンくず)・InvoiceIndex(詳細へ) |
account.two-factor | TenantProfile(2段階認証の設定へ) |
account.notifications・notifications.index | NotificationMenu・通知の一覧(無ければリンクを出さない) |
import { Plan, AddonIndex, Payment } from '@codebase-jp/lase/views/tenant';
const contractRoutes = {
path: '/contract',
component: ContractLayout, // アプリのレイアウト
children: [
{ path: '', name: 'settings.billing.plan', component: Plan },
{ path: 'addons', name: 'settings.billing.addons', component: AddonIndex },
{ path: 'payment', name: 'settings.billing.payment', component: Payment },
],
};アプリの画面のまとまりに使う
MenuLayout は、組織の設定・個人の設定と同じサイドバーを、アプリの画面のまとまり(レポートなど)にも使えます。親のルートの component に置いて meta.menuLayout に見出しと区切りを書き、子のルートに meta.menu を付けます。
import { MenuLayout } from '@codebase-jp/lase/views/common';
const reportRoutes = {
path: 'reports',
name: 'reports',
component: MenuLayout,
meta: { menuLayout: { title: 'レポート', sections: [{ key: 'tasks', name: 'タスク' }, { key: 'workflows', name: 'ワークフロー' }] } },
children: [
{ path: 'trend', name: 'reports.trend', component: TrendReport, meta: { menu: { name: '推移', icon: 'bi bi-graph-up', section: 'tasks', order: 10 } } },
],
};ユーザーの編集に足すタブ
userTabs のタブの部品は、props の tenantUserId(所属の ID)を受け取って自分で内容を読み込み、defineExpose で次を公開します。ユーザーの編集の「保存」は、Lase の基本の項目(ステータス・役割)を保存してから、変更のあるタブの save() を順に呼びます。エージェントは、エージェントの画面のボタンから開く画面で同じタブを編集します。
| 公開するもの | 説明 |
|---|---|
isDirty | 保存していない変更があるか |
save() | 保存する(失敗したら例外を投げる) |
reset() | 保存前の内容に戻す |
ヘッダーの入口
| 部品 | 説明 |
|---|---|
HelpMenu | ヘルプのメニュー。置いているルート(お知らせ・ガイド・お問い合わせ)と、links で渡すアプリのリンク(ヘルプのサイトなど)を並べる |
AccountMenu | ログイン中のユーザーのメニュー。組織の名前の下に組織の設定(出せる項目が1つも無い人には出さない)、ユーザーの名前の下に個人の設定を並べ、最後にログアウトを置く。default のスロットでアプリの項目を足せる(個人の設定とログアウトのあいだ) |
<script setup lang="ts">
import { AccountMenu, HelpMenu } from '@codebase-jp/lase/components';
</script>
<template>
<ul class="navbar-nav">
<HelpMenu :links="[{ name: 'ヘルプ', href: '/docs/' }]" />
<AccountMenu />
</ul>
</template>運営からのお知らせ
運営が管理画面で書いたお知らせを、利用者に見せる部品です。利用者に見えるのは、公開(PUBLISHED)で、日付が今日までのものだけです(日付を先にすると、その日から見える)。
| 部品・ルート | 説明 |
|---|---|
AnnouncementRoutes | 一覧(announcements.index、/announcements)と詳細(announcements.show)。ヘルプのメニューから開ける |
RecentAnnouncements | トップ画面に置く、最近のお知らせ。管理画面で「ホームに表示する」にしたものを、新しい順に limit 件(既定は5件)まで並べる。1件も無ければ何も出さない |
UrgentAnnouncementBanner | ヘッダーの上に置く、緊急のお知らせ。管理画面で「緊急表示の期限」を入れたものを、その日時まで出す。5分ごとに読み直す。利用者は閉じられ、閉じたものはそのブラウザでは内容が変わるまで出さない。無ければ何も出さない |
UrgentAnnouncementBanner は高さが変わるため、ヘッダーを画面の上に固定しているアプリは、ヘッダーと一緒に固定し、本文の余白をその高さに合わせてください。
<template>
<div class="fixed-top">
<UrgentAnnouncementBanner />
<Header />
</div>
<main><RouterView /></main>
</template>TypeScript 型
モデルの TypeScript 型は @codebase-jp/lase/models からインポートできます:
import type { Tenant, User, Subscription, Plan } from '@codebase-jp/lase/models';