Newer
Older
NocodeToolOnRDL / src / components / property / buttonAction.tsx
@Sakoda2269 Sakoda2269 11 days ago 39 KB reactに移行完了
import type{ SelectComponentListenerType } from "../../hooks/useComponent";
import { ButtonActionTypeMap } from "../../types/action"; 
import type { ButtonActionType, ButtonAddDataAction, ButtonDeleteDataAction, ButtonNavigateAction, ButtonSearchDataAction, ButtonUpdateDataAction } from "../../types/action";
import type { ButtonComponent, Component } from "../../types/component";
import type { Screen } from "../../types/screen";
import type { Table } from "../../types/table";
import { useEffect, useMemo, useState } from "react"
import { MdEdit } from "react-icons/md";

interface ButtonActionPropertiesProps {
    component: ButtonComponent
    screens: Screen[],
    curScreenIndex: number,
    tables: Table[],
    updateComponent: (index: number, newComponent: Component) => void,
    setSelectComponentListener: (listener: SelectComponentListenerType) => void
    activateSelectMode: () => void
}

export function ButtonActionProperties({
    component,
    screens,
    curScreenIndex,
    tables,
    updateComponent,
    setSelectComponentListener,
    activateSelectMode
}: ButtonActionPropertiesProps) {

    const buttonAction = useMemo(() => component.action?.type ?? "", [component]);
    const [action, setAction] = useState<ButtonActionType | "">(component.action?.type ?? "");

    useEffect(() => {
        // eslint-disable-next-line react-hooks/set-state-in-effect
        setAction(buttonAction)
    }, [buttonAction])

    return (
        <div className="flex flex-col">
            <div className="mb-4">
                <label className="block font-medium text-gray-700 mb-2">
                    アクション
                </label>
                <select
                    className="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
                    value={action}
                    onChange={(e) => setAction(e.target.value as (ButtonActionType | ""))}
                >
                    <option value={""}>選択してください</option>
                    {Object.keys(ButtonActionTypeMap).map((key) =>
                        <option key={key} value={key}>{key}</option>
                    )}
                </select>
            </div>
            {action === "navigate" && <NavigateProperties
                component={component}
                screens={screens}
                curScreenIndex={curScreenIndex}
                updateComponent={updateComponent}
            />}
            {action === "addData" && <AddDataProperties
                component={component}
                screens={screens}
                tables={tables}
                updateComponent={updateComponent}
                setSelectComponentListener={setSelectComponentListener}
                activateSelectMode={activateSelectMode}
            />}
            {action === "updateData" && <UpdateDataProperties
                component={component}
                screens={screens}
                tables={tables}
                updateComponent={updateComponent}
                setSelectComponentListener={setSelectComponentListener}
                activateSelectMode={activateSelectMode}
            />}
            {action === "deleteData" && <DeleteDataProperties
                component={component}
                screens={screens}
                tables={tables}
                updateComponent={updateComponent}
                setSelectComponentListener={setSelectComponentListener}
                activateSelectMode={activateSelectMode}
            />}
            {action === "searchData" && <SearchDataProperties
                component={component}
                tables={tables}
                updateComponent={updateComponent}
                setSelectComponentListener={setSelectComponentListener}
                activateSelectMode={activateSelectMode}
            />}
        </div>
    )
}

interface NavigatePropertiesProps {
    component: ButtonComponent
    screens: Screen[],
    curScreenIndex: number,
    updateComponent: (index: number, component: Component) => void
}

function NavigateProperties({
    screens,
    curScreenIndex,
    component,
    updateComponent,
}: NavigatePropertiesProps) {

    const originalScreenName = useMemo(() => (component.action?.action as ButtonNavigateAction)?.nextScreenName ?? "", [component])
    const [screenName, setScreenName] = useState<string>(originalScreenName);

    useEffect(() => {
        // eslint-disable-next-line react-hooks/set-state-in-effect
        setScreenName(originalScreenName)
    }, [originalScreenName])

    const validate = () => {
        if (!screenName.trim()) {
            return false;
        }
        if (screenName === "") {
            return false;
        }
        return true;
    }

    const handleConfirm = () => {
        const navigateAction: ButtonNavigateAction = {
            nextScreenName: screenName
        }
        const newComponent: ButtonComponent = { ...component, action: { type: "navigate", action: navigateAction } }
        updateComponent(-1, newComponent);
    }

    return (
        <div className="flex-col">
            <div className="mb-4">
                <label className="block font-medium text-gray-700 mb-2">
                    遷移先スクリーン
                </label>
                <select
                    className="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
                    value={screenName}
                    onChange={(e) => setScreenName(e.target.value)}
                >
                    <option value={""}>選択してください</option>
                    <option value="_back">戻る</option>
                    {screens.filter((_, idx) => idx !== curScreenIndex).map((screen, idx) => (
                        <option key={idx} value={screen.name}>
                            {screen.name}
                        </option>
                    ))}
                </select>
            </div>
            <div className="mb-4 flex justify-end">
                {validate() &&
                    <button
                        onClick={handleConfirm}
                        disabled={(component.action?.action as ButtonNavigateAction)?.nextScreenName === screenName}
                        className="px-4 py-2 text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 disabled:bg-gray-300 disabled:cursor-not-allowed rounded-lg transition-colors"
                    >
                        決定
                    </button>
                }
            </div>
        </div>
    )
}

interface AddDataPropertiesProps {
    component: ButtonComponent
    screens: Screen[]
    tables: Table[]
    updateComponent: (index: number, newComponent: Component) => void,
    setSelectComponentListener: (listener: SelectComponentListenerType) => void
    activateSelectMode: () => void
}

function AddDataProperties({
    component,
    screens,
    tables,
    updateComponent,
    setSelectComponentListener,
    activateSelectMode
}: AddDataPropertiesProps) {

    const buttonAction = useMemo(() => (component.action?.type ?? "") === "addData" ? component.action?.action as ButtonAddDataAction
        : {
            tableName: "",
            newData: {},
            failScreenName: "",
            successScreenName: ""
        }, [component])

    const [tableName, setTableName] = useState<string>(buttonAction?.tableName ?? "");
    const [data, setData] = useState<{ [key: string]: string }>(buttonAction?.newData ?? {});
    const [selectModeButton, setSelectModeButton] = useState<string>("");
    const [successScreen, setSuccessScreen] = useState<string>(buttonAction?.successScreenName ?? "")
    const [failScreen, setFailScreen] = useState<string>(buttonAction?.failScreenName ?? "")

    const handleTableChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
        setTableName(e.target.value);
        const res: { [key: string]: string } = {}
        for (const col of tables.find((table) => table.name === e.target.value)?.columns ?? []) {
            if (!col.foreignTableName) {
                res[col.name] = "";
            } else {
                const ftable = tables.find((table) => table.name === col.foreignTableName);
                for (const fcol of ftable?.columns ?? []) {
                    if (fcol.isPrimary) {
                        res[`${ftable?.name}.${fcol.name}`] = "";
                    }
                }
            }
        }
        setData(res);
        setSuccessScreen("");
        setFailScreen("");
    }

    const handleChange = (key: string, value: string) => {
        setData(prev => ({ ...prev, [key]: value }));
    }

    const handleSelectMode = (key: string) => {
        setSelectComponentListener((component) => {
            if (component !== null) {
                setData(prev => ({ ...prev, [key]: "${" + component.name + "}" }));
            }
            setSelectModeButton("");
        })
        setSelectModeButton(key);
        activateSelectMode();
    }

    const validate = () => {
        if (tableName === "") {
            return false;
        }
        for (const key of Object.keys(data)) {
            if (data[key] === "") {
                return false;
            }
        }
        return true;
    }

    const handleConfirm = () => {
        const action: ButtonAddDataAction = {
            tableName: tableName,
            newData: data,
            successScreenName: successScreen,
            failScreenName: failScreen
        }
        const newComponent: ButtonComponent = { ...component, action: { type: "addData", action: action } };
        updateComponent(-1, newComponent)
    }

    const changeCheck = () => {
        if (!buttonAction) {
            return false;
        }
        if (buttonAction.tableName !== tableName) {
            return false;
        }
        for (const key of Object.keys(buttonAction.newData)) {
            if (!(key in data)) {
                return false;
            }
            if (data[key] !== buttonAction.newData[key]) {
                return false;
            }
        }
        if (buttonAction.successScreenName !== successScreen) {
            return false;
        }
        if (buttonAction.failScreenName !== failScreen) {
            return false;
        }
        return true;
    }

    return (
        <div className="flex h-full min-h-0 flex-col overflow-y-auto">
            <div className="mb-4">
                <label className="block font-medium text-gray-700 mb-2">
                    テーブル
                </label>
                <select
                    className="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
                    value={tableName}
                    onChange={handleTableChange}
                >
                    <option value={""}>選択してください</option>
                    {tables.map((table, idx) => (
                        <option key={idx} value={table.name}>
                            {table.name}
                        </option>
                    ))}
                </select>
            </div>
            {tableName !== "" && <div className="flex-col">
                {Object.keys(data).map((colName, idx) => (
                    <div key={`col${idx}2`} className="mb-4">
                        <label className="block font-medium text-gray-700 mb-2">
                            {colName}
                        </label>
                        <div className="flex gap-4">
                            <input
                                type="text"
                                value={data[colName] ?? ""}
                                onChange={(e) => handleChange(colName, e.target.value)}
                                className="flex-6 w-full px-3 py-1 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 outline-none transition-colors"
                            />
                            <button
                                onClick={() => handleSelectMode(colName)}
                                className={`flex-1 flex items-center border border-gray-300 justify-center 
                                    rounded-lg hover:bg-gray-300 hover:text-gray-900 
                                    transition ${selectModeButton === colName ? "bg-blue-500 text-gray-100" : "text-gray-600"}`}
                            >
                                <MdEdit />
                            </button>
                        </div>
                    </div>
                ))}
                <div className="mb-4">
                    <label className="block font-medium text-gray-700 mb-2">
                        成功時スクリーン
                    </label>
                    <select
                        className="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
                        value={successScreen}
                        onChange={(e) => setSuccessScreen(e.target.value)}
                    >
                        <option value={""}>なし</option>
                        <option value="_back">戻る</option>
                        {screens.map((screen, idx) => (
                            <option key={idx} value={screen.name}>
                                {screen.name}
                            </option>
                        ))}
                    </select>
                </div>
                <div className="mb-4">
                    <label className="block font-medium text-gray-700 mb-2">
                        失敗時スクリーン
                    </label>
                    <select
                        className="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
                        value={failScreen}
                        onChange={(e) => setFailScreen(e.target.value)}
                    >
                        <option value={""}>なし</option>
                        <option value="_back">戻る</option>
                        {screens.map((screen, idx) => (
                            <option key={idx} value={screen.name}>
                                {screen.name}
                            </option>
                        ))}
                    </select>
                </div>
            </div>}
            <div className="mb-4 flex justify-end">
                <button
                    onClick={handleConfirm}
                    disabled={changeCheck() || !validate()}
                    className="px-4 py-2 text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 disabled:bg-gray-300 disabled:cursor-not-allowed rounded-lg transition-colors"
                >
                    決定
                </button>
            </div>
        </div>
    )
}



interface UpdateDataPropertiesProps {
    component: ButtonComponent
    screens: Screen[]
    tables: Table[]
    updateComponent: (index: number, newComponent: Component) => void,
    setSelectComponentListener: (listener: SelectComponentListenerType) => void
    activateSelectMode: () => void
}

function UpdateDataProperties({
    component,
    screens,
    tables,
    updateComponent,
    setSelectComponentListener,
    activateSelectMode
}: UpdateDataPropertiesProps) {

    const buttonAction = useMemo(() => (component.action?.type ?? "") === "updateData" ? component.action?.action as ButtonUpdateDataAction
        : {
            tableName: "",
            newData: {},
            failScreenName: "",
            successScreenName: ""
        }, [component])

    const [tableName, setTableName] = useState<string>(buttonAction?.tableName ?? "");
    const [data, setData] = useState<{ [key: string]: string }>(buttonAction?.newData ?? {});
    const [selectModeButton, setSelectModeButton] = useState<string>("");
    const [successScreen, setSuccessScreen] = useState<string>(buttonAction?.successScreenName ?? "")
    const [failScreen, setFailScreen] = useState<string>(buttonAction?.failScreenName ?? "")

    const handleTableChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
        setTableName(e.target.value);
        const res: { [key: string]: string } = {}
        for (const col of tables.find((table) => table.name === e.target.value)?.columns ?? []) {
            if (!col.foreignTableName) {
                res[col.name] = "";
            } else {
                const ftable = tables.find((table) => table.name === col.foreignTableName);
                for (const fcol of ftable?.columns ?? []) {
                    if (fcol.isPrimary) {
                        res[`${ftable?.name}.${fcol.name}`] = "";
                    }
                }
            }
        }
        setData(res);
        setSuccessScreen("");
        setFailScreen("");
    }

    const handleChange = (key: string, value: string) => {
        setData(prev => ({ ...prev, [key]: value }));
    }

    const handleSelectMode = (key: string) => {
        setSelectComponentListener((component) => {
            if (component !== null) {
                setData(prev => ({ ...prev, [key]: "${" + component.name + "}" }));
            }
            setSelectModeButton("");
        })
        setSelectModeButton(key);
        activateSelectMode();
    }

    const validate = () => {
        if (tableName === "") {
            return false;
        }
        for (const key of Object.keys(data)) {
            if (data[key] === "") {
                return false;
            }
        }
        return true;
    }

    const handleConfirm = () => {
        const action: ButtonUpdateDataAction = {
            tableName: tableName,
            newData: data,
            successScreenName: successScreen,
            failScreenName: failScreen
        }
        const newComponent: ButtonComponent = { ...component, action: { type: "updateData", action: action } };
        updateComponent(-1, newComponent)
    }

    const changeCheck = () => {
        if (!buttonAction) {
            return false;
        }
        if (buttonAction.tableName !== tableName) {
            return false;
        }
        for (const key of Object.keys(buttonAction.newData)) {
            if (!(key in data)) {
                return false;
            }
            if (data[key] !== buttonAction.newData[key]) {
                return false;
            }
        }
        if (buttonAction.successScreenName !== successScreen) {
            return false;
        }
        if (buttonAction.failScreenName !== failScreen) {
            return false;
        }
        return true;
    }

    return (
        <div className="flex-col overflow-y-auto">
            <div className="mb-4">
                <label className="block font-medium text-gray-700 mb-2">
                    テーブル
                </label>
                <select
                    className="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
                    value={tableName}
                    onChange={handleTableChange}
                >
                    <option value={""}>選択してください</option>
                    {tables.map((table, idx) => (
                        <option key={idx} value={table.name}>
                            {table.name}
                        </option>
                    ))}
                </select>
            </div>
            {tableName !== "" && <div className="flex-col">
                {Object.keys(data).map((colName, idx) => (
                    <div key={`col${idx}2`} className="mb-4">
                        <label className="block font-medium text-gray-700 mb-2">
                            {colName}
                        </label>
                        <div className="flex gap-4">
                            <input
                                type="text"
                                value={data[colName] ?? ""}
                                onChange={(e) => handleChange(colName, e.target.value)}
                                className="flex-6 w-full px-3 py-1 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 outline-none transition-colors"
                            />
                            <button
                                onClick={() => handleSelectMode(colName)}
                                className={`flex-1 flex items-center border border-gray-300 justify-center 
                                    rounded-lg hover:bg-gray-300 hover:text-gray-900 
                                    transition ${selectModeButton === colName ? "bg-blue-500 text-gray-100" : "text-gray-600"}`}
                            >
                                <MdEdit />
                            </button>
                        </div>
                    </div>
                ))}
                <div className="mb-4">
                    <label className="block font-medium text-gray-700 mb-2">
                        成功時スクリーン
                    </label>
                    <select
                        className="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
                        value={successScreen}
                        onChange={(e) => setSuccessScreen(e.target.value)}
                    >
                        <option value={""}>なし</option>
                        <option value="_back">戻る</option>
                        {screens.map((screen, idx) => (
                            <option key={idx} value={screen.name}>
                                {screen.name}
                            </option>
                        ))}
                    </select>
                </div>
                <div className="mb-4">
                    <label className="block font-medium text-gray-700 mb-2">
                        失敗時スクリーン
                    </label>
                    <select
                        className="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
                        value={failScreen}
                        onChange={(e) => setFailScreen(e.target.value)}
                    >
                        <option value={""}>なし</option>
                        <option value="_back">戻る</option>
                        {screens.map((screen, idx) => (
                            <option key={idx} value={screen.name}>
                                {screen.name}
                            </option>
                        ))}
                    </select>
                </div>
            </div>}
            <div className="mb-4 flex justify-end">
                <button
                    onClick={handleConfirm}
                    disabled={changeCheck() || !validate()}
                    className="px-4 py-2 text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 disabled:bg-gray-300 disabled:cursor-not-allowed rounded-lg transition-colors"
                >
                    決定
                </button>
            </div>
        </div>
    )
}

interface DeleteDataPropertiesProps {
    component: ButtonComponent
    screens: Screen[]
    tables: Table[]
    updateComponent: (index: number, newComponent: Component) => void,
    setSelectComponentListener: (listener: SelectComponentListenerType) => void
    activateSelectMode: () => void
}

function DeleteDataProperties({
    component,
    screens,
    tables,
    updateComponent,
    setSelectComponentListener,
    activateSelectMode
}: DeleteDataPropertiesProps) {

    const buttonAction = useMemo(() => (component.action?.type ?? "") === "deleteData" ? component.action?.action as ButtonDeleteDataAction : {
        tableName: "",
        deleteData: {},
        failScreenName: "",
        successScreenName: ""
    }, [component])

    const [tableName, setTableName] = useState<string>(buttonAction?.tableName ?? "");
    const [data, setData] = useState<{ [key: string]: string }>(buttonAction?.deleteData ?? {});
    const [selectModeButton, setSelectModeButton] = useState<string>("");
    const [successScreen, setSuccessScreen] = useState<string>(buttonAction?.successScreenName ?? "")
    const [failScreen, setFailScreen] = useState<string>(buttonAction?.failScreenName ?? "")

    const handleTableChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
        setTableName(e.target.value);
        const res: { [key: string]: string } = {}
        for (const col of tables.find((table) => table.name === e.target.value)?.columns ?? []) {
            if (col.isPrimary) {
                res[col.name] = "";
            }
        }
        setData(res);
        setSuccessScreen("");
        setFailScreen("");
    }

    const handleChange = (key: string, value: string) => {
        setData(prev => ({ ...prev, [key]: value }));
    }

    const handleSelectMode = (key: string) => {
        setSelectComponentListener((component) => {
            if (component !== null) {
                setData(prev => ({ ...prev, [key]: "${" + component.name + "}" }));
            }
            setSelectModeButton("");
        })
        setSelectModeButton(key);
        activateSelectMode();
    }

    const validate = () => {
        if (tableName === "") {
            return false;
        }
        for (const key of Object.keys(data)) {
            if (data[key] === "") {
                return false;
            }
        }
        return true;
    }

    const handleConfirm = () => {
        const action: ButtonDeleteDataAction = {
            tableName: tableName,
            deleteData: data,
            successScreenName: successScreen,
            failScreenName: failScreen
        }
        const newComponent: ButtonComponent = { ...component, action: { type: "deleteData", action: action } };
        updateComponent(-1, newComponent)
    }

    const changeCheck = () => {
        if (!buttonAction) {
            return false;
        }
        if (buttonAction.tableName !== tableName) {
            return false;
        }
        for (const key of Object.keys(buttonAction.deleteData)) {
            if (!(key in data)) {
                return false;
            }
            if (data[key] !== buttonAction.deleteData[key]) {
                return false;
            }
        }
        if (buttonAction.successScreenName !== successScreen) {
            return false;
        }
        if (buttonAction.failScreenName !== failScreen) {
            return false;
        }
        return true;
    }

    return (
        <div className="flex-col overflow-y-auto">
            <div className="mb-4">
                <label className="block font-medium text-gray-700 mb-2">
                    テーブル
                </label>
                <select
                    className="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
                    value={tableName}
                    onChange={handleTableChange}
                >
                    <option value={""}>選択してください</option>
                    {tables.map((table, idx) => (
                        <option key={idx} value={table.name}>
                            {table.name}
                        </option>
                    ))}
                </select>
            </div>
            {tableName !== "" && <div className="flex-col">
                {Object.keys(data).map((colName, idx) => (
                    <div key={`col${idx}2`} className="mb-4">
                        <label className="block font-medium text-gray-700 mb-2">
                            {colName}
                        </label>
                        <div className="flex gap-4">
                            <input
                                type="text"
                                value={data[colName] ?? ""}
                                onChange={(e) => handleChange(colName, e.target.value)}
                                className="flex-6 w-full px-3 py-1 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 outline-none transition-colors"
                            />
                            <button
                                onClick={() => handleSelectMode(colName)}
                                className={`flex-1 flex items-center border border-gray-300 justify-center 
                                    rounded-lg hover:bg-gray-300 hover:text-gray-900 
                                    transition ${selectModeButton === colName ? "bg-blue-500 text-gray-100" : "text-gray-600"}`}
                            >
                                <MdEdit />
                            </button>
                        </div>
                    </div>
                ))}
                <div className="mb-4">
                    <label className="block font-medium text-gray-700 mb-2">
                        成功時スクリーン
                    </label>
                    <select
                        className="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
                        value={successScreen}
                        onChange={(e) => setSuccessScreen(e.target.value)}
                    >
                        <option value={""}>なし</option>
                        <option value={"_back"}>戻る</option>
                        {screens.map((screen, idx) => (
                            <option key={idx} value={screen.name}>
                                {screen.name}
                            </option>
                        ))}
                    </select>
                </div>
                <div className="mb-4">
                    <label className="block font-medium text-gray-700 mb-2">
                        失敗時スクリーン
                    </label>
                    <select
                        className="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
                        value={failScreen}
                        onChange={(e) => setFailScreen(e.target.value)}
                    >
                        <option value={""}>なし</option>
                        <option value={"_back"}>戻る</option>
                        {screens.map((screen, idx) => (
                            <option key={idx} value={screen.name}>
                                {screen.name}
                            </option>
                        ))}
                    </select>
                </div>
            </div>}
            <div className="mb-4 flex justify-end">
                <button
                    onClick={handleConfirm}
                    disabled={changeCheck() || !validate()}
                    className="px-4 py-2 text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 disabled:bg-gray-300 disabled:cursor-not-allowed rounded-lg transition-colors"
                >
                    決定
                </button>
            </div>
        </div>
    )
}


interface SearchDataPropertiesProps {
    component: ButtonComponent
    tables: Table[]
    updateComponent: (index: number, newComponent: Component) => void,
    setSelectComponentListener: (listener: SelectComponentListenerType) => void
    activateSelectMode: () => void
}

function SearchDataProperties({
    component,
    tables,
    updateComponent,
    setSelectComponentListener,
    activateSelectMode
}: SearchDataPropertiesProps) {

    const buttonAction = useMemo(() => (component.action?.type ?? "") === "searchData" ? component.action?.action as ButtonSearchDataAction : {
        tableName: "",
        searchData: {},
        targetComponentName: ""
    } as ButtonSearchDataAction, [component])

    const [tableName, setTableName] = useState<string>(buttonAction?.tableName ?? "");
    const [selectableColumns, setSelectableColumns] = useState<string[]>(Object.keys(buttonAction?.searchData ?? {}));
    const [data, setData] = useState<{ [key: string]: string }>(buttonAction?.searchData ?? {});
    const [selectModeButton, setSelectModeButton] = useState<string>("");
    const [targetComponentId, setTargetComponentId] = useState<string>(buttonAction.targetComponentName ?? "");

    const handleTableChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
        setTableName(e.target.value);
        const res = [];
        for (const col of tables.find((table) => table.name === e.target.value)?.columns ?? []) {
            if (!col.isPrimary) {
                if (!col.foreignTableName) {
                    res.push(col.name)
                } else {
                    const table = tables.find((table) => table.name === col.foreignTableName);
                    for (const fcol of col.foreignTableColumns ?? []) {
                        res.push(`${table?.name}.${fcol}`)
                    }
                }
            }
        }
        console.log(res);
        setSelectableColumns(res);
        setData({});
    }

    const handleChange = (key: string, value: string) => {
        setData(prev => ({ ...prev, [key]: value }));
    }

    const handleSelectMode = (key: string) => {
        setSelectComponentListener((component) => {
            if (component !== null) {
                setData(prev => ({ ...prev, [key]: "${" + component.name + "}" }));
            }
            setSelectModeButton("");
        })
        setSelectModeButton(key);
        activateSelectMode();
    }

    const handleTargetSelect = () => {
        setSelectComponentListener((component) => {
            if (component !== null) {
                setTargetComponentId("${" + component.name + "}")
            }
            setSelectModeButton("");
        })
        setSelectModeButton("_____");
        activateSelectMode();
    }

    const handleSelect = (colName: string) => {
        if (colName in data) {
            setData(prev => {
                const res: { [key: string]: string } = {};
                for (const key of Object.keys(prev)) {
                    if (key !== colName) {
                        res[key] = prev[key];
                    }
                }
                return res;
            });
        } else {
            setData(prev => ({ ...prev, [colName]: "" }));
        }
    }

    const validate = () => {
        if (tableName === "") {
            return false;
        }
        if (Object.keys(data).length === 0) {
            return false;
        }
        for (const key of Object.keys(data)) {
            if (data[key] === "") {
                return false;
            }
        }
        if (!targetComponentId.trim()) {
            return false;
        }
        if (targetComponentId === "") {
            return false;
        }
        return true;
    }

    const handleConfirm = () => {
        const action: ButtonSearchDataAction = {
            tableName: tableName,
            searchData: data,
            targetComponentName: targetComponentId
        }
        const newComponent: ButtonComponent = { ...component, action: { type: "searchData", action: action } };
        updateComponent(-1, newComponent)
    }

    const changeCheck = () => {
        if (!buttonAction) {
            return false;
        }
        if (buttonAction.tableName !== tableName) {
            return false;
        }
        for (const key of Object.keys(buttonAction.searchData)) {
            if (!(key in data)) {
                return false;
            }
            if (data[key] !== buttonAction.searchData[key]) {
                return false;
            }
        }
        if (buttonAction.targetComponentName !== targetComponentId) {
            return false;
        }
        return true;
    }

    return (
        <div className="flex-col overflow-y-auto">
            <div className="mb-4">
                <label className="block font-medium text-gray-700 mb-2">
                    テーブル
                </label>
                <select
                    className="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
                    value={tableName}
                    onChange={handleTableChange}
                >
                    <option value={""}>選択してください</option>
                    {tables.map((table, idx) => (
                        <option key={idx} value={table.name}>
                            {table.name}
                        </option>
                    ))}
                </select>
            </div>
            {tableName !== "" && <div className="flex-col">
                {selectableColumns.map((colName, idx) => (
                    <div key={`col${idx}2`} className="mb-4">
                        <label className="block font-medium text-gray-700 mb-2">
                            <input
                                type="checkbox"
                                className="mr-2"
                                checked={colName in data}
                                onChange={() => handleSelect(colName)}
                            />
                            {colName}
                        </label>
                        <div className="flex gap-4">
                            <input
                                type="text"
                                value={data[colName] ?? ""}
                                onChange={(e) => handleChange(colName, e.target.value)}
                                readOnly={!(colName in data)}
                                className="flex-6 w-full px-3 py-1 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 outline-none transition-colors"
                            />
                            <button
                                onClick={() => handleSelectMode(colName)}
                                disabled={!(colName in data)}
                                className={`flex-1 flex items-center border border-gray-300 justify-center 
                                    rounded-lg hover:bg-gray-300 hover:text-gray-900 
                                    transition ${selectModeButton === colName ? "bg-blue-500 text-gray-100" : "text-gray-600"}
                                    ${!(colName in data) && "hover:cursor-not-allowed"}
                                `}
                            >
                                <MdEdit />
                            </button>
                        </div>
                    </div>
                ))}
                <div className="mb-4">
                    <label className="block font-medium text-gray-700 mb-2">
                        宛先テーブル
                    </label>
                    <div className="flex gap-4">
                        <input
                            type="text"
                            value={targetComponentId}
                            onChange={(e) => setTargetComponentId(e.target.value)}
                            className="flex-6 w-full px-3 py-1 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 outline-none transition-colors"
                        />
                        <button
                            onClick={() => handleTargetSelect()}
                            className={`flex-1 flex items-center border border-gray-300 justify-center 
                                rounded-lg hover:bg-gray-300 hover:text-gray-900 
                                transition ${selectModeButton === "_____" ? "bg-blue-500 text-gray-100" : "text-gray-600"}
                            `}
                        >
                            <MdEdit />
                        </button>
                    </div>
                </div>
            </div>}
            <div className="mb-4 flex justify-end">
                <button
                    onClick={handleConfirm}
                    disabled={!validate() || changeCheck()}
                    className="px-4 py-2 text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 disabled:bg-gray-300 disabled:cursor-not-allowed rounded-lg transition-colors"
                >
                    決定
                </button>
            </div>
        </div>
    )
}