2024-12-22

ChatGPTを使って、アプリ開発(ワークフロー6):開発4

 前回のChatGPTの提案で作成したコードで開発環境サーバーを起動したところ、「django.core.exceptions.FieldError: Unknown field(s) (assigned_user) specified for Task」というエラーが出ました。これは、最初に作成した「Task」モデルに「assigned_user」というフィールドがないためです。次の担当者を示す「assigned_user」ですが、ワークフローの実行時に選択するようにするので、form.pyを以下のように変更します。

from django import forms
from .models import Flow, Task


class FlowForm(forms.ModelForm):
    class Meta:
        model = Flow
        fields = ['name']


class TaskForm(forms.ModelForm):
    class Meta:
        model = Task
        fields = ['name']さ

最初の画面の「新規作成」をクリックします。






「django.template.exceptions.TemplateDoesNotExist: flow_create.html」というエラーが発生しました。これは、「flow_create.html」というテンプレートとが存在しないためです。ChatGPTの指示で作成したテンプレートは「flow_create_edit.html」なので、views.pyを以下のように修正します。

# フロー作成ビュー
def flow_create(request):
    if request.method == 'POST':
        # フォームの処理を追加
        # 新しいフローを作成し保存する
        return redirect('flow_list')
    return render(request, 'workflow/flow_create_edit.html')

前の画面に戻って、再度、「新規作成」をクリックします。

以下のような画面が開きます。








フロー作成画面について、もう少し画面構成や機能を追加していきます。まずは、ChatGPTに要求を伝えます。


ChatGPTへのスクリプト:

タスクを作成するエリアを四角で囲み、その中に「START」と「END」というボックス(角丸のボックス)をデフォルトで作成し、「タスク作成」ボタンをクリックしたら、タスク名を入力する欄と、「入力項目設定」というボタンを中に含んだボックス(角丸のボックス)を追加したい。なお、タスクを作成するエリアは可変とし、画面オーバーした場合はスクロールできるようにする。デフォルトの大きさは、1200px×600pxにしたい。 「START」のボックスは左上に、「END」のボックスは右下に配置したい。


ChatGPTからの回答:

この要件に基づいて、Djangoのテンプレートに必要なHTMLとCSSを追加し、タスク作成エリアを実装します。タスクエリアは可変で、デフォルトのサイズが1200px × 600pxとなり、スクロールが可能な仕組みにします。また、「START」と「END」のボックスは指定の位置に配置し、タスクを追加できるようにします。

1. HTMLとCSSのテンプレート

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>フロー作成</title>
    <link rel="stylesheet" href="{% static 'css/styles.css' %}">
    <style>
        /* タスク作成エリアのスタイル */
        #task-area {
            width: 1200px;
            height: 600px;
            border: 2px solid #ccc;
            position: relative;
            overflow: auto;
            padding: 10px;
        }

        /* STARTとENDボックスのスタイル */
        .task-box {
            width: 150px;
            height: 50px;
            border-radius: 10px;
            background-color: #f0f0f0;
            border: 2px solid #333;
            display: flex;
            justify-content: center;
            align-items: center;
            position: absolute;
            text-align: center;
        }

        #start-box {
            top: 10px;
            left: 10px;
        }

        #end-box {
            bottom: 10px;
            right: 10px;
        }

        /* 新しいタスクボックスのスタイル */
        .new-task-box {
            width: 200px;
            height: 100px;
            border-radius: 10px;
            background-color: #e0f7fa;
            border: 2px solid #00796b;
            position: absolute;
            padding: 10px;
            box-sizing: border-box;
        }

        .new-task-box input[type="text"] {
            width: 90%;
            margin-bottom: 10px;
        }

        .new-task-box button {
            width: 100%;
        }

        /* タスクエリア全体がスクロール可能な場合のスタイル */
        #task-area::-webkit-scrollbar {
            width: 12px;
            height: 12px;
        }

        #task-area::-webkit-scrollbar-thumb {
            background-color: #888;
            border-radius: 10px;
        }

        #task-area::-webkit-scrollbar-track {
            background-color: #f0f0f0;
        }

    </style>
</head>
<body>
    <h1>フロー作成</h1>

    <!-- タスク作成エリア -->
    <div id="task-area">
        <!-- STARTボックス -->
        <div id="start-box" class="task-box">
            START
        </div>

        <!-- ENDボックス -->
        <div id="end-box" class="task-box">
            END
        </div>
    </div>

    <!-- タスク作成ボタン -->
    <button type="button" id="add-task-btn">タスク作成</button>

    <script>
        let taskCounter = 0;  // タスクIDカウンター

        // タスク作成ボタンのクリックイベント
        document.getElementById('add-task-btn').addEventListener('click', function() {
            taskCounter += 1;
            const taskArea = document.getElementById('task-area');

            // 新しいタスクボックスを作成
            const newTaskBox = document.createElement('div');
            newTaskBox.className = 'new-task-box';
            newTaskBox.style.top = (50 + taskCounter * 60) + 'px';  // 位置を動的に設定
            newTaskBox.style.left = (50 + taskCounter * 60) + 'px'; // 位置を動的に設定

            // タスク名入力欄を追加
            const taskInput = document.createElement('input');
            taskInput.type = 'text';
            taskInput.placeholder = 'タスク名を入力';

            // 入力項目設定ボタンを追加
            const settingsButton = document.createElement('button');
            settingsButton.textContent = '入力項目設定';

            // 新しいタスクボックスに要素を追加
            newTaskBox.appendChild(taskInput);
            newTaskBox.appendChild(settingsButton);

            // タスクエリアに新しいタスクボックスを追加
            taskArea.appendChild(newTaskBox);
        });
    </script>
</body>
</html>


2. 説明

  1. タスク作成エリア (#task-area):
    このエリアはスクロール可能で、デフォルトのサイズが1200px × 600pxです。overflow: autoで、内容がエリアを超える場合にスクロールできるようにしています。

  2. STARTENDのボックス:
    #start-box#end-boxというIDを持った角丸のボックスがそれぞれ左上(top: 10px; left: 10px;)と右下(bottom: 10px; right: 10px;)に配置されています。

  3. タスク作成ボタン:
    「タスク作成」ボタンをクリックすると、タスク名を入力する欄と「入力項目設定」ボタンを含んだ新しいタスクボックスがタスクエリアに動的に追加されます。ボックスは自動的に位置が調整されますが、最初はtopleftの位置を固定的にずらすことで配置しています。

  4. JavaScript:
    タスク作成ボタンをクリックすることで、new-task-boxが作成され、タスクエリア内に追加されます。taskCounterによって、ボックスが配置される位置が異なるように動的に調整されます。

3. 今後の拡張

  • タスクのドラッグ&ドロップ:
    例えば、jsPlumbDragulaなどのライブラリを利用して、タスクボックスをドラッグ&ドロップで移動可能にすることができます。

  • 入力項目設定のポップアップ:
    「入力項目設定」ボタンをクリックした際に、ポップアップウィンドウを表示して、入力項目を動的に追加・削除する機能を実装することができます。

  • タスク間の接続:
    タスク間を線で結ぶ機能も後で追加することで、ワークフローの全体像を視覚化できます。

このテンプレートを基に、実際のワークフロー作成・編集機能のユーザーインターフェースを実装することができます。


このページを再表示すると、以下のような画面になります。









左下の「タスク作成」ボタンをクリックすると、タスクのボックスが表示されます。









ChatGPTへのスクリプト:

flow_create_edit.htmlでタスクを作成後、そのタスクボックスにある「入力項目設定ボタン」をクリックしたときに、ダイアログボックスを表示させ、項目を設定したい。ダイアログボックスの仕様は以下の通り。 ・上部に項目名、データタイプ(一覧から選択)、データタイプがCharなら最大文字数、データタイプがマスターなら対象マスターを一覧から選択 ・下の部分にはすでに登録済の項目の一覧、一覧には上部の入力項目をテーブル形式で表示


ChatGPTからの回答:

目的は「入力項目設定ボタン」をクリックした際に、指定された仕様のダイアログボックスを表示させることです。

実装手順

1. ダイアログボックスのHTML構造

ダイアログボックスはモーダル形式で表示します。必要な項目を含むHTMLを作成し、CSSとJavaScriptを使用して表示・非表示を制御します。

2. JavaScriptでダイアログの動作を制御

「入力項目設定」ボタンをクリックしたときに、モーダルを表示します。また、入力内容を保持するための操作を実装します。

3. CSSでモーダルの見た目を整える

ダイアログボックスのレイアウトを設定し、背景が暗くなるようにします。


実装コード


<!-- タスクボックス内に含まれる入力項目設定ボタンのダイアログボックス -->
<div id="dialog" class="modal" style="display: none;">
    <div class="modal-content">
        <span class="close">×</span>
        <h2>入力項目設定</h2>
        
        <!-- 上部: 項目名、データタイプ、追加設定 -->
        <div>
            <label for="field-name">項目名:</label>
            <input type="text" id="field-name" placeholder="項目名を入力">

            <label for="data-type">データタイプ:</label>
            <select id="data-type">
                <option value="Char">Char</option>
                <option value="Master">Master</option>
                <option value="Integer">Integer</option>
            </select>

            <div id="char-settings" style="display: none;">
                <label for="max-length">最大文字数:</label>
                <input type="number" id="max-length" placeholder="文字数を入力">
            </div>

            <div id="master-settings" style="display: none;">
                <label for="master-list">対象マスター:</label>
                <select id="master-list">
                    <option value="Master1">Master1</option>
                    <option value="Master2">Master2</option>
                    <option value="Master3">Master3</option>
                </select>
            </div>
        </div>

        <!-- 下部: 登録済の項目一覧 -->
        <h3>登録済の項目一覧</h3>
        <table id="field-table" border="1">
            <thead>
                <tr>
                    <th>項目名</th>
                    <th>データタイプ</th>
                    <th>設定</th>
                </tr>
            </thead>
            <tbody>
                <!-- JavaScriptで動的に項目を追加 -->
            </tbody>
        </table>

        <button id="save-field">項目を追加</button>
    </div>
</div>

<script>
    // ダイアログボックスを制御するスクリプト
    document.addEventListener('DOMContentLoaded', function() {
        const dialog = document.getElementById('dialog');
        const closeBtn = document.querySelector('.close');
        const addFieldButton = document.getElementById('save-field');
        const dataTypeSelect = document.getElementById('data-type');
        const charSettings = document.getElementById('char-settings');
        const masterSettings = document.getElementById('master-settings');
        const fieldTableBody = document.querySelector('#field-table tbody');

        // 「入力項目設定」ボタンがクリックされたときにモーダルを表示
        document.querySelectorAll('.new-task-box button').forEach(button => {
            button.addEventListener('click', function() {
                dialog.style.display = 'block';
            });
        });

        // モーダルを閉じる
        closeBtn.addEventListener('click', function() {
            dialog.style.display = 'none';
        });

        // データタイプに応じて追加設定を表示
        dataTypeSelect.addEventListener('change', function() {
            const selectedType = this.value;
            charSettings.style.display = selectedType === 'Char' ? 'block' : 'none';
            masterSettings.style.display = selectedType === 'Master' ? 'block' : 'none';
        });

        // 項目を追加
        addFieldButton.addEventListener('click', function() {
            const fieldName = document.getElementById('field-name').value;
            const dataType = dataTypeSelect.value;
            const maxLength = document.getElementById('max-length').value;
            const masterList = document.getElementById('master-list').value;

            // 入力内容をテーブルに追加
            const newRow = document.createElement('tr');
            newRow.innerHTML = `
                <td>${fieldName}</td>
                <td>${dataType}</td>
                <td>${dataType === 'Char' ? `最大文字数: ${maxLength}` : dataType === 'Master' ? `対象: ${masterList}` : ''}</td>
            `;
            fieldTableBody.appendChild(newRow);

            // フィールドをリセット
            document.getElementById('field-name').value = '';
            dataTypeSelect.value = 'Char';
            charSettings.style.display = 'none';
            masterSettings.style.display = 'none';
        });
    });
</script>

<style>
    /* モーダルのスタイル */
    .modal {
        position: fixed;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        background-color: rgba(0, 0, 0, 0.5);
        display: flex;
        justify-content: center;
        align-items: center;
    }

    .modal-content {
        background-color: white;
        padding: 20px;
        border-radius: 8px;
        width: 500px;
    }

    .close {
        position: absolute;
        top: 10px;
        right: 10px;
        cursor: pointer;
    }
</style>

仕様に基づいた機能説明

  1. 入力項目設定フォーム:

    • 項目名、データタイプを入力または選択。
    • データタイプが Char の場合は最大文字数を入力。
    • データタイプが Master の場合は対象マスターを選択。
  2. 登録済の項目一覧:

    • 入力された項目をテーブル形式で下部に表示。
  3. 動的なインタラクション:

    • JavaScriptでデータタイプに応じた追加設定の表示切り替え。
    • 項目を追加するたびにフォームをリセット。

これで、タスクごとにダイアログボックスを使って入力項目を設定する基本的な仕組みが完成します。


ChatGPTへのスクリプト:

flow_create_edit.htmlの全体のコードは?


ChatGPTからの回答:

以下は、flow_create_edit.html の全体コードです。これには、タスクボックスの「入力項目設定」ボタンをクリックするとモーダルダイアログが表示され、入力項目を設定する機能を追加したものを含みます。


完全版 flow_create_edit.html

{% extends '../base.html' %}
{% load static %}
{% block contents %}
<style>
    /* タスク作成エリアのスタイル */
    #task-area {
        width: 1200px;
        height: 600px;
        border: 2px solid #ccc;
        position: relative;
        overflow: auto;
        padding: 10px;
    }

    /* STARTとENDボックスのスタイル */
    .task-box {
        width: 150px;
        height: 50px;
        border-radius: 10px;
        background-color: #f0f0f0;
        border: 2px solid #333;
        display: flex;
        justify-content: center;
        align-items: center;
        position: absolute;
        text-align: center;
    }

    #start-box {
        top: 10px;
        left: 10px;
    }

    #end-box {
        bottom: 10px;
        right: 10px;
    }

    /* 新しいタスクボックスのスタイル */
    .new-task-box {
        width: 200px;
        height: 100px;
        border-radius: 10px;
        background-color: #e0f7fa;
        border: 2px solid #00796b;
        position: absolute;
        padding: 10px;
        box-sizing: border-box;
    }

    .new-task-box input[type="text"] {
        width: 90%;
        margin-bottom: 10px;
    }

    .new-task-box button {
        width: 100%;
    }

    /* タスクエリア全体がスクロール可能な場合のスタイル */
    #task-area::-webkit-scrollbar {
        width: 12px;
        height: 12px;
    }

    #task-area::-webkit-scrollbar-thumb {
        background-color: #888;
        border-radius: 10px;
    }

    #task-area::-webkit-scrollbar-track {
        background-color: #f0f0f0;
    }

    /* モーダルのスタイル */
    .modal {
        position: fixed;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        background-color: rgba(0, 0, 0, 0.5);
        display: flex;
        justify-content: center;
        align-items: center;
    }

    .modal-content {
        background-color: white;
        padding: 20px;
        border-radius: 8px;
        width: 500px;
        position: relative;
    }

    .close {
        position: absolute;
        top: 10px;
        right: 10px;
        cursor: pointer;
    }
</style>

<h1>フロー作成</h1>

<!-- タスク作成エリア -->
<div id="task-area">
    <!-- STARTボックス -->
    <div id="start-box" class="task-box">
        START
    </div>

    <!-- ENDボックス -->
    <div id="end-box" class="task-box">
        END
    </div>
</div>

<!-- タスク作成ボタン -->
<button type="button" id="add-task-btn">タスク作成</button>

<!-- ダイアログボックス -->
<div id="dialog" class="modal" style="display: none;">
    <div class="modal-content">
        <span class="close">×</span>
        <h2>入力項目設定</h2>
        
        <!-- 上部: 項目名、データタイプ、追加設定 -->
        <div>
            <label for="field-name">項目名:</label>
            <input type="text" id="field-name" placeholder="項目名を入力">

            <label for="data-type">データタイプ:</label>
            <select id="data-type">
                <option value="Char">Char</option>
                <option value="Master">Master</option>
                <option value="Integer">Integer</option>
            </select>

            <div id="char-settings" style="display: none;">
                <label for="max-length">最大文字数:</label>
                <input type="number" id="max-length" placeholder="文字数を入力">
            </div>

            <div id="master-settings" style="display: none;">
                <label for="master-list">対象マスター:</label>
                <select id="master-list">
                    <option value="Master1">Master1</option>
                    <option value="Master2">Master2</option>
                    <option value="Master3">Master3</option>
                </select>
            </div>
        </div>

        <!-- 下部: 登録済の項目一覧 -->
        <h3>登録済の項目一覧</h3>
        <table id="field-table" border="1">
            <thead>
                <tr>
                    <th>項目名</th>
                    <th>データタイプ</th>
                    <th>設定</th>
                </tr>
            </thead>
            <tbody>
                <!-- JavaScriptで動的に項目を追加 -->
            </tbody>
        </table>

        <button id="save-field">項目を追加</button>
    </div>
</div>

<script>
    let taskCounter = 0;

    // タスク作成ボタンのクリックイベント
    document.getElementById('add-task-btn').addEventListener('click', function() {
        taskCounter += 1;
        const taskArea = document.getElementById('task-area');

        const newTaskBox = document.createElement('div');
        newTaskBox.className = 'new-task-box';
        newTaskBox.style.top = (50 + taskCounter * 60) + 'px';
        newTaskBox.style.left = (50 + taskCounter * 60) + 'px';

        const taskInput = document.createElement('input');
        taskInput.type = 'text';
        taskInput.placeholder = 'タスク名を入力';

        const settingsButton = document.createElement('button');
        settingsButton.textContent = '入力項目設定';
        settingsButton.addEventListener('click', function() {
            document.getElementById('dialog').style.display = 'block';
        });

        newTaskBox.appendChild(taskInput);
        newTaskBox.appendChild(settingsButton);

        taskArea.appendChild(newTaskBox);
    });

    // ダイアログ制御
    const dialog = document.getElementById('dialog');
    const closeBtn = document.querySelector('.close');
    const dataTypeSelect = document.getElementById('data-type');
    const charSettings = document.getElementById('char-settings');
    const masterSettings = document.getElementById('master-settings');
    const fieldTableBody = document.querySelector('#field-table tbody');

    closeBtn.addEventListener('click', function() {
        dialog.style.display = 'none';
    });

    dataTypeSelect.addEventListener('change', function() {
        charSettings.style.display = this.value === 'Char' ? 'block' : 'none';
        masterSettings.style.display = this.value === 'Master' ? 'block' : 'none';
    });

    document.getElementById('save-field').addEventListener('click', function() {
        const fieldName = document.getElementById('field-name').value;
        const dataType = dataTypeSelect.value;
        const maxLength = document.getElementById('max-length').value;
        const masterList = document.getElementById('master-list').value;

        const newRow = document.createElement('tr');
        newRow.innerHTML = `
            <td>${fieldName}</td>
            <td>${dataType}</td>
            <td>${dataType === 'Char' ? `最大文字数: ${maxLength}` : dataType === 'Master' ? `対象: ${masterList}` : ''}</td>
        `;
        fieldTableBody.appendChild(newRow);

        document.getElementById('field-name').value = '';
        dataTypeSelect.value = 'Char';
        charSettings.style.display = 'none';
        masterSettings.style.display = 'none';
    });
</script>
{% endblock %}

このコードで、タスクボックスの「入力項目設定」ボタンをクリックすると、仕様に基づくモーダルダイアログが表示され、項目を追加することが可能になります。


このコードを実装後、「入力項目設定」ボタンをクリックすると、以下のような入力ボックスが表示されます。






次回はこの入力ボックスをアレンジしていきたいと思います。