mcpField
F
frontend-development

frontend-development

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

frontend-development について

Frappe UI、Vue 3、TailwindCSSを使ったFrappeアプリのフロントエンド開発を案内するスキルです。SPAとポータルページの構築方法を扱います。

主な機能

  1. 01SPAまたはポータルページの選択
  2. 02Vue 3フロントエンド環境の設定
  3. 03リソースによるデータ取得
  4. 04Frappe UIコンポーネントの利用

活用シーン

  1. 01FrappeアプリのカスタムSPA構築
  2. 02一般公開ポータルページの作成
  3. 03リソース経由での文書・リスト取得

インストール

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

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

インストール後、Claude Code で/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 の機能強化フォーム/リストのスクリプト(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 のスクリプト作成について → desk-customization
  • API エンドポイントの実装について → api-development
  • アプリのアーキテクチャについて → app-development
  • 公式アプリの UI/UX パターンについて → ui-patterns

参考資料

ガードレール

  • カスタムフロントエンドには必ず Frappe UI を使用してください:アプリのフロントエンドにバニラ JS、jQuery、カスタムフレームワークを使用しないでください。標準は Frappe UI(Vue 3 + TailwindCSS)です。これにより、CRM、Helpdesk、その他の公式 Frappe アプリとの一貫性が保たれます。
  • FrappeUI コンポーネントを使用してください:一貫性を保つため、カスタム HTML よりも <Button>、<Input>、<FormControl> を優先してください。
  • CRM/Helpdesk アプリのシェルパターンに従ってください:CRUD アプリでは、公式 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 を含めます
バニラ JS/jQuery を使用しているUX の一貫性が損なわれ、保守の負担が増えますカスタムフロントエンドには必ず Frappe UI を使用します
カスタムのアプリシェルを設計しているエコシステムとの一貫性が損なわれますナビゲーション、リスト、フォームには CRM/Helpdesk のパターンに従います

コメント