kirin-ri / memo

0 stars 0 forks source link

wwww #32

Open kirin-ri opened 1 month ago

kirin-ri commented 1 month ago
// API Card
function ApiCard(
  api: Defs.Api,
  tagList: Defs.Tag[],
  changeStatus: boolean,
  setChangeStatus: any,
  setErrorMsg: any,
  errorMsg: any,
  setDeployJobId: any,
) {
  let selected = false;

  // 检查是否有选中的标签
  const selectedTags = tagList.filter((tag) => tag.select).map((tag) => tag.tag_name);

  // 如果没有选中的标签,默认所有API都被选中显示
  if (selectedTags.length === 0) {
    selected = true;
  } else {
    // 否则根据标签选择状态来决定是否显示灰色
    api.tags.forEach((apiTag: string) => {
      tagList.forEach((item: Defs.Tag) => {
        if (apiTag === item.tag_name && item.select) {
          selected = true;
        }
      });
    });
  }

  // 确保未被选中时,仍可以打开 modal
  if (!errorMsg) {
    $(`#${api.physical_name}`).collapse('hide');
  }

  return (
    <div
      className={`card ${
        selected ? 'card-primary' : 'card-not-applicable not-applicable'
      } card-collapse-sample`}
    >
      <div
        className="card-header collapsed"
        data-toggle="collapse"
        data-target={`#card-body-${api.physical_name}`}
      >
        <div
          className="api-name-font"
          style={{
            fontSize: `clamp(0.6rem, ${
              29 / `${api.physical_name} / ${api.logical_name}`.length
            }vw, 1rem)`,
          }}
        >
          <i
            className={`fa fa-circle provide-icon-size ${
              selected
                ? api.provide
                  ? 'provide-icon-color'
                  : 'no-provide-icon-color'
                : ''
            }`}
            aria-hidden="true"
          />
          {Spacer({ size: 10, horizontal: true })}
          {`${api.physical_name} / ${api.logical_name}`}
        </div>
      </div>
      <div className="card-body collapse" id={`card-body-${api.physical_name}`}>
        <div className="api-list-title">
          <p>データセット 連携項目 一覧</p>
          {Spacer({ size: 20 })}
        </div>
        {api.outinfo.map((val: Defs.TableInfo) => {
          return val.column.map((item: Defs.Column) => (
            <div className="list-contents-grid" key={item.physicalName}>
              <p>{item.logicalName}</p>
            </div>
          ));
        })}
        {Spacer({ size: 20 })}
        <div className="text-danger error-msg">
          {errorMsg[api.physical_name]}
        </div>
        <br />
        {api.provide ? (
          <>
            <div className="btn-center">
              <button
                className="btn btn-secondary btn-secondary"
                data-toggle="modal"
                data-target={`#${api.physical_name}-dtl-modal`} // 确保 modal 的 target id 绑定正确
                data-backdrop="true"
              >
                サンプルデータ挿入 / データセット連携項目追加
              </button>
            </div>
          </>
        ) : (
          <div className="btn-center">
            <button
              className="btn btn-secondary btn-secondary"
              onClick={() => {
                putDeploy(
                  api,
                  changeStatus,
                  setChangeStatus,
                  setErrorMsg,
                  setDeployJobId,
                );
              }}
            >
              有効化
            </button>
          </div>
        )}
      </div>
    </div>
  );
}
kirin-ri commented 1 month ago
import React, { useEffect, useState, useLayoutEffect } from 'react';
import { commonAjax } from '../../../components/commonAjax';
import { Spacer } from '../spacer';
import * as Defs from './apiDefs';
import ApiUpdateResultModal from './apiUpdateResultModal';
import CreateNewApi from './createNewApi';
import EditApiList from './editApiList';
import NewAPIDtlModal from './newAPIDtlModal';
import NewSampleDataModal from './newSampleDataModal';
import SelectDeployApi from './selectDeployApi';
import { DialogModalOK, hideDialog, showDialog } from './dialogModal';
import { Link } from 'react-router-dom';

// main
function NewApiList() {
  // useState
  const [apiList, setApiList] = useState<Defs.Api[]>([]);
  const [tagList, setTags] = useState<Defs.Tag[]>([]);
  const [changeStatus, setChangeStatus] = useState<boolean>(false);
  const [enableDplApi, setEnableDplApi] = useState<boolean>(false);
  const [errorMsg, setErrorMsg] = useState<{}>({});
  const [deployJobId, setDeployJobId] = useState<string>('');
  const [isModalOpen, setIsModalOpen] = useState(false);
  const [expandedApis, setExpandedApis] = useState<Set<string>>(new Set()); // 记录展开状态

  /* API情報取得のリクエスト */
  useEffect(() => {
    (async () => {
      commonAjax
        .axios({ swalFire: false, loading: true })
        .get('/api/api')
        .then((res) => {
          const data = res.data.api;
          const resTags: React.SetStateAction<Defs.Tag[]> = [];
          let tmpTags: string[] = [];
          data.map((item: any) => {
            tmpTags = tmpTags.concat(item.tags);
          });
          tmpTags = Array.from(new Set(tmpTags));
          tmpTags.map((t: string) => {
            resTags.push({ tag_name: t, select: false }); // 初期设为 false
          });
          setApiList(data);
          setTags(resTags);
          setEnableDplApi(res.data.enableDplApi);
        });
    })();
  }, [changeStatus]);

  useLayoutEffect(() => {
    if (deployJobId) {
      // ジョブ開始ダイアログ表示
      showDialog('api-deploy-job-start-notification');
    }
  }, [deployJobId]);

  // 标签切换时重置展开状态
  const handleTagChange = (updatedTags: Defs.Tag[]) => {
    setTags(updatedTags);
    setExpandedApis(new Set()); // 重置展开的API状态,收起所有
  };

  const metricsProcess = process.env.REACT_APP_METRICS_PROCESS;
  const title = `${metricsProcess} データセット一覧`;

  return (
    <div className="content-wrapper api-list">
      <section className="page-cover">
        <h1>{title}</h1>
      </section>
      {/* Content Header (Page header) */}
      <section className="content-header">
        <div className="content-header-left">
          <h1>{title}</h1>
          <div className="content-header-desc">
            現在、設定されているデータセット一覧です
          </div>
        </div>
        {enableDplApi && (
          <div className="content-header-right">
            <button
              type="button"
              className="btn btn-secondary"
              data-toggle="modal"
              data-target="#editApiListModal"
              onClick={() => {
                setIsModalOpen(true);
              }}
            >
              編集
            </button>
            <button
              type="button"
              className="btn btn-primary"
              data-toggle="modal"
              data-target="#api-dtl-modal"
              onClick={() => {
                setIsModalOpen(true);
              }}
            >
              追加
            </button>
            <button
              type="button"
              className="btn-long-text btn-primary"
              data-toggle="modal"
              data-target="#selectDeployApiModal"
              onClick={() => {
                setIsModalOpen(true);
              }}
            >
              一括有効化
            </button>
          </div>
        )}
      </section>
      <section className="content">
        {/* filter tag */}
        <div className="inline-form">
          <div className="inline-form-cat">フィルタータグ</div>
          {FilterTags(tagList, handleTagChange)}
        </div>
        {Spacer({ size: 30 })}
        {/* API List (Grid) */}
        {viewList(
          apiList,
          tagList,
          changeStatus,
          setChangeStatus,
          setErrorMsg,
          errorMsg,
          setDeployJobId,
          expandedApis,
          setExpandedApis // 传递展开状态控制函数
        )}
      </section>
      {isModalOpen && (
        <>
          <CreateNewApi
            changeStatus={changeStatus}
            setChangeStatus={setChangeStatus}
          />
          <EditApiList
            val={apiList}
            changeStatus={changeStatus}
            setChangeStatus={setChangeStatus}
          />
          <SelectDeployApi
            val={apiList}
            changeStatus={changeStatus}
            setChangeStatus={setChangeStatus}
          />
        </>
      )}
      <ApiDeployJobStartNotification jobId={deployJobId} />
    </div>
  );
}

// filter tags 逻辑修改
function FilterTags(tags: Defs.Tag[], handleTagChange: (updatedTags: Defs.Tag[]) => void) {
  return (
    <div className="inline-form-group">
      <div className="inline-form-label">分類</div>
      {tags.map((item: Defs.Tag) => (
        <div
          className={`btn btn-tag ${item.select ? 'active' : ''}`}
          key={item.tag_name}
          onClick={() => {
            item.select = !item.select; // 切换选择状态
            const t = tags.slice(0, tags.length);
            handleTagChange(t); // 标签切换时重置展开状态
          }}
        >
          {item.tag_name}
        </div>
      ))}
    </div>
  );
}

// 过滤显示逻辑修改
function viewList(
  apiList: Defs.Api[],
  tagList: Defs.Tag[],
  changeStatus: boolean,
  setChangeStatus: any,
  setErrorMsg: any,
  errorMsg: any,
  setDeployJobId: any,
  expandedApis: Set<string>, // 展开的API状态
  setExpandedApis: React.Dispatch<React.SetStateAction<Set<string>>>, // 控制展开状态的函数
) {
  let filteredList = apiList;

  // 检查是否有选中的标签
  const selectedTags = tagList.filter((tag) => tag.select).map((tag) => tag.tag_name);

  // 如果有选中的标签,按标签过滤API列表
  if (selectedTags.length > 0) {
    filteredList = apiList.filter((api) =>
      api.tags.some((tag) => selectedTags.includes(tag)),
    );
  }

  const length = Math.ceil(filteredList.length / 3);
  let listItem: any = [];
  if (length != 0) {
    listItem = transpose(
      new Array(length).fill(0).map((_, i) => filteredList.slice(i * 3, (i + 1) * 3)),
    );
  }

  return (
    <div className="list-wrapper">
      <div className="apiList">
        {0 >= 0 && listItem.length > 0
          ? listItem[0].map((val: Defs.Api) =>
              ApiCard(
                val,
                tagList,
                changeStatus,
                setChangeStatus,
                setErrorMsg,
                errorMsg,
                setDeployJobId,
                expandedApis,
                setExpandedApis // 传递展开状态控制函数
              ),
            )
          : null}
      </div>
      <div className="apiList">
        {1 >= 0 && listItem.length > 1
          ? listItem[1].map((val: Defs.Api) =>
              ApiCard(
                val,
                tagList,
                changeStatus,
                setChangeStatus,
                setErrorMsg,
                errorMsg,
                setDeployJobId,
                expandedApis,
                setExpandedApis // 传递展开状态控制函数
              ),
            )
          : null}
      </div>
      <div className="apiList">
        {2 >= 0 && listItem.length > 2
          ? listItem[2].map((val: Defs.Api) =>
              ApiCard(
                val,
                tagList,
                changeStatus,
                setChangeStatus,
                setErrorMsg,
                errorMsg,
                setDeployJobId,
                expandedApis,
                setExpandedApis // 传递展开状态控制函数
              ),
            )
          : null}
      </div>
    </div>
  );
}

// API Card
function ApiCard(
  api: Defs.Api,
  tagList: Defs.Tag[],
  changeStatus: boolean,
  setChangeStatus: any,
  setErrorMsg: any,
  errorMsg: any,
  setDeployJobId: any,
  expandedApis: Set<string>, // 传入已展开的API
  setExpandedApis: React.Dispatch<React.SetStateAction<Set<string>>>, // 控制展开状态的函数
) {
  let selected = false;

  // 检查是否有选中的标签
  const selectedTags = tagList.filter((tag) => tag.select).map((tag) => tag.tag_name);

  // 如果没有选中的标签,默认所有API都被选中显示
  if (selectedTags.length === 0) {
    selected = true;
  } else {
    // 否则根据标签选择状态来决定是否显示灰色
    api.tags.forEach((apiTag: string) => {
      tagList.forEach((item: Defs.Tag) => {
        if (apiTag === item.tag_name && item.select) {
          selected = true;
        }
      });
    });
  }

  // 切换展开状态
  const toggleExpand = () => {
    const newExpandedApis = new Set(expandedApis);
    if (newExpandedApis.has(api.physical_name)) {
      newExpandedApis.delete(api.physical_name);
    } else {
      newExpandedApis.add(api.physical_name);
    }
    setExpandedApis(newExpandedApis);
  };

  const isExpanded = expandedApis.has(api.physical_name); // 当前API是否已展开

  return (
    <div
      className={`card ${
        selected ? 'card-primary' : 'card-not-applicable not-applicable'
      } card-collapse-sample`}
    >
      <div
        className={`card-header ${isExpanded ? '' : 'collapsed'}`}
        data-toggle="collapse"
        data-target={`#card-body-${api.physical_name}`}
        onClick={toggleExpand} // 点击时切换展开状态
      >
        <div
          className="api-name-font"
          style={{
            fontSize: `clamp(0.6rem, ${
              29 / `${api.physical_name} / ${api.logical_name}`.length
            }vw, 1rem)`,
          }}
        >
          <i
            className={`fa fa-circle provide-icon-size ${
              selected
                ? api.provide
                  ? 'provide-icon-color'
                  : 'no-provide-icon-color'
                : ''
            }`}
            aria-hidden="true"
          />
          {Spacer({ size: 10, horizontal: true })}
          {`${api.physical_name} / ${api.logical_name}`}
        </div>
      </div>
      <div className={`card-body collapse ${isExpanded ? 'show' : ''}`} id={`card-body-${api.physical_name}`}>
        <div className="api-list-title">
          <p>データセット 連携項目 一覧</p>
          {Spacer({ size: 20 })}
        </div>
        {api.outinfo.map((val: Defs.TableInfo) => {
          return val.column.map((item: Defs.Column) => (
            <div className="list-contents-grid" key={item.physicalName}>
              <p>{item.logicalName}</p>
            </div>
          ));
        })}
        {Spacer({ size: 20 })}
        <div className="text-danger error-msg">
          {errorMsg[api.physical_name]}
        </div>
        <br />
        {api.provide ? (
          <>
            <div className="btn-center">
              <button
                className="btn btn-secondary btn-secondary"
                data-toggle="modal"
                data-target={`#${api.physical_name}-dtl-modal`} // 确保 modal 的 target id 绑定正确
                data-backdrop="true"
              >
                サンプルデータ挿入 / データセット連携項目追加
              </button>
            </div>
          </>
        ) : (
          <div className="btn-center">
            <button
              className="btn btn-secondary btn-secondary"
              onClick={() => {
                putDeploy(
                  api,
                  changeStatus,
                  setChangeStatus,
                  setErrorMsg,
                  setDeployJobId,
                );
              }}
            >
              有効化
            </button>
          </div>
        )}
      </div>
    </div>
  );
}
kirin-ri commented 1 month ago
// main
function NewApiList() {
  // useState
  const [apiList, setApiList] = useState<Defs.Api[]>([]);
  const [tagList, setTags] = useState<Defs.Tag[]>([]);
  const [changeStatus, setChangeStatus] = useState<boolean>(false);
  const [enableDplApi, setEnableDplApi] = useState<boolean>(false);
  const [errorMsg, setErrorMsg] = useState<{}>({});
  const [deployJobId, setDeployJobId] = useState<string>('');
  const [isModalOpen, setIsModalOpen] = useState(false);
  const [expandedApis, setExpandedApis] = useState<Set<string>>(new Set()); // 记录展开状态

  /* API情報取得のリクエスト */
  useEffect(() => {
    (async () => {
      commonAjax
        .axios({ swalFire: false, loading: true })
        .get('/api/api')
        .then((res) => {
          const data = res.data.api;
          const resTags: React.SetStateAction<Defs.Tag[]> = [];
          let tmpTags: string[] = [];
          data.map((item: any) => {
            tmpTags = tmpTags.concat(item.tags);
          });
          tmpTags = Array.from(new Set(tmpTags));
          tmpTags.map((t: string) => {
            resTags.push({ tag_name: t, select: false }); // 初期设为 false
          });
          setApiList(data);
          setTags(resTags);
          setEnableDplApi(res.data.enableDplApi);
        });
    })();
  }, [changeStatus]);

  useLayoutEffect(() => {
    if (deployJobId) {
      // ジョブ開始ダイアログ表示
      showDialog('api-deploy-job-start-notification');
    }
  }, [deployJobId]);

  // 标签切换时重置展开状态,确保所有卡片收起
  const handleTagChange = (updatedTags: Defs.Tag[]) => {
    setTags(updatedTags);
    setExpandedApis(new Set()); // 重置展开的API状态,收起所有
  };

  const metricsProcess = process.env.REACT_APP_METRICS_PROCESS;
  const title = `${metricsProcess} データセット一覧`;

  return (
    <div className="content-wrapper api-list">
      <section className="page-cover">
        <h1>{title}</h1>
      </section>
      {/* Content Header (Page header) */}
      <section className="content-header">
        <div className="content-header-left">
          <h1>{title}</h1>
          <div className="content-header-desc">
            現在、設定されているデータセット一覧です
          </div>
        </div>
        {enableDplApi && (
          <div className="content-header-right">
            <button
              type="button"
              className="btn btn-secondary"
              data-toggle="modal"
              data-target="#editApiListModal"
              onClick={() => {
                setIsModalOpen(true);
              }}
            >
              編集
            </button>
            <button
              type="button"
              className="btn btn-primary"
              data-toggle="modal"
              data-target="#api-dtl-modal"
              onClick={() => {
                setIsModalOpen(true);
              }}
            >
              追加
            </button>
            <button
              type="button"
              className="btn-long-text btn-primary"
              data-toggle="modal"
              data-target="#selectDeployApiModal"
              onClick={() => {
                setIsModalOpen(true);
              }}
            >
              一括有効化
            </button>
          </div>
        )}
      </section>
      <section className="content">
        {/* filter tag */}
        <div className="inline-form">
          <div className="inline-form-cat">フィルタータグ</div>
          {FilterTags(tagList, handleTagChange)}
        </div>
        {Spacer({ size: 30 })}
        {/* API List (Grid) */}
        {viewList(
          apiList,
          tagList,
          changeStatus,
          setChangeStatus,
          setErrorMsg,
          errorMsg,
          setDeployJobId,
          expandedApis,
          setExpandedApis // 传递展开状态控制函数
        )}
      </section>
      {isModalOpen && (
        <>
          <CreateNewApi
            changeStatus={changeStatus}
            setChangeStatus={setChangeStatus}
          />
          <EditApiList
            val={apiList}
            changeStatus={changeStatus}
            setChangeStatus={setChangeStatus}
          />
          <SelectDeployApi
            val={apiList}
            changeStatus={changeStatus}
            setChangeStatus={setChangeStatus}
          />
        </>
      )}
      <ApiDeployJobStartNotification jobId={deployJobId} />
    </div>
  );
}

// filter tags 逻辑修改
function FilterTags(tags: Defs.Tag[], handleTagChange: (updatedTags: Defs.Tag[]) => void) {
  return (
    <div className="inline-form-group">
      <div className="inline-form-label">分類</div>
      {tags.map((item: Defs.Tag) => (
        <div
          className={`btn btn-tag ${item.select ? 'active' : ''}`}
          key={item.tag_name}
          onClick={() => {
            item.select = !item.select; // 切换选择状态
            const t = tags.slice(0, tags.length);
            handleTagChange(t); // 标签切换时重置展开状态
          }}
        >
          {item.tag_name}
        </div>
      ))}
    </div>
  );
}

// 过滤显示逻辑修改
function viewList(
  apiList: Defs.Api[],
  tagList: Defs.Tag[],
  changeStatus: boolean,
  setChangeStatus: any,
  setErrorMsg: any,
  errorMsg: any,
  setDeployJobId: any,
  expandedApis: Set<string>, // 展开的API状态
  setExpandedApis: React.Dispatch<React.SetStateAction<Set<string>>>, // 控制展开状态的函数
) {
  let filteredList = apiList;

  // 检查是否有选中的标签
  const selectedTags = tagList.filter((tag) => tag.select).map((tag) => tag.tag_name);

  // 如果有选中的标签,按标签过滤API列表
  if (selectedTags.length > 0) {
    filteredList = apiList.filter((api) =>
      api.tags.some((tag) => selectedTags.includes(tag)),
    );
  }

  const length = Math.ceil(filteredList.length / 3);
  let listItem: any = [];
  if (length != 0) {
    listItem = transpose(
      new Array(length).fill(0).map((_, i) => filteredList.slice(i * 3, (i + 1) * 3)),
    );
  }

  return (
    <div className="list-wrapper">
      <div className="apiList">
        {0 >= 0 && listItem.length > 0
          ? listItem[0].map((val: Defs.Api) =>
              ApiCard(
                val,
                tagList,
                changeStatus,
                setChangeStatus,
                setErrorMsg,
                errorMsg,
                setDeployJobId,
                expandedApis,
                setExpandedApis // 传递展开状态控制函数
              ),
            )
          : null}
      </div>
      <div className="apiList">
        {1 >= 0 && listItem.length > 1
          ? listItem[1].map((val: Defs.Api) =>
              ApiCard(
                val,
                tagList,
                changeStatus,
                setChangeStatus,
                setErrorMsg,
                errorMsg,
                setDeployJobId,
                expandedApis,
                setExpandedApis // 传递展开状态控制函数
              ),
            )
          : null}
      </div>
      <div className="apiList">
        {2 >= 0 && listItem.length > 2
          ? listItem[2].map((val: Defs.Api) =>
              ApiCard(
                val,
                tagList,
                changeStatus,
                setChangeStatus,
                setErrorMsg,
                errorMsg,
                setDeployJobId,
                expandedApis,
                setExpandedApis // 传递展开状态控制函数
              ),
            )
          : null}
      </div>
    </div>
  );
}

// API Card
function ApiCard(
  api: Defs.Api,
  tagList: Defs.Tag[],
  changeStatus: boolean,
  setChangeStatus: any,
  setErrorMsg: any,
  errorMsg: any,
  setDeployJobId: any,
  expandedApis: Set<string>, // 传入已展开的API
  setExpandedApis: React.Dispatch<React.SetStateAction<Set<string>>>, // 控制展开状态的函数
) {
  let selected = false;

  // 检查是否有选中的标签
  const selectedTags = tagList.filter((tag) => tag.select).map((tag) => tag.tag_name);

  // 如果没有选中的标签,默认所有API都被选中显示
  if (selectedTags.length === 0) {
    selected = true;
  } else {
    // 否则根据标签选择状态来决定是否显示灰色
    api.tags.forEach((apiTag: string) => {
      tagList.forEach((item: Defs.Tag) => {
        if (apiTag === item.tag_name && item.select) {
          selected = true;
        }
      });
    });
  }

  // 切换展开状态
  const toggleExpand = () => {
    const newExpandedApis = new Set(expandedApis);
    if (newExpandedApis.has(api.physical_name)) {
      newExpandedApis.delete(api.physical_name);
    } else {
      newExpandedApis.add(api.physical_name);
    }
    setExpandedApis(newExpandedApis);
  };

  const isExpanded = expandedApis.has(api.physical_name); // 当前API是否已展开

  return (
    <div
      className={`card ${
        selected ? 'card-primary' : 'card-not-applicable not-applicable'
      } card-collapse-sample`}
    >
      <div
        className={`card-header ${isExpanded ? '' : 'collapsed'}`}
        data-toggle="collapse"
        data-target={`#card-body-${api.physical_name}`}
        onClick={toggleExpand} // 点击时切换展开状态
      >
        <div
          className="api-name-font"
          style={{
            fontSize: `clamp(0.6rem, ${
              29 / `${api.physical_name} / ${api.logical_name}`.length
            }vw, 1rem)`,
          }}
        >
          <i
            className={`fa fa-circle provide-icon-size ${
              selected
                ? api.provide
                  ? 'provide-icon-color'
                  : 'no-provide-icon-color'
                : ''
            }`}
            aria-hidden="true"
          />
          {Spacer({ size: 10, horizontal: true })}
          {`${api.physical_name} / ${api.logical_name}`}
        </div>
      </div>
      <div className={`card-body collapse ${isExpanded ? 'show' : ''}`} id={`card-body-${api.physical_name}`}>
        <div className="api-list-title">
          <p>データセット 連携項目 一覧</p>
          {Spacer({ size: 20 })}
        </div>
        {api.outinfo.map((val: Defs.TableInfo) => {
          return val.column.map((item: Defs.Column) => (
            <div className="list-contents-grid" key={item.physicalName}>
              <p>{item.logicalName}</p>
            </div>
          ));
        })}
        {Spacer({ size: 20 })}
        <div className="text-danger error-msg">
          {errorMsg[api.physical_name]}
        </div>
        <br />
        {api.provide ? (
          <>
            <div className="btn-center">
              <button
                className="btn btn-secondary btn-secondary"
                data-toggle="modal"
                data-target={`#${api.physical_name}-dtl-modal`} // 确保 modal 的 target id 绑定正确
                data-backdrop="true"
              >
                サンプルデータ挿入 / データセット連携項目追加
              </button>
            </div>
          </>
        ) : (
          <div className="btn-center">
            <button
              className="btn btn-secondary btn-secondary"
              onClick={() => {
                putDeploy(
                  api,
                  changeStatus,
                  setChangeStatus,
                  setErrorMsg,
                  setDeployJobId,
                );
              }}
            >
              有効化
            </button>
          </div>
        )}
      </div>
    </div>
  );
}
kirin-ri commented 1 month ago
import React, { useEffect, useLayoutEffect, useState } from 'react';
import { Link } from 'react-router-dom';
import { commonAjax } from '../../../components/commonAjax';
import { Spacer } from '../spacer';
import * as Defs from './apiDefs';
import ApiUpdateResultModal from './apiUpdateResultModal';
import CreateNewApi from './createNewApi';
import { DialogModalOK, hideDialog, showDialog } from './dialogModal';
import EditApiList from './editApiList';
import NewAPIDtlModal from './newAPIDtlModal';
import NewSampleDataModal from './newSampleDataModal';
import SelectDeployApi from './selectDeployApi';

function NewApiList() {
  // useState
  const [apiList, setApiList] = useState<Defs.Api[]>([]);
  const [tagList, setTags] = useState<Defs.Tag[]>([]);
  const [changeStatus, setChangeStatus] = useState<boolean>(false);
  const [enableDplApi, setEnableDplApi] = useState<boolean>(false);
  const [errorMsg, setErrorMsg] = useState<{}>({});
  const [deployJobId, setDeployJobId] = useState<string>('');
  const [isModalOpen, setIsModalOpen] = useState(false);

  /* API情報取得のリクエスト */
  useEffect(() => {
    (async () => {
      commonAjax
        .axios({ swalFire: false, loading: true })
        .get('/api/api')
        .then((res) => {
          const data = res.data.api;
          const resTags: React.SetStateAction<Defs.Tag[]> = [];
          let tmpTags: string[] = [];
          data.map((item: any) => {
            tmpTags = tmpTags.concat(item.tags);
          });
          tmpTags = Array.from(new Set(tmpTags));
          tmpTags.map((t: string) => {
            resTags.push({ tag_name: t, select: false }); // 初期设为 false
          });
          setApiList(data);
          setTags(resTags);
          setEnableDplApi(res.data.enableDplApi);
        });
    })();
  }, [changeStatus]);

  useLayoutEffect(() => {
    if (deployJobId) {
      // ジョブ開始ダイアログ表示
      showDialog('api-deploy-job-start-notification');
    }
  }, [deployJobId]);

  const metricsProcess = process.env.REACT_APP_METRICS_PROCESS;
  const title = `${metricsProcess} データセット一覧`;

  return (
    <div className="content-wrapper api-list">
      <section className="page-cover">
        <h1>{title}</h1>
      </section>
      {/* Content Header (Page header) */}
      <section className="content-header">
        <div className="content-header-left">
          <h1>{title}</h1>
          <div className="content-header-desc">
            現在、設定されているデータセット一覧です
          </div>
        </div>
        {enableDplApi && (
          <div className="content-header-right">
            <button
              type="button"
              className="btn btn-secondary"
              data-toggle="modal"
              data-target="#editApiListModal"
              onClick={() => {
                setIsModalOpen(true);
              }}
            >
              編集
            </button>
            <button
              type="button"
              className="btn btn-primary"
              data-toggle="modal"
              data-target="#api-dtl-modal"
              onClick={() => {
                setIsModalOpen(true);
              }}
            >
              追加
            </button>
            <button
              type="button"
              className="btn-long-text btn-primary"
              data-toggle="modal"
              data-target="#selectDeployApiModal"
              onClick={() => {
                setIsModalOpen(true);
              }}
            >
              一括有効化
            </button>
          </div>
        )}
      </section>
      <section className="content">
        {/* filter tag */}
        <div className="inline-form">
          <div className="inline-form-cat">フィルタータグ</div>
          {FilterTags(tagList, setTags)}
        </div>
        {Spacer({ size: 30 })}
        {/* API List (Grid) */}
        {viewList(
          apiList,
          tagList,
          changeStatus,
          setChangeStatus,
          setErrorMsg,
          errorMsg,
          setDeployJobId,
        )}
      </section>
      {isModalOpen && (
        <>
          <CreateNewApi
            changeStatus={changeStatus}
            setChangeStatus={setChangeStatus}
          />
          <EditApiList
            val={apiList}
            changeStatus={changeStatus}
            setChangeStatus={setChangeStatus}
          />
          <SelectDeployApi
            val={apiList}
            changeStatus={changeStatus}
            setChangeStatus={setChangeStatus}
          />
        </>
      )}
      <ApiDeployJobStartNotification jobId={deployJobId} />
    </div>
  );
}

// filter tags 逻辑修改
function FilterTags(tags: Defs.Tag[], setTags: any) {
  return (
    <div className="inline-form-group">
      <div className="inline-form-label">分類</div>
      {tags.map((item: Defs.Tag) => (
        <div
          className={`btn btn-tag ${item.select ? 'active' : ''}`}
          key={item.tag_name}
          onClick={() => {
            item.select = !item.select; // 切换选择状态
            const t = tags.slice(0, tags.length);
            setTags(t);
          }}
        >
          {item.tag_name}
        </div>
      ))}
    </div>
  );
}

// 过滤显示逻辑修改
function viewList(
  apiList: Defs.Api[],
  tagList: Defs.Tag[],
  changeStatus: boolean,
  setChangeStatus: any,
  setErrorMsg: any,
  errorMsg: any,
  setDeployJobId: any,
) {
  let filteredList = apiList;

  // 检查是否有选中的标签
  const selectedTags = tagList.filter((tag) => tag.select).map((tag) => tag.tag_name);

  // 如果有选中的标签,按标签过滤API列表
  if (selectedTags.length > 0) {
    filteredList = apiList.filter((api) =>
      api.tags.some((tag) => selectedTags.includes(tag)),
    );
  }

  // 渲染所有 Modal 保证初始化
  const modals = apiList.map((val) => (
    <>
      <NewAPIDtlModal val={val} />
      <NewSampleDataModal
        id={val.physical_name}
        logicalName={val.logical_name}
      />
      <ApiUpdateResultModal val={val} />
    </>
  ));

  const length = Math.ceil(filteredList.length / 3);
  let listItem: any = [];
  if (length != 0) {
    listItem = transpose(
      new Array(length).fill(0).map((_, i) => filteredList.slice(i * 3, (i + 1) * 3)),
    );
  }

  return (
    <div className="list-wrapper">
      <div className="apiList">
        {0 >= 0 && listItem.length > 0
          ? listItem[0].map((val: Defs.Api) =>
              ApiCard(
                val,
                tagList,
                changeStatus,
                setChangeStatus,
                setErrorMsg,
                errorMsg,
                setDeployJobId,
              ),
            )
          : null}
      </div>
      <div className="apiList">
        {1 >= 0 && listItem.length > 1
          ? listItem[1].map((val: Defs.Api) =>
              ApiCard(
                val,
                tagList,
                changeStatus,
                setChangeStatus,
                setErrorMsg,
                errorMsg,
                setDeployJobId,
              ),
            )
          : null}
      </div>
      <div className="apiList">
        {2 >= 0 && listItem.length > 2
          ? listItem[2].map((val: Defs.Api) =>
              ApiCard(
                val,
                tagList,
                changeStatus,
                setChangeStatus,
                setErrorMsg,
                errorMsg,
                setDeployJobId,
              ),
            )
          : null}
      </div>
      {/* 渲染所有 Modal */}
      {modals}
    </div>
  );
}

// API Card
// API Card
function ApiCard(
  api: Defs.Api,
  tagList: Defs.Tag[],
  changeStatus: boolean,
  setChangeStatus: any,
  setErrorMsg: any,
  errorMsg: any,
  setDeployJobId: any,
) {
  let selected = false;

  // 检查是否有选中的标签
  const selectedTags = tagList.filter((tag) => tag.select).map((tag) => tag.tag_name);

  // 如果没有选中的标签,默认所有API都被选中显示
  if (selectedTags.length === 0) {
    selected = true;
  } else {
    // 否则根据标签选择状态来决定是否显示灰色
    api.tags.forEach((apiTag: string) => {
      tagList.forEach((item: Defs.Tag) => {
        if (apiTag === item.tag_name && item.select) {
          selected = true;
        }
      });
    });
  }

  // 确保未被选中时,仍可以打开 modal
  if (!errorMsg) {
    $(`#${api.physical_name}`).collapse('hide');
  }

  return (
    <div
      className={`card ${
        selected ? 'card-primary' : 'card-not-applicable not-applicable'
      } card-collapse-sample`}
    >
      <div
        className="card-header collapsed"
        data-toggle="collapse"
        data-target={`#card-body-${api.physical_name}`}
      >
        <div
          className="api-name-font"
          style={{
            fontSize: `clamp(0.6rem, ${
              29 / `${api.physical_name} / ${api.logical_name}`.length
            }vw, 1rem)`,
          }}
        >
          <i
            className={`fa fa-circle provide-icon-size ${
              selected
                ? api.provide
                  ? 'provide-icon-color'
                  : 'no-provide-icon-color'
                : ''
            }`}
            aria-hidden="true"
          />
          {Spacer({ size: 10, horizontal: true })}
          {`${api.physical_name} / ${api.logical_name}`}
        </div>
      </div>
      <div className="card-body collapse" id={`card-body-${api.physical_name}`}>
        <div className="api-list-title">
          <p>データセット 連携項目 一覧</p>
          {Spacer({ size: 20 })}
        </div>
        {api.outinfo.map((val: Defs.TableInfo) => {
          return val.column.map((item: Defs.Column) => (
            <div className="list-contents-grid" key={item.physicalName}>
              <p>{item.logicalName}</p>
            </div>
          ));
        })}
        {Spacer({ size: 20 })}
        <div className="text-danger error-msg">
          {errorMsg[api.physical_name]}
        </div>
        <br />
        {api.provide ? (
          <>
            <div className="btn-center">
              <button
                className="btn btn-secondary btn-secondary"
                data-toggle="modal"
                data-target={`#${api.physical_name}-dtl-modal`} // 确保 modal 的 target id 绑定正确
                data-backdrop="true"
              >
                サンプルデータ挿入 / データセット連携項目追加
              </button>
            </div>
          </>
        ) : (
          <div className="btn-center">
            <button
              className="btn btn-secondary btn-secondary"
              onClick={() => {
                putDeploy(
                  api,
                  changeStatus,
                  setChangeStatus,
                  setErrorMsg,
                  setDeployJobId,
                );
              }}
            >
              有効化
            </button>
          </div>
        )}
      </div>
    </div>
  );
}

// transpose columns and rows
const transpose = (a: any[]) =>
  a[0].map((_: any, c: string | number) => a.map((r) => r[c]).filter(Boolean));

// put api(deploy)
function putDeploy(
  api: Defs.Api,
  changeStatus: boolean,
  setChangeStatus: any,
  setErrorMsg: any,
  setDeployJobId: any,
) {
  if (api) {
    if (!api.provide) {
      (async () => {
        const errorMsgList: { [key: string]: string } = {
          [api.physical_name]: '',
        };
        commonAjax
          .axios({ swalFire: false, loading: true })
          .put('/api/api/deploy', { id: [api.physical_name] })
          .then((res) => {
            if (res.data.errorMsg != null) {
              const { errorMsg } = res.data;
              console.log({ errorMsg });
              errorMsgList[api.physical_name] = errorMsg;
              setErrorMsg(errorMsgList);
            } else {
              setChangeStatus(!changeStatus);
              setErrorMsg(errorMsgList);
              setDeployJobId(res.data.jobId);
            }
          });
      })();
    }
  }
}

function ApiDeployJobStartNotification({ jobId }: { jobId?: string }) {
  return (
    <DialogModalOK
      id="api-deploy-job-start-notification"
      title="処理を開始しました"
    >
      <p>
        データセット有効化処理を開始しました。
        <br />
        完了には時間がかかりますのでお待ちください。
        <br />
        処理の状況は下記画面で確認できます。
      </p>
      <Link
        to={`/jobs/${jobId}`}
        className="btn btn-secondary"
        target="_blank"
        onClick={() => hideDialog('api-deploy-job-start-notification')}
      >
        ジョブステータス確認
      </Link>
    </DialogModalOK>
  );
}

export default NewApiList;
kirin-ri commented 1 month ago
  const collapseAllCards = () => {
    apiList.forEach((api) => {
      // 使用 jQuery 将所有展开的 card 收起
      $(`#card-body-${api.physical_name}`).collapse('hide');
    });
  };

  return (
    <div className="inline-form-group">
      <div className="inline-form-label">分類</div>
      {tags.map((item: Defs.Tag) => (
        <div
          className={`btn btn-tag ${item.select ? 'active' : ''}`}
          key={item.tag_name}
          onClick={() => {
            item.select = !item.select; // 切换选择状态
            const t = tags.slice(0, tags.length);
            setTags(t);
            collapseAllCards(); // 标签变化时收起所有 API card
          }}
        >
          {item.tag_name}
        </div>
      ))}
    </div>
  );
}
kirin-ri commented 1 month ago
ERROR in src/app2/components/pages/apiList.tsx:153:5
TS2304: Cannot find name 'apiList'.
    151 | function FilterTags(tags: Defs.Tag[], setTags: any) {
    152 |   const collapseAllCards = () => {
  > 153 |     apiList.forEach((api) => {
        |     ^^^^^^^
    154 |       // 使用 jQuery 将所有展开的 card 收起
    155 |       $(`#card-body-${api.physical_name}`).collapse('hide');
    156 |     });

ERROR in src/app2/components/pages/apiList.tsx:153:22
TS7006: Parameter 'api' implicitly has an 'any' type.
    151 | function FilterTags(tags: Defs.Tag[], setTags: any) {
    152 |   const collapseAllCards = () => {
  > 153 |     apiList.forEach((api) => {
        |                      ^^^
    154 |       // 使用 jQuery 将所有展开的 card 收起
    155 |       $(`#card-body-${api.physical_name}`).collapse('hide');
    156 |     });
kirin-ri commented 1 month ago
function FilterTags(tags: Defs.Tag[], setTags: any, apiList: Defs.Api[]) {
  const collapseAllCards = () => {
    apiList.forEach((api) => {
      // 使用 jQuery 将所有展开的 card 收起
      $(`#card-body-${api.physical_name}`).collapse('hide');
    });
  };

  return (
    <div className="inline-form-group">
      <div className="inline-form-label">分類</div>
      {tags.map((item: Defs.Tag) => (
        <div
          className={`btn btn-tag ${item.select ? 'active' : ''}`}
          key={item.tag_name}
          onClick={() => {
            item.select = !item.select; // 切换选择状态
            const t = tags.slice(0, tags.length);
            setTags(t);
            collapseAllCards(); // 标签变化时收起所有 API card
          }}
        >
          {item.tag_name}
        </div>
      ))}
    </div>
  );
}
kirin-ri commented 1 month ago

{/ filter tag /}

フィルタータグ
{FilterTags(tagList, setTags, apiList)}