ポータルのカスタマイズ
kintone のポータル画面は、ログイン後に最初に表示されるページです。JavaScript と CSS のカスタマイズを活用すれば、業務に合わせたダッシュボードやタスク管理画面を構築できます。
ポータルのカスタマイズは、アプリのカスタマイズとは異なる仕組みで動作します。この記事では、ポータルに関連するイベントや API の使い方から、実践的なカスタマイズパターンまで解説します。
ポータルイベントの基本
ポータル画面が表示された際に発火するイベントが用意されています。
| イベント名 | 発火タイミング |
|---|---|
portal.show | ポータル画面が表示された時(PC) |
mobile.portal.show | ポータル画面が表示された時(モバイル) |
(() => {
'use strict';
kintone.events.on(['portal.show'], (event) => {
console.log('ポータル画面が表示されました');
return event;
});
})();
ポータルのカスタマイズは、kintone システム管理 の「JavaScript / CSS によるカスタマイズ」から登録します。アプリの設定画面からではなく、システム管理から設定する点に注意してください。設定には kintone のシステム管理者権限が必要です。
ポータル API
ポータルのカスタマイズでは、以下の API が利用できます。
kintone.portal.getContentSpaceElement()
ポータルのコンテンツエリアの要素(DOM)を取得します。この要素に対して HTML 要素を追加することで、ポータルにカスタムコンテンツを表示できます。
kintone.events.on(['portal.show'], (event) => {
const contentSpace = kintone.portal.getContentSpaceElement();
if (contentSpace) {
const div = document.createElement('div');
div.textContent = 'カスタムコンテンツ';
contentSpace.appendChild(div);
}
return event;
});
kintone.portal.getContentSpaceElement() は portal.show
イベントのコールバック内でのみ使用できます。イベント外から呼び出すと null が返されます。
モバイル対応
モバイルでは kintone.mobile.portal.getContentSpaceElement() を使用します。
kintone.events.on(['mobile.portal.show'], (event) => {
const contentSpace = kintone.mobile.portal.getContentSpaceElement();
if (contentSpace) {
const div = document.createElement('div');
div.textContent = 'モバイルポータルのカスタムコンテンツ';
contentSpace.appendChild(div);
}
return event;
});
実践パターン 1: お知らせパネルの追加
ポータルにカスタムのお知らせパネルを表示するパターンです。
(() => {
'use strict';
const events = ['portal.show', 'mobile.portal.show'];
kintone.events.on(events, (event) => {
const isMobile = event.type === 'mobile.portal.show';
const contentSpace = isMobile
? kintone.mobile.portal.getContentSpaceElement()
: kintone.portal.getContentSpaceElement();
if (!contentSpace) {
return event;
}
// お知らせパネルの作成
const panel = document.createElement('div');
panel.style.cssText = `
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 20px 24px;
border-radius: 8px;
margin-bottom: 16px;
font-size: 14px;
line-height: 1.6;
`;
const title = document.createElement('h2');
title.textContent = '📢 お知らせ';
title.style.cssText = 'margin: 0 0 8px 0; font-size: 18px;';
const message = document.createElement('p');
message.textContent = '2026年3月1日より、新しい経費精算フローが開始されます。詳細は総務部までお問い合わせください。';
message.style.margin = '0';
panel.appendChild(title);
panel.appendChild(message);
// ポータルの先頭に挿入
contentSpace.prepend(panel);
return event;
});
})();
実践パターン 2: 自分のタスク一覧ダッシュボード
REST API を使って、ログインユーザーに割り当てられたタスクをポータルに一覧表示するパターンです。
(() => {
'use strict';
const TASK_APP_ID = 15; // タスク管理アプリのID
kintone.events.on(['portal.show'], async (event) => {
const contentSpace = kintone.portal.getContentSpaceElement();
if (!contentSpace) {
return event;
}
const loginUser = kintone.getLoginUser();
try {
// ログインユーザーの未完了タスクを取得
const result = await kintone.api(
kintone.api.url('/k/v1/records.json', true),
'GET',
{
app: TASK_APP_ID,
query: `担当者 in ("${loginUser.code}") and ステータス not in ("完了") order by 期限日 asc limit 10`,
fields: ['レコード番号', 'タスク名', '期限日', 'ステータス', '優先度'],
}
);
// ダッシュボードの作成
const dashboard = document.createElement('div');
dashboard.style.cssText = `
background: white;
border: 1px solid #e3e7ee;
border-radius: 8px;
padding: 20px;
margin-bottom: 16px;
`;
const heading = document.createElement('h2');
heading.textContent = `📋 マイタスク(${result.records.length} 件)`;
heading.style.cssText = 'margin: 0 0 12px 0; font-size: 16px; color: #333;';
dashboard.appendChild(heading);
if (result.records.length === 0) {
const empty = document.createElement('p');
empty.textContent = '未完了のタスクはありません 🎉';
empty.style.color = '#888';
dashboard.appendChild(empty);
} else {
const table = document.createElement('table');
table.style.cssText = 'width: 100%; border-collapse: collapse; font-size: 14px;';
// ヘッダー
const thead = document.createElement('thead');
thead.innerHTML = `
<tr style="background: #f5f6fa; text-align: left;">
<th style="padding: 8px 12px; border-bottom: 2px solid #e3e7ee;">タスク名</th>
<th style="padding: 8px 12px; border-bottom: 2px solid #e3e7ee;">期限日</th>
<th style="padding: 8px 12px; border-bottom: 2px solid #e3e7ee;">ステータス</th>
<th style="padding: 8px 12px; border-bottom: 2px solid #e3e7ee;">優先度</th>
</tr>
`;
table.appendChild(thead);
// ボディ
const tbody = document.createElement('tbody');
for (const record of result.records) {
const row = document.createElement('tr');
const recordId = record['レコード番号'].value;
const taskUrl = `/k/${TASK_APP_ID}/show#record=${recordId}`;
row.innerHTML = `
<td style="padding: 8px 12px; border-bottom: 1px solid #eee;">
<a href="${taskUrl}" style="color: #3498db; text-decoration: none;">${record['タスク名'].value}</a>
</td>
<td style="padding: 8px 12px; border-bottom: 1px solid #eee;">${record['期限日'].value || '-'}</td>
<td style="padding: 8px 12px; border-bottom: 1px solid #eee;">${record['ステータス'].value}</td>
<td style="padding: 8px 12px; border-bottom: 1px solid #eee;">${record['優先度'].value}</td>
`;
tbody.appendChild(row);
}
table.appendChild(tbody);
dashboard.appendChild(table);
}
contentSpace.prepend(dashboard);
} catch (error) {
console.error('タスクの取得に失敗しました', error);
}
return event;
});
})();
ポータルのカスタマイズでは、kintone.api
を使って複数のアプリからデータを取得し、統合的なダッシュボードを構築できます。ただし、API
の呼び出し回数が増えるとページの表示速度に影響するため、取得するフィールドを絞り込む(fields
パラメータの活用)ことをおすすめします。
実践パターン 3: 集計サマリーの表示
複数アプリの集計結果をポータルに表示するパターンです。
(() => {
'use strict';
const APP_IDS = {
案件管理: 10,
問い合わせ: 11,
タスク: 15,
};
kintone.events.on(['portal.show'], async (event) => {
const contentSpace = kintone.portal.getContentSpaceElement();
if (!contentSpace) {
return event;
}
const loginUser = kintone.getLoginUser();
// 各アプリから件数を並列で取得
const [cases, inquiries, tasks] = await Promise.all([
kintone.api(kintone.api.url('/k/v1/records.json', true), 'GET', {
app: APP_IDS.案件管理,
query: `担当者 in ("${loginUser.code}") and ステータス in ("進行中")`,
totalCount: true,
fields: ['レコード番号'],
}),
kintone.api(kintone.api.url('/k/v1/records.json', true), 'GET', {
app: APP_IDS.問い合わせ,
query: `対応者 in ("${loginUser.code}") and 対応状況 in ("未対応")`,
totalCount: true,
fields: ['レコード番号'],
}),
kintone.api(kintone.api.url('/k/v1/records.json', true), 'GET', {
app: APP_IDS.タスク,
query: `担当者 in ("${loginUser.code}") and ステータス not in ("完了")`,
totalCount: true,
fields: ['レコード番号'],
}),
]);
// カードスタイルのサマリーを構築
const container = document.createElement('div');
container.style.cssText = `
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 16px;
margin-bottom: 16px;
`;
const items = [
{ label: '進行中の案件', count: cases.totalCount, color: '#3498db', appId: APP_IDS.案件管理 },
{ label: '未対応の問い合わせ', count: inquiries.totalCount, color: '#e74c3c', appId: APP_IDS.問い合わせ },
{ label: '未完了タスク', count: tasks.totalCount, color: '#2ecc71', appId: APP_IDS.タスク },
];
for (const item of items) {
const card = document.createElement('a');
card.href = `/k/${item.appId}/`;
card.style.cssText = `
display: block;
background: white;
border: 1px solid #e3e7ee;
border-left: 4px solid ${item.color};
border-radius: 8px;
padding: 20px;
text-decoration: none;
transition: box-shadow 0.2s;
`;
card.addEventListener('mouseenter', () => {
card.style.boxShadow = '0 2px 8px rgba(0,0,0,0.1)';
});
card.addEventListener('mouseleave', () => {
card.style.boxShadow = 'none';
});
card.innerHTML = `
<div style="font-size: 14px; color: #888; margin-bottom: 4px;">${item.label}</div>
<div style="font-size: 32px; font-weight: bold; color: #333;">${item.count}</div>
`;
container.appendChild(card);
}
contentSpace.prepend(container);
return event;
});
})();
CSS カスタマイズとの併用
ポータルの見た目をさらにカスタマイズするには、CSS ファイルを追加します。
/* ポータルの背景色を変更 */
.gaia-portal-content-area {
background-color: #f5f6fa;
}
/* ポータルコンテンツエリアの幅を調整 */
.gaia-portal-content {
max-width: 1200px;
margin: 0 auto;
}
/* お知らせウィジェットのスタイル調整 */
.gaia-portal-notification {
border-radius: 8px;
overflow: hidden;
}
kintone の内部 CSS クラス名は kintone のアップデートにより変更される可能性があります。特に、kintone のフロントエンド刷新に伴い、既存の CSS クラス名が使用できなくなる場合があります。カスタマイズの CSS が影響を受けていないか、定期的に確認してください。
ポータルカスタマイズの制約
| 項目 | 制約 |
|---|---|
| カスタマイズファイルの登録場所 | kintone システム管理(アプリ設定ではない) |
| 管理者権限 | kintone システム管理者権限が必要 |
| スペースポータル | スペースのポータルは portal.show では対応不可 |
| ゲストスペース | ゲストスペースのポータルには別途カスタマイズが必要 |
| イベント内でのみ有効な API | getContentSpaceElement() はイベント外では null |
portal.show イベントは kintone
のトップポータル画面でのみ発火します。スペースごとのポータル画面をカスタマイズする場合は、space.portal.show
イベントを使用してください。
まとめ
- ポータルのカスタマイズは
portal.show/mobile.portal.showイベントで実装する kintone.portal.getContentSpaceElement()でコンテンツエリアの DOM 要素を取得できる- カスタマイズファイルは kintone システム管理 から登録する(アプリ設定ではない)
- REST API を組み合わせることで、複数アプリを横断したダッシュボードを構築できる
- 表示速度を考慮し、
Promise.allによる並列取得とfieldsパラメータの活用を心がける - CSS クラス名は kintone のアップデートで変更される可能性があるため、定期的に確認する