mcpField
F
frappe-frontend-development

frappe-frontend-development

github.com/lubusin開発69293 回インストール12 週間前に更新
リポジトリ

frappe-frontend-development について

Frappe Frontend Developmentは、Frappe UIやポータルページでFrappeアプリのフロントエンドを構築するスキルです。Vue 3とTailwindCSSのSPA、データリソース、コンポーネント、本番環境の設定を扱います。

主な機能

  1. 01Frappe UIのSPAフロントエンド構築
  2. 02JinjaとHTMLによるポータルページ作成
  3. 03Resource APIによるデータ取得
  4. 04Frappe UIコンポーネントとユーティリティの利用
  5. 05TailwindCSSと本番ビルドの設定

活用シーン

  1. 01FrappeアプリのカスタムSPA作成
  2. 02一般公開向けポータルページの構築
  3. 03ページ分割されたDocType一覧と文書操作の追加
  4. 04フロントエンドの認証とAPIアクセス設定

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/lubusin/frappe-skills --skill frappe-frontend-development

インストール後、Claude Code で/frappe-frontend-developmentを入力して読み込まれたか確認します。

同梱ファイルreferences/frappe-ui.mdreferences/portal-development.md

詳細

Frappe フロントエンド開発

Frappe UI(Vue 3 + TailwindCSS)とポータルページを使用して、モダンなフロントエンドアプリケーションを構築します。

使用する場面

  • Frappe アプリ向けのカスタム SPA フロントエンドを構築する場合
  • Frappe UI コンポーネント(Button、Dialog、ListView など)を使用する場合
  • Resource、ListResource、DocumentResource を使用してデータ取得を実装する場合
  • ポータル/一般公開ページを作成する場合
  • Frappe アプリ内に Vue 3 フロントエンドのツールをセットアップする場合

必要な入力

  • アプリ名と、フロントエンドがすでにあるかどうか
  • フロントエンドの種類(Frappe UI を使ったフル SPA、またはポータルページ)
  • 認証要件(ログインユーザー、ゲストアクセス)
  • 必要な主要コンポーネントとデータリソース

手順

0) フロントエンドのアプローチを選択する
アプローチ使用する場面スタック
Frappe UI SPAカスタムアプリのフロントエンドVue 3、TailwindCSS、Vite
ポータルページシンプルな公開ページJinja + HTML、最小限の JS
Desk 拡張管理 UI の拡張Form/List スクリプト(frappe-desk-customization を参照)
1) Frappe UI フロントエンドのひな形を作成する
# Inside your Frappe app directory
cd apps/my_app
npx degit frappe/frappe-ui-starter frontend

# Install dependencies
cd frontend
yarn

# Start dev server
yarn dev
2) main.js を設定する
import { createApp } from 'vue'
import {
    FrappeUI,
    setConfig,
    frappeRequest,
    resourcesPlugin,
    pageMetaPlugin
} from 'frappe-ui'
import App from './App.vue'
import './index.css'

let app = createApp(App)

// Register FrappeUI plugin (components + directives)
app.use(FrappeUI)

// Enable Frappe response parsing
setConfig('resourceFetcher', frappeRequest)

// Optional: Options API resource support
app.use(resourcesPlugin)

// Optional: Reactive page titles
app.use(pageMetaPlugin)

app.mount('#app')
3) Resource でデータを取得する

汎用 Resource — カスタム API 呼び出しに使用します。

import { createResource } from 'frappe-ui'

let stats = createResource({
    url: 'my_app.api.get_dashboard_stats',
    params: { period: 'monthly' },
    auto: true,
    cache: 'dashboard-stats',
    transform(data) {
        return { ...data, formatted_total: format_currency(data.total) }
    },
    onSuccess(data) { console.log('Loaded:', data) },
    onError(error) { console.error('Failed:', error) }
})

// Properties
stats.data       // Response data
stats.loading    // Boolean: request in progress
stats.error      // Error object if failed
stats.fetched    // Boolean: data fetched at least once

// Methods
stats.fetch()    // Trigger request
stats.reload()   // Re-fetch
stats.submit({ period: 'weekly' })  // Fetch with new params
stats.reset()    // Reset state

List Resource — ページネーション付きの DocType リストに使用します。

import { createListResource } from 'frappe-ui'

let todos = createListResource({
    doctype: 'ToDo',
    fields: ['name', 'description', 'status'],
    filters: { status: 'Open' },
    orderBy: 'creation desc',
    pageLength: 20,
    auto: true,
    cache: 'open-todos'
})

// List-specific API
todos.data              // Array of records
todos.hasNextPage       // Boolean: more pages
todos.next()            // Load next page
todos.reload()          // Refresh list

// CRUD operations
todos.insert.submit({ description: 'New task' })
todos.setValue.submit({ name: 'TODO-001', status: 'Closed' })
todos.delete.submit('TODO-001')
todos.runDocMethod.submit({ method: 'send_email', name: 'TODO-001' })

Document Resource — 単一ドキュメントの操作に使用します。

import { createDocumentResource } from 'frappe-ui'

let todo = createDocumentResource({
    doctype: 'ToDo',
    name: 'TODO-001',
    whitelistedMethods: {
        sendEmail: 'send_email',
        markComplete: 'mark_complete'
    },
    onSuccess(doc) { console.log('Loaded:', doc.name) }
})

// Document API
todo.doc                 // Full document object
todo.reload()            // Refresh document

// Update fields
todo.setValue.submit({ status: 'Closed' })

// Debounced update (coalesces rapid changes)
todo.setValueDebounced.submit({ description: 'Updated' })

// Call whitelisted methods
todo.sendEmail.submit({ email: '[email protected]' })

// Delete
todo.delete.submit()
4) Frappe UI コンポーネントを使用する
<template>
    <div class="p-4">
        <Button variant="solid" theme="blue" @click="showDialog = true">
            Add Todo
        </Button>

        <ListView :columns="columns" :rows="todos.data">
            <template #cell="{ column, row, value }">
                <Badge v-if="column.key === 'status'" :theme="value === 'Open' ? 'orange' : 'green'">
                    {{ value }}
                </Badge>
                <span v-else>{{ value }}</span>
            </template>
        </ListView>

        <Dialog v-model="showDialog" :options="{ title: 'New Todo' }">
            <template #body-content>
                <TextInput v-model="newDescription" placeholder="Description" />
            </template>
            <template #actions>
                <Button variant="solid" @click="addTodo">Save</Button>
            </template>
        </Dialog>
    </div>
</template>

<script setup>
import { ref } from 'vue'
import { Button, ListView, Badge, Dialog, TextInput, createListResource } from 'frappe-ui'

const showDialog = ref(false)
const newDescription = ref('')

const todos = createListResource({
    doctype: 'ToDo',
    fields: ['name', 'description', 'status'],
    auto: true
})

const columns = [
    { label: 'Description', key: 'description' },
    { label: 'Status', key: 'status', width: 100 }
]

function addTodo() {
    todos.insert.submit(
        { description: newDescription.value },
        { onSuccess() { showDialog.value = false; newDescription.value = '' } }
    )
}
</script>

利用可能なコンポーネントのカテゴリ:

カテゴリコンポーネント
入力TextInput、Textarea、Select、Combobox、MultiSelect、Checkbox、Switch、DatePicker、TimePicker、Slider、Password、Rating
表示Alert、Avatar、Badge、Breadcrumbs、Progress、Tooltip、ErrorMessage、LoadingText
ナビゲーションButton、Dropdown、Tabs、Sidebar、Popover
レイアウトDialog、ListView、Calendar、Tree
リッチコンテンツTextEditor(TipTap)、Charts、FileUploader
5) ディレクティブとユーティリティを追加する
<script setup>
import { onOutsideClickDirective, visibilityDirective, debounce } from 'frappe-ui'

const vOnOutsideClick = onOutsideClickDirective
const vVisibility = visibilityDirective

const debouncedSearch = debounce((query) => {
    // Search logic
}, 500)
</script>

<template>
    <div v-on-outside-click="closeDropdown">...</div>
    <div v-visibility="onVisible">Lazy loaded content</div>
</template>
6) TailwindCSS を設定する
// tailwind.config.js
module.exports = {
    presets: [
        require('frappe-ui/src/utils/tailwind.config')
    ],
    content: [
        './index.html',
        './src/**/*.{vue,js,ts}',
        './node_modules/frappe-ui/src/components/**/*.{vue,js,ts}'
    ]
}
7) 本番環境向けにビルドする
# Build frontend assets
cd frontend && yarn build

# Assets are served at /frontend by Frappe
8) ポータルページ(別のアプローチ)

フル SPA を使わずにシンプルな公開ページを作成する場合:

# In your app's website/ or www/ directory
# my_app/www/my_page.html

{% extends "templates/web.html" %}
{% block page_content %}
<h1>{{ title }}</h1>
<p>Welcome, {{ frappe.session.user }}</p>
{% endblock %}
# my_app/www/my_page.py
def get_context(context):
    context.title = "My Page"
    context.data = frappe.get_all("ToDo", filters={"owner": frappe.session.user})

検証

  • yarn dev がエラーなく起動する
  • コンポーネントが正しくレンダリングされる
  • データリソースがデータを取得して表示する
  • CRUD 操作(挿入、更新、削除)が機能する
  • 認証が機能する(ログインリダイレクト、セッション処理)
  • yarn build が正常に完了する
  • 本番用アセットが Frappe から正しく配信される

失敗時の問題解決/デバッグ

  • CORS エラー:ローカル開発では ignore_csrf を設定し、本番環境では適切な CSRF トークンが設定されていることを確認します
  • API 呼び出しで 404 が発生する:メソッドのパスを確認し、@frappe.whitelist() デコレーターを検証します
  • コンポーネントが見つからない:インポートパスが正しいことを確認し、frappe-ui のバージョンを確認します
  • スタイルが崩れている:TailwindCSS の設定に frappe-ui コンポーネントのパスが含まれていることを確認します
  • 認証の問題:セッション Cookie を確認し、開発用プロキシ設定のサイト URL が一致していることを確認します

エスカレーション

  • Desk UI のスクリプト作成について → frappe-desk-customization
  • API エンドポイントの実装について → frappe-api-development
  • アプリのアーキテクチャについて → frappe-app-development
  • 公式アプリの UI/UX パターンについて → frappe-ui-patterns

参考資料

ガードレール

  • カスタムフロントエンドには必ず Frappe UI を使用してください:アプリのフロントエンドに vanilla JS、jQuery、カスタムフレームワークを使用しないでください。Frappe UI(Vue 3 + TailwindCSS)が標準です。これにより、CRM、Helpdesk、その他の公式 Frappe アプリとの一貫性が保たれます。
  • FrappeUI コンポーネントを使用してください:一貫性を保つため、カスタム HTML よりも <Button>、<Input>、<FormControl> を優先してください。
  • CRM/Helpdesk アプリのシェルパターンに従ってください:CRUD アプリでは、公式 Frappe アプリのサイドバーナビゲーション、リストビュー、フォームレイアウト、ルーティングパターンを説明する frappe-ui-patterns スキルに従ってください。
  • 読み込み状態を処理してください:API 呼び出し中は必ず読み込みインジケーターを表示してください。resource.loading を使用してください。
  • API レスポンスを検証してください:データにアクセスする前にエラーを確認し、exc レスポンスを処理してください。
  • プロキシを正しく設定してください:開発サーバーで API 呼び出しを Frappe バックエンドにプロキシする必要があります。
  • 認証を処理してください:$session.user を確認し、必要に応じてログインページにリダイレクトしてください。

よくある間違い

間違い失敗する理由修正方法
CORS/プロキシの設定漏れ開発環境で API 呼び出しが失敗します/api を Frappe サイトに転送するよう Vite プロキシを設定します
認証状態を処理しないログアウト中のユーザーでアプリがクラッシュしますマウント時に call('frappe.auth.get_logged_user') を確認します
リソース URL の誤りAPI 呼び出しで 404 エラーが発生します正しいメソッドパスで createResource を使用します
サイト URL のハードコード環境間で動作しなくなります相対 URL または環境変数を使用します
CSRF トークンを含めないPOST リクエストが失敗しますfrappe.csrf_token を使用するか、セッションを適切に設定します
TailwindCSS 設定の漏れFrappe UI のスタイルが崩れますTailwind のコンテンツパスに frappe-ui を含めます
vanilla JS/jQuery の使用UX に一貫性がなく、保守の負担が増しますカスタムフロントエンドには必ず Frappe UI を使用してください
独自のアプリシェルデザインエコシステムとの一貫性がなくなりますナビゲーション、リスト、フォームには CRM/Helpdesk のパターンに従ってください

コメント