kirin-ri / memo

0 stars 0 forks source link

asakai #4

Open kirin-ri opened 1 year ago

kirin-ri commented 1 year ago

おはようございます。

朝会を始めたいと思います。 まず、本日のメンバー稼働予定です。 麻生さんが午後休で、大越さんが午後帰社と、伊藤さんと中島さんが予定休です。 ほか出席していますので、問題ないかと思います。

続いて前日やったことと今日やることの共有をお願いします。 まず、大越さんからお願いします。 であれば、小松さんお願いします。 続いて、秋山さんお願いします。 続いてLIですが、 昨日、本番環境でのJenkins構築の手順書に実行ユーザや期待結果などの追記を行いました。確認を受けた後、実際の構築作業に取り掛かりました。本日の作業は、手順書に従って構築を進めていきたいと考えています。以上です。 続いて、山村さん

朝会後で話したい内容について、特に記載ございませんが、何かある方いらっしゃいますか?

LIから1点、山村さんか大越さん向けですが、昨日最新image取り込みのタスクで、運用ルールは後で決める話だったと思うので、今回catelog-web-apに対する修正のプルリクはしない方がいいですか? Jenkisfileとtemplate-web-ap.yamlとweb-ap.yaml

それでは、朝会を終わりにします。本日も一日よろしくお願いいたします。

kirin-ri commented 1 year ago
import React, { ChangeEvent, useState } from "react";
import { useForm } from 'react-hook-form';
import { Spacer } from "../spacer";
import { commonAjax } from '../../../components/commonAjax';
import * as Defs from './apiDefs';
import Test from "./test";

const NewAPIDtlModal = (props: { val: Defs.Api }) => {
  // プルダウンの選択肢
  const gender = [
    { key: 'integer', label: 'integer' },
    { key: 'long', label: 'long' },
    { key: 'float', label: 'float' },
    { key: 'double', label: 'double' },
    { key: 'string', label: 'string' },
    { key: 'byte', label: 'byte' },
    { key: 'binary', label: 'binary' },
    { key: 'boolean', label: 'boolean(true,false)' },
    { key: 'date', label: 'date(yyyy-mm-dd)' },
    { key: 'dateTime', label: 'dateTime(yyyy-mm-ddTHH:MM:SSZ)' },
  ];

  // api情報をコピー
  const tmp = JSON.parse(JSON.stringify(props.val))
  // apiの各項目に削除、編集中、編集済のフラグを付与
  tmp.outinfo[0].column.map((col:Defs.EditColumn)=>{
    col["delete"] = false;
    col["editing"] = false;
    col["edited"] = false;
  })

  // useState
  const origin = JSON.parse(JSON.stringify(tmp))
  const [editAPI, setEditAPI] = useState<Defs.EditApi>(tmp);
  const [editIndex, setEditIndex] = useState<string>("")
  const [reflectLock, setReflectLock] = useState<boolean>(true)
  const [updated, setUpdated] = useState<boolean>(false)
  // input form
  const [colPhysicalName, setColPhysicalName] = useState<string>("")
  const [colLogicalName, setColLogicalName] = useState<string>("")
  const [colType, setColType] = useState<string>("")

  // react hook form
  const {
    register,
    resetField,
    formState: { isDirty, isValid, errors },
  } = useForm({
    defaultValues: { colLogicalName: '', colPhysicalName:'', colType:''},
    mode: 'onChange',
    criteriaMode: 'all',
  });
  const colLogicalNameField = register('colLogicalName', {
    required: {
      value: true,
      message: '論理名は入力が必須の項目です',
    },
  });
  const colPhysicalNameField = register('colPhysicalName', {
    required: {
      value: true,
      message: '物理名は入力が必須の項目です',
    },
    pattern: {
      value: /^[0-9a-zA-Z_]+$/,
      message: '物理名には英数字およびアンダースコアのみ使用可能です',
    }
  });
  const colTypeField = register('colType', {
    required: {
      value: true,
      message: 'データ型は入力が必須の項目です',
    },
  });

  // 物理名、論理名が入力された際に値を設定する
  const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
    switch (e.target.name) {
      case 'colLogicalName':
        setColLogicalName(e.target.value);
        break;
      case 'colPhysicalName':
        setColPhysicalName(e.target.value);
        break;
    }
  };
  // データ型が入力された際に値を設定する
  const selectChange = (e: ChangeEvent<HTMLSelectElement>) => {
        setColType(e.target.value);
  };

  // 更新と追加のボタン切り替え
  const addOrUpd = ()=>{
    if(editIndex===""){
      return(
        <div className="btn-right">
          <button className="btn btn-secondary"
            disabled={!isDirty || !isValid}
            onClick={()=>{
              const addCol={
                "physicalName":colPhysicalName,
                "logicalName":colLogicalName,
                "type":colType,
                "required":$('#'+editAPI.physical_name+'-required').prop('checked'),
                "delete":false,
                "editing":false,
                "edited":true
              }
              editAPI.outinfo[0].column.push(addCol)
              setEditAPI({...editAPI})
              formReset()
              if(reflectLock){
                $('#'+editAPI.physical_name+'-error').text("")
              }
            }}
          >
            追加する
          </button>
        </div>
      )
    } else {
      return(
        <div className="btn-right">
          <button className="btn btn-secondary"
            disabled={
              Boolean(errors.colLogicalName)
              || Boolean(errors.colPhysicalName)
              || Boolean(errors.colType)
            }
            onClick={()=>{
              const addCol={
                "physicalName":colPhysicalName,
                "logicalName":colLogicalName,
                "type":colType,
                "required":$('#'+editAPI.physical_name+'-required').prop('checked'),
                "delete":false,
                "editing":false,
                "edited":true
              }
              editAPI.outinfo[0].column[Number(editIndex)] = addCol
              setEditAPI({...editAPI})
              setEditIndex("")
              formReset()
            }}
          >
            更新する
          </button>
        </div>
      )
    }
  }

  // 削除と復元のボタン切り替え
  const deleteOrUndo = (item:Defs.EditColumn) => {
    if(!item.delete) {
      return(
        <i className="fa fa-times-circle" aria-hidden="true"
           onClick={()=>{
            editCancel(item)
            item.delete=true
            setEditAPI({...editAPI})
           }}
        ></i>
      )
    } else {
      return(
        <i className="fa fa-reply" aria-hidden="true"
           onClick={()=>{
            item.delete=false
            formReset()
            if(reflectLock){
              $('#'+editAPI.physical_name+'-error').text("")
            }
            setEditAPI({...editAPI})
           }}
        ></i>
      )
    }
  }

  // 編集中断処理
  const editCancel = (item:Defs.EditColumn) => {
    formReset()
    setEditIndex("")
    item.editing=false
  }

  // フォームの初期化
  const formReset = () => {
    $('#'+editAPI.physical_name+'-required').prop('checked',false);
    setColLogicalName("")
    setColPhysicalName("")
    setColType("")
    resetField('colLogicalName')
    resetField('colPhysicalName')
    resetField('colType')
  }

  // put api(add col or delete col)
  function putChangeAPI(){
        (async () => {
          let apiList = JSON.parse(JSON.stringify(editAPI))
          apiList.outinfo[0].column.map((item:any, index:number)=>{
            if(item.delete){
              apiList.outinfo[0].column[index] = {}
            } else {
              delete item["delete"]
              delete item["editing"]
              delete item["edited"]
            }
          })
          apiList.outinfo[0].column = apiList.outinfo[0].column.filter((tmp:any) => Object.keys(tmp).length)
          commonAjax
            .axios({swalFire: true, loading: true})
            .put(`/api/api/${editAPI.physical_name}`, apiList.outinfo[0].column)
            .then((res) => {
              apiList.outinfo[0].column.map((col:Defs.EditColumn)=>{
                col["delete"] = false;
                col["editing"] = false;
                col["edited"] = false;
              })
              setEditAPI(apiList)
              setUpdated(true)
            });
        })();
  }

  // モーダルが閉じられた場合、表示するカラムをもとに戻す
  $("#"+props.val.physical_name+"-dtl-modal").on("hidden.bs.modal",()=>{
    if(!updated){
      setEditAPI(JSON.parse(JSON.stringify(origin)))
      setEditIndex("")
      setReflectLock(true)
      formReset()
      $('#'+editAPI.physical_name+'-error').text("")
      $("#"+props.val.physical_name+"-dtl-modal").off("hidden.bs.modal")
    }else{
      setUpdated(false)
      window.location.reload()
    }
  })

  return (
    <>
    {/* Add API col Modal */}
    {/* Content Header (Page header) */}
    <div className="modal fade" id={editAPI.physical_name+"-dtl-modal"}>
      <div className="modal-dialog api-dtl-modal-width">
        <div className="modal-content">
          <div className="modal-header">
            <h4 className="modal-title">サンプルデータ挿入 / API連携項目追加</h4>
            <button type="button" className="close" data-dismiss="modal" aria-label="Close">
              <span aria-hidden="true">&times;</span>
            </button>
          </div>
          <div className="modal-body">
            {/* API name display area */}
            <div className="name-disp-area">
              <div className="api-name">
                <div className="dtl-content-width">API 論理名</div>
                <div className="dtl-modal-font-color">{editAPI.logical_name}</div>
              </div>
              <div className="api-name">
                <div className="dtl-content-width">API 物理名</div>
                <div className="dtl-modal-font-color">{editAPI.physical_name}</div>
              </div>
              <div className="btn-right">
                <button className="btn btn-secondary"
                onClick={()=>{
                  $("#"+editAPI.physical_name+"-dtl-modal").modal("hide").on("hidden.bs.modal",()=>{
                    setTimeout(()=>$('#'+editAPI.physical_name.replaceAll('_', '')+"sample-data").modal("show"))
                    $("#"+editAPI.physical_name+"-dtl-modal").off("hidden.bs.modal")
                  })
                }}>
                  サンプルデータ挿入
                </button>
              </div>
            </div>
            {Spacer({"size":10})}
            <div className="dividing-line"/>
            {/* API col display area */}
            {Spacer({"size":10})}
            <div className="col-disp-title">すでに連携されている連携項目</div>
            {Spacer({"size":20})}
            {editAPI.outinfo.map((val: Defs.EditTableInfo) => {
              const res: any[] = []
              val.column.map((item: Defs.EditColumn, index:number) => {
                res.push(
                  <div className={`add-col-grid col-disp-grid ${item.delete?"delete-col":item.editing?"editing-col":item.edited&&"edited-col"}`}>
                    <div className="add-item">
                      <div className="add-input-form-title"/>
                      <p className="adjust-indent-base adjust-indent-check">{item.required&&"✔"}</p>
                    </div>
                    <div className="add-item">
                      <div className="add-input-form-title"/>
                      <p className="adjust-indent-base adjust-indent-form">{item.logicalName}</p>
                    </div>
                    <div className="add-item">
                      <div className="add-input-form-title"/>
                      <p className="adjust-indent-base adjust-indent-form">{item.physicalName}</p>
                    </div>
                    <div className="add-item">
                      <div className="add-input-form-title"/>
                      <p className="adjust-indent-base adjust-indent-type">{item.type}</p>
                    </div>
                    <div className="btn-left">
                      <i className={`fa fa-solid fa-pen ${item.delete&&"not-applicable"}`}
                         aria-hidden="true"
                         onClick={()=>{
                          if(item.editing){
                            editCancel(item)
                          }else{
                            if(editIndex!==""){
                              editAPI.outinfo[0].column[Number(editIndex)].editing=false
                            }
                            formReset()
                            setColLogicalName(item.logicalName)
                            setColPhysicalName(item.physicalName)
                            setColType(item.type)
                            $('#'+editAPI.physical_name+'-required').prop('checked',item.required);
                            item.editing=true
                            setEditIndex(String(index))
                          }
                         }}
                      ></i>
                      {Spacer({"size":50,horizontal:true})}
                      {deleteOrUndo(item)}
                    </div>
                  </div>
                )
              })
              return res
            })}
            {Spacer({"size":30})}
            <div className="dividing-line"/>
            {/* Add col input area */}
            {Spacer({"size":30})}
            <div className="add-col-grid">
              <div className="add-item">
                <div className="add-input-form-title">必須</div>
                <input type="checkbox" id={editAPI.physical_name+"-required"} className="form-check-input" name={editAPI.physical_name+"-required"} value="1"/>
                <label className="form-check-label check-only input-data-check" htmlFor={editAPI.physical_name+"-required"}></label>
              </div>
              <div className="add-item">
                <div className="add-input-form-title">論理名</div>
                <input
                  type="text"
                  id={editAPI.physical_name+"-add-col-l-name"}
                  className="form-control add-input-form"
                  value={colLogicalName}
                  {...colLogicalNameField}
                  onChange={(e) => {
                    colLogicalNameField.onChange(e);
                    handleChange(e);
                  }}
                />
              </div>
              <div className="add-item">
                <div className="add-input-form-title">物理名</div>
                <input
                  type="text"
                  id={editAPI.physical_name+"-add-col-p-name"}
                  className="form-control add-input-form"
                  value={colPhysicalName}
                  {...colPhysicalNameField}
                  onChange={(e) => {
                    colPhysicalNameField.onChange(e);
                    handleChange(e);
                  }}
                />
              </div>
              <div className="add-item">
                <div className="add-input-form-title">データ型</div>
                <select
                  id={editAPI.physical_name+"-category-sel"}
                  className="form-control input-data-type"
                  required
                  value={colType}
                  {...colTypeField}
                  onChange={(e) => {
                    colTypeField.onChange(e);
                    selectChange(e);
                  }}
                >
                  <option hidden value="">選択する</option>
                  {gender.map((item, i) => (
                    <option
                      value={item.key}
                      key={item.key}
                    >
                      {item.label}
                    </option>
                  ))}
                </select>
              </div>
              {addOrUpd()}
            </div>
            {Spacer({"size":25})}
            <div className="error-msg-area">
              {errors.colLogicalName?.message && (
                  <div className="text-danger">{errors.colLogicalName.message}</div>
              )}
              {errors.colPhysicalName?.message && (
                  <div className="text-danger">{errors.colPhysicalName.message}</div>
              )}
              {errors.colType?.message && (
                  <div className="text-danger">{errors.colType.message}</div>
              )}
              <div className="text-danger" id={editAPI.physical_name+'-error'}></div>
            </div>
            <div className="reflect-col-btn">
            <button className="btn btn-primary" data-toggle="modal" data-target='#test-modal'>test用</button>
              <button className="btn btn-primary"
                      onClick={()=>{
                        let flg = true
                        editAPI.outinfo[0].column.map((item:Defs.EditColumn)=>{
                          if(!item.delete){
                            flg = false
                            setReflectLock(false)
                          }
                        })
                        if(!flg){
                          putChangeAPI()
                        }else{
                          $('#'+editAPI.physical_name+'-error').text("連携項目は1つ以上必要です")
                        }
                      }}>
                反映する
              </button>
              <Test/>
            </div>
            {Spacer({"size":30})}
          </div>
        </div>
        {/* <!-- /.modal-content --> */}
      </div>
      {/* <!-- /.modal-dialog --> */}
    </div>
    {/* <!-- /.modal --> */}
  </>
  )
}

export default NewAPIDtlModal;
kirin-ri commented 1 year ago
// Test Modal
const Test = () => {

  return (
    <>
    {/* Add API col Modal */}
    {/* Content Header (Page header) */}
    <div className="modal fade" id="test-modal">
      <div className="modal-dialog api-dtl-modal-width">
        <div className="modal-content">
          <div className="modal-header">
            <h4 className="modal-title">test用</h4>
          </div>
        </div>
        {/* <!-- /.modal-content --> */}
      </div>
      {/* <!-- /.modal-dialog --> */}
    </div>
    {/* <!-- /.modal --> */}
  </>
  )
}

export default Test;
kirin-ri commented 1 year ago
<div class="modal" tabindex="-1" role="dialog" id="test-modal">
  <div class="modal-dialog" role="document">
    <div class="modal-content">
      <div class="modal-header">
        <h5 class="modal-title">Modal title</h5>
        <button type="button" class="close" data-dismiss="modal" aria-label="Close">
          <span aria-hidden="true">&times;</span>
        </button>
      </div>
      <div class="modal-body">
        <p>Modal body text goes here.</p>
      </div>
      <div class="modal-footer">
        <button type="button" class="btn btn-primary">Save changes</button>
        <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
      </div>
    </div>
  </div>
</div>
kirin-ri commented 1 year ago
      <button className="btn btn-primary" onClick={() => setIsModalOpen(true)}>test用</button>

      {isModalOpen && (
        <div id="test-modal">
          <p>This is a modal. Click the button to close.</p>
          <button onClick={() => setIsModalOpen(false)}>Close Modal</button>
        </div>
      )}
kirin-ri commented 1 year ago
import React, { ChangeEvent, useState } from "react";
import { useForm } from 'react-hook-form';
import { Spacer } from "../spacer";
import { commonAjax } from '../../../components/commonAjax';
import * as Defs from './apiDefs';
import Test from "./test";

const NewAPIDtlModal = (props: { val: Defs.Api }) => {

  const [isModalOpen,setIsModalOpen]=useState(false);
  // プルダウンの選択肢
  const gender = [
    { key: 'integer', label: 'integer' },
    { key: 'long', label: 'long' },
    { key: 'float', label: 'float' },
    { key: 'double', label: 'double' },
    { key: 'string', label: 'string' },
    { key: 'byte', label: 'byte' },
    { key: 'binary', label: 'binary' },
    { key: 'boolean', label: 'boolean(true,false)' },
    { key: 'date', label: 'date(yyyy-mm-dd)' },
    { key: 'dateTime', label: 'dateTime(yyyy-mm-ddTHH:MM:SSZ)' },
  ];

  // api情報をコピー
  const tmp = JSON.parse(JSON.stringify(props.val))
  // apiの各項目に削除、編集中、編集済のフラグを付与
  tmp.outinfo[0].column.map((col:Defs.EditColumn)=>{
    col["delete"] = false;
    col["editing"] = false;
    col["edited"] = false;
  })

  // useState
  const origin = JSON.parse(JSON.stringify(tmp))
  const [editAPI, setEditAPI] = useState<Defs.EditApi>(tmp);
  const [editIndex, setEditIndex] = useState<string>("")
  const [reflectLock, setReflectLock] = useState<boolean>(true)
  const [updated, setUpdated] = useState<boolean>(false)
  // input form
  const [colPhysicalName, setColPhysicalName] = useState<string>("")
  const [colLogicalName, setColLogicalName] = useState<string>("")
  const [colType, setColType] = useState<string>("")

  // react hook form
  const {
    register,
    resetField,
    formState: { isDirty, isValid, errors },
  } = useForm({
    defaultValues: { colLogicalName: '', colPhysicalName:'', colType:''},
    mode: 'onChange',
    criteriaMode: 'all',
  });
  const colLogicalNameField = register('colLogicalName', {
    required: {
      value: true,
      message: '論理名は入力が必須の項目です',
    },
  });
  const colPhysicalNameField = register('colPhysicalName', {
    required: {
      value: true,
      message: '物理名は入力が必須の項目です',
    },
    pattern: {
      value: /^[0-9a-zA-Z_]+$/,
      message: '物理名には英数字およびアンダースコアのみ使用可能です',
    }
  });
  const colTypeField = register('colType', {
    required: {
      value: true,
      message: 'データ型は入力が必須の項目です',
    },
  });

  // 物理名、論理名が入力された際に値を設定する
  const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
    switch (e.target.name) {
      case 'colLogicalName':
        setColLogicalName(e.target.value);
        break;
      case 'colPhysicalName':
        setColPhysicalName(e.target.value);
        break;
    }
  };
  // データ型が入力された際に値を設定する
  const selectChange = (e: ChangeEvent<HTMLSelectElement>) => {
        setColType(e.target.value);
  };

  // 更新と追加のボタン切り替え
  const addOrUpd = ()=>{
    if(editIndex===""){
      return(
        <div className="btn-right">
          <button className="btn btn-secondary"
            disabled={!isDirty || !isValid}
            onClick={()=>{
              const addCol={
                "physicalName":colPhysicalName,
                "logicalName":colLogicalName,
                "type":colType,
                "required":$('#'+editAPI.physical_name+'-required').prop('checked'),
                "delete":false,
                "editing":false,
                "edited":true
              }
              editAPI.outinfo[0].column.push(addCol)
              setEditAPI({...editAPI})
              formReset()
              if(reflectLock){
                $('#'+editAPI.physical_name+'-error').text("")
              }
            }}
          >
            追加する
          </button>
        </div>
      )
    } else {
      return(
        <div className="btn-right">
          <button className="btn btn-secondary"
            disabled={
              Boolean(errors.colLogicalName)
              || Boolean(errors.colPhysicalName)
              || Boolean(errors.colType)
            }
            onClick={()=>{
              const addCol={
                "physicalName":colPhysicalName,
                "logicalName":colLogicalName,
                "type":colType,
                "required":$('#'+editAPI.physical_name+'-required').prop('checked'),
                "delete":false,
                "editing":false,
                "edited":true
              }
              editAPI.outinfo[0].column[Number(editIndex)] = addCol
              setEditAPI({...editAPI})
              setEditIndex("")
              formReset()
            }}
          >
            更新する
          </button>
        </div>
      )
    }
  }

  // 削除と復元のボタン切り替え
  const deleteOrUndo = (item:Defs.EditColumn) => {
    if(!item.delete) {
      return(
        <i className="fa fa-times-circle" aria-hidden="true"
           onClick={()=>{
            editCancel(item)
            item.delete=true
            setEditAPI({...editAPI})
           }}
        ></i>
      )
    } else {
      return(
        <i className="fa fa-reply" aria-hidden="true"
           onClick={()=>{
            item.delete=false
            formReset()
            if(reflectLock){
              $('#'+editAPI.physical_name+'-error').text("")
            }
            setEditAPI({...editAPI})
           }}
        ></i>
      )
    }
  }

  // 編集中断処理
  const editCancel = (item:Defs.EditColumn) => {
    formReset()
    setEditIndex("")
    item.editing=false
  }

  // フォームの初期化
  const formReset = () => {
    $('#'+editAPI.physical_name+'-required').prop('checked',false);
    setColLogicalName("")
    setColPhysicalName("")
    setColType("")
    resetField('colLogicalName')
    resetField('colPhysicalName')
    resetField('colType')
  }

  // put api(add col or delete col)
  function putChangeAPI(){
        (async () => {
          let apiList = JSON.parse(JSON.stringify(editAPI))
          apiList.outinfo[0].column.map((item:any, index:number)=>{
            if(item.delete){
              apiList.outinfo[0].column[index] = {}
            } else {
              delete item["delete"]
              delete item["editing"]
              delete item["edited"]
            }
          })
          apiList.outinfo[0].column = apiList.outinfo[0].column.filter((tmp:any) => Object.keys(tmp).length)
          commonAjax
            .axios({swalFire: true, loading: true})
            .put(`/api/api/${editAPI.physical_name}`, apiList.outinfo[0].column)
            .then((res) => {
              apiList.outinfo[0].column.map((col:Defs.EditColumn)=>{
                col["delete"] = false;
                col["editing"] = false;
                col["edited"] = false;
              })
              setEditAPI(apiList)
              setUpdated(true)
            });
        })();
  }

  // モーダルが閉じられた場合、表示するカラムをもとに戻す
  $("#"+props.val.physical_name+"-dtl-modal").on("hidden.bs.modal",()=>{
    if(!updated){
      setEditAPI(JSON.parse(JSON.stringify(origin)))
      setEditIndex("")
      setReflectLock(true)
      formReset()
      $('#'+editAPI.physical_name+'-error').text("")
      $("#"+props.val.physical_name+"-dtl-modal").off("hidden.bs.modal")
    }else{
      setUpdated(false)
      window.location.reload()
    }
  })

  return (
    <>
    {/* Add API col Modal */}
    {/* Content Header (Page header) */}
    <div className="modal fade" id={editAPI.physical_name+"-dtl-modal"}>
      <div className="modal-dialog api-dtl-modal-width">
        <div className="modal-content">
          <div className="modal-header">
            <h4 className="modal-title">サンプルデータ挿入 / API連携項目追加</h4>
            <button type="button" className="close" data-dismiss="modal" aria-label="Close">
              <span aria-hidden="true">&times;</span>
            </button>
          </div>
          <div className="modal-body">
            {/* API name display area */}
            <div className="name-disp-area">
              <div className="api-name">
                <div className="dtl-content-width">API 論理名</div>
                <div className="dtl-modal-font-color">{editAPI.logical_name}</div>
              </div>
              <div className="api-name">
                <div className="dtl-content-width">API 物理名</div>
                <div className="dtl-modal-font-color">{editAPI.physical_name}</div>
              </div>
              <div className="btn-right">
                <button className="btn btn-secondary"
                onClick={()=>{
                  $("#"+editAPI.physical_name+"-dtl-modal").modal("hide").on("hidden.bs.modal",()=>{
                    setTimeout(()=>$('#'+editAPI.physical_name.replaceAll('_', '')+"sample-data").modal("show"))
                    $("#"+editAPI.physical_name+"-dtl-modal").off("hidden.bs.modal")
                  })
                }}>
                  サンプルデータ挿入
                </button>
              </div>
            </div>
            {Spacer({"size":10})}
            <div className="dividing-line"/>
            {/* API col display area */}
            {Spacer({"size":10})}
            <div className="col-disp-title">すでに連携されている連携項目</div>
            {Spacer({"size":20})}
            {editAPI.outinfo.map((val: Defs.EditTableInfo) => {
              const res: any[] = []
              val.column.map((item: Defs.EditColumn, index:number) => {
                res.push(
                  <div className={`add-col-grid col-disp-grid ${item.delete?"delete-col":item.editing?"editing-col":item.edited&&"edited-col"}`}>
                    <div className="add-item">
                      <div className="add-input-form-title"/>
                      <p className="adjust-indent-base adjust-indent-check">{item.required&&"✔"}</p>
                    </div>
                    <div className="add-item">
                      <div className="add-input-form-title"/>
                      <p className="adjust-indent-base adjust-indent-form">{item.logicalName}</p>
                    </div>
                    <div className="add-item">
                      <div className="add-input-form-title"/>
                      <p className="adjust-indent-base adjust-indent-form">{item.physicalName}</p>
                    </div>
                    <div className="add-item">
                      <div className="add-input-form-title"/>
                      <p className="adjust-indent-base adjust-indent-type">{item.type}</p>
                    </div>
                    <div className="btn-left">
                      <i className={`fa fa-solid fa-pen ${item.delete&&"not-applicable"}`}
                         aria-hidden="true"
                         onClick={()=>{
                          if(item.editing){
                            editCancel(item)
                          }else{
                            if(editIndex!==""){
                              editAPI.outinfo[0].column[Number(editIndex)].editing=false
                            }
                            formReset()
                            setColLogicalName(item.logicalName)
                            setColPhysicalName(item.physicalName)
                            setColType(item.type)
                            $('#'+editAPI.physical_name+'-required').prop('checked',item.required);
                            item.editing=true
                            setEditIndex(String(index))
                          }
                         }}
                      ></i>
                      {Spacer({"size":50,horizontal:true})}
                      {deleteOrUndo(item)}
                    </div>
                  </div>
                )
              })
              return res
            })}
            {Spacer({"size":30})}
            <div className="dividing-line"/>
            {/* Add col input area */}
            {Spacer({"size":30})}
            <div className="add-col-grid">
              <div className="add-item">
                <div className="add-input-form-title">必須</div>
                <input type="checkbox" id={editAPI.physical_name+"-required"} className="form-check-input" name={editAPI.physical_name+"-required"} value="1"/>
                <label className="form-check-label check-only input-data-check" htmlFor={editAPI.physical_name+"-required"}></label>
              </div>
              <div className="add-item">
                <div className="add-input-form-title">論理名</div>
                <input
                  type="text"
                  id={editAPI.physical_name+"-add-col-l-name"}
                  className="form-control add-input-form"
                  value={colLogicalName}
                  {...colLogicalNameField}
                  onChange={(e) => {
                    colLogicalNameField.onChange(e);
                    handleChange(e);
                  }}
                />
              </div>
              <div className="add-item">
                <div className="add-input-form-title">物理名</div>
                <input
                  type="text"
                  id={editAPI.physical_name+"-add-col-p-name"}
                  className="form-control add-input-form"
                  value={colPhysicalName}
                  {...colPhysicalNameField}
                  onChange={(e) => {
                    colPhysicalNameField.onChange(e);
                    handleChange(e);
                  }}
                />
              </div>
              <div className="add-item">
                <div className="add-input-form-title">データ型</div>
                <select
                  id={editAPI.physical_name+"-category-sel"}
                  className="form-control input-data-type"
                  required
                  value={colType}
                  {...colTypeField}
                  onChange={(e) => {
                    colTypeField.onChange(e);
                    selectChange(e);
                  }}
                >
                  <option hidden value="">選択する</option>
                  {gender.map((item, i) => (
                    <option
                      value={item.key}
                      key={item.key}
                    >
                      {item.label}
                    </option>
                  ))}
                </select>
              </div>
              {addOrUpd()}
            </div>
            {Spacer({"size":25})}
            <div className="error-msg-area">
              {errors.colLogicalName?.message && (
                  <div className="text-danger">{errors.colLogicalName.message}</div>
              )}
              {errors.colPhysicalName?.message && (
                  <div className="text-danger">{errors.colPhysicalName.message}</div>
              )}
              {errors.colType?.message && (
                  <div className="text-danger">{errors.colType.message}</div>
              )}
              <div className="text-danger" id={editAPI.physical_name+'-error'}></div>
            </div>
            <div className="reflect-col-btn">
            <button className="btn btn-primary" onClick={() => setIsModalOpen(true)}>test用</button>

{isModalOpen && (
  <div id="test-modal">
    <p>This is a modal. Click the button to close.</p>
    <button onClick={() => setIsModalOpen(false)}>Close Modal</button>
  </div>
)}

              <button className="btn btn-primary"
                      onClick={()=>{
                        let flg = true
                        editAPI.outinfo[0].column.map((item:Defs.EditColumn)=>{
                          if(!item.delete){
                            flg = false
                            setReflectLock(false)
                          }
                        })
                        if(!flg){
                          putChangeAPI()
                        }else{
                          $('#'+editAPI.physical_name+'-error').text("連携項目は1つ以上必要です")
                        }
                      }}>
                反映する
              </button>
              <Test/>
            </div>
            {Spacer({"size":30})}
          </div>
        </div>
        {/* <!-- /.modal-content --> */}
      </div>
      {/* <!-- /.modal-dialog --> */}
    </div>
    {/* <!-- /.modal --> */}
  </>
  )
}

export default NewAPIDtlModal;
kirin-ri commented 1 year ago
import React from "react"
import { Spacer } from "../spacer";
import { useState, useEffect } from 'react';
import NewAPIDtlModal from './newAPIDtlModal'
import NewSampleDataModal from './newSampleDataModal'
import CreateNewApi from './createNewApi'
import EditApiList from './editApiList'
import { commonAjax } from '../../../components/commonAjax';
import * as Defs from './apiDefs';

// main
function NewApiList() {
  // useState
  const [apiList, setApiList] = useState<Defs.Api[]>([]);
  const [tagList, setTags] = useState<Defs.Tag[]>([]);
  const [changeStatus, setChangeStatus] = useState<boolean>(false);

  /* API情報取得のリクエスト */
  useEffect(() => {
    (async () => {
       commonAjax
        .axios({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:true})
          })
          setApiList(data);
          setTags(resTags)
        });
    })();
  }, [changeStatus]);

  return (
    <div className="content-wrapper scm-api-list">
      <section className="page-cover">
        <h1>SCM API一覧</h1>
      </section>
      {/* Content Header (Page header) */}
      <section className="content-header">
        <div className="content-header-left">
          <h1>SCM API一覧</h1>
          <div className="content-header-desc">現在、設定されているAPI一覧です</div>
        </div>
        <div className="content-header-right">
          <button className="btn btn-secondary" data-toggle="modal" data-target='#editApiListModal'>編集</button>
          <button className="btn btn-primary" data-toggle="modal" data-target='#api-dtl-modal'>追加</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)}
      </section>
      <CreateNewApi
        changeStatus={changeStatus}
        setChangeStatus={setChangeStatus}
      />
      <EditApiList
        val={apiList}
        changeStatus={changeStatus}
        setChangeStatus={setChangeStatus}
      />
    </div>
  );
}

// fillter tag
function FilterTags (tags:Defs.Tag[],setTags:any){
    return(
      <div className="inline-form-group">
        <div className="inline-form-label">分類</div>
        {tags.map((item:Defs.Tag) => {
          if(item.select){
            return(
              <div className="btn btn-tag active"
                onClick={()=>{
                  item.select = false;
                  let t = tags.slice(0,tags.length);
                  setTags(t);
                }}
              >
                {item.tag_name}
              </div>
            )
          }else{
            return(
              <div className="btn btn-tag"
              onClick={()=>{
                item.select = true;
                let t = tags.slice(0,tags.length);
                setTags(t);
              }}
              >
                {item.tag_name}
              </div>
            )
          }
        })}
      </div>
    )
}

function viewList (item:Defs.Api[], tagList:Defs.Tag[], changeStatus:boolean, setChangeStatus:any){
  // if(item.length){
    const length = Math.ceil(item.length / 3)
    let listItem:any=[]
    if(length!=0){
      listItem = transpose(new Array(length).fill(0).map((_, i) => item.slice(i * 3, (i + 1) * 3)))
    }

    return(
        <div className="list-wrapper">
          <div className="apiList">
            {0 >= 0&&0<listItem.length?listItem[0].map((val: Defs.Api)=>ApiCard(val,tagList,changeStatus,setChangeStatus)):null}
          </div>
          <div className="apiList">
            {1>= 0&&1<listItem.length?listItem[1].map((val: Defs.Api)=>ApiCard(val,tagList,changeStatus,setChangeStatus)):null}
          </div>
          <div className="apiList">
            {2>= 0&&2<listItem.length?listItem[2].map((val: Defs.Api)=>ApiCard(val,tagList,changeStatus,setChangeStatus)):null}
          </div>
        </div>
    )
  // }
}

// API Card
function ApiCard(api: Defs.Api,tagList:Defs.Tag[],changeStatus:boolean,setChangeStatus:any) {
  let selected = false
  tagList.map((item:Defs.Tag)=>{
    api.tags.map((apiTag:string)=>{
      if(apiTag==item.tag_name){
        if(item.select){
          selected=true
        }
      }
    })
  })
  $('#'+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={'#'+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': undefined
            }`}
            aria-hidden="true"
          />
          {Spacer({"size":10, horizontal:true})}
          {api.physical_name+' / '+api.logical_name}
          </div>
        </div>
        <div className="card-body collapse" id={api.physical_name}>
          <div className="api-list-title">
            <p>
              API 連携項目 一覧
            </p>
            {Spacer({"size":20})}
          </div>
          {api.outinfo.map((val: Defs.TableInfo) => {
            const res: JSX.Element[] = []
            val.column.map((item: Defs.Column) => {
              res.push(
                <div className="list-contents-grid">
                  <p>{item.logicalName}</p>
                </div>
              )
            })
            return res
          })}
          {Spacer({"size":20})}
          <div className="btn-center">
            {api.provide
              ?
                <button
                  className="btn btn-secondary btn-secondary"
                  data-toggle="modal"
                  data-target={"#"+api.physical_name+"-dtl-modal"}
                >
                  サンプルデータ挿入 / API連携項目追加
                </button>
              :
                <button
                  className="btn btn-secondary btn-secondary"
                  onClick={()=>{putDeploy(api,changeStatus,setChangeStatus)}}
                >
                  Deploy
                </button>
            }
          </div>
          {/* <div>{NewAPIDtlModal(api)}</div> */}
          <NewAPIDtlModal val={api} />
          <NewSampleDataModal id={api.physical_name}/>
        </div>
      </div>
    </>
  )
}

// Sample Modal
function Modal() {
  return (
    <>
    {/* Content Header (Page header) */}
    <div className="modal fade" id="sampleModal1">
      <div className="modal-dialog modal-xl">
        <div className="modal-content">
          <div className="modal-header">
            <h4 className="modal-title">SAMPLE MODAL</h4>
            <button type="button" className="close" data-dismiss="modal" aria-label="Close">
              <span aria-hidden="true">&times;</span>
            </button>
          </div>
          <div className="modal-body">
            <p>BODY</p>
          </div>
        </div>
        {/* <!-- /.modal-content --> */}
      </div>
      {/* <!-- /.modal-dialog --> */}
    </div>
    {/* <!-- /.modal --> */}
    {/* /.content */}
  </>
  )
}

// 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){
  if (api) {
    if (!api.provide) {
      (async () => {
        commonAjax
          .axios({swalFire: true, loading: true})
          .put(`/api/api/${api.physical_name}`)
          .then((res) => {
            setChangeStatus(changeStatus?false:true);
          });
      })();
    }
  }
}

export default NewApiList;
kirin-ri commented 1 year ago
import React, { ChangeEvent, useState } from "react";
import { useForm } from 'react-hook-form';
import { Spacer } from "../spacer";
import { commonAjax } from '../../../components/commonAjax';
import * as Defs from './apiDefs';

const NewAPIDtlModal = (props: { val: Defs.Api }) => {
  // プルダウンの選択肢
  const gender = [
    { key: 'integer', label: 'integer' },
    { key: 'long', label: 'long' },
    { key: 'float', label: 'float' },
    { key: 'double', label: 'double' },
    { key: 'string', label: 'string' },
    { key: 'byte', label: 'byte' },
    { key: 'binary', label: 'binary' },
    { key: 'boolean', label: 'boolean(true,false)' },
    { key: 'date', label: 'date(yyyy-mm-dd)' },
    { key: 'dateTime', label: 'dateTime(yyyy-mm-ddTHH:MM:SSZ)' },
  ];

  // api情報をコピー
  const tmp = JSON.parse(JSON.stringify(props.val))
  // apiの各項目に削除、編集中、編集済のフラグを付与
  tmp.outinfo[0].column.map((col:Defs.EditColumn)=>{
    col["delete"] = false;
    col["editing"] = false;
    col["edited"] = false;
  })

  // useState
  const origin = JSON.parse(JSON.stringify(tmp))
  const [editAPI, setEditAPI] = useState<Defs.EditApi>(tmp);
  const [editIndex, setEditIndex] = useState<string>("")
  const [reflectLock, setReflectLock] = useState<boolean>(true)
  const [updated, setUpdated] = useState<boolean>(false)
  // input form
  const [colPhysicalName, setColPhysicalName] = useState<string>("")
  const [colLogicalName, setColLogicalName] = useState<string>("")
  const [colType, setColType] = useState<string>("")

  // react hook form
  const {
    register,
    resetField,
    formState: { isDirty, isValid, errors },
  } = useForm({
    defaultValues: { colLogicalName: '', colPhysicalName:'', colType:''},
    mode: 'onChange',
    criteriaMode: 'all',
  });
  const colLogicalNameField = register('colLogicalName', {
    required: {
      value: true,
      message: '論理名は入力が必須の項目です',
    },
  });
  const colPhysicalNameField = register('colPhysicalName', {
    required: {
      value: true,
      message: '物理名は入力が必須の項目です',
    },
    pattern: {
      value: /^[0-9a-zA-Z_]+$/,
      message: '物理名には英数字およびアンダースコアのみ使用可能です',
    }
  });
  const colTypeField = register('colType', {
    required: {
      value: true,
      message: 'データ型は入力が必須の項目です',
    },
  });

  // 物理名、論理名が入力された際に値を設定する
  const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
    switch (e.target.name) {
      case 'colLogicalName':
        setColLogicalName(e.target.value);
        break;
      case 'colPhysicalName':
        setColPhysicalName(e.target.value);
        break;
    }
  };
  // データ型が入力された際に値を設定する
  const selectChange = (e: ChangeEvent<HTMLSelectElement>) => {
        setColType(e.target.value);
  };

  // 更新と追加のボタン切り替え
  const addOrUpd = ()=>{
    if(editIndex===""){
      return(
        <div className="btn-right">
          <button className="btn btn-secondary"
            disabled={!isDirty || !isValid}
            onClick={()=>{
              const addCol={
                "physicalName":colPhysicalName,
                "logicalName":colLogicalName,
                "type":colType,
                "required":$('#'+editAPI.physical_name+'-required').prop('checked'),
                "delete":false,
                "editing":false,
                "edited":true
              }
              editAPI.outinfo[0].column.push(addCol)
              setEditAPI({...editAPI})
              formReset()
              if(reflectLock){
                $('#'+editAPI.physical_name+'-error').text("")
              }
            }}
          >
            追加する
          </button>
        </div>
      )
    } else {
      return(
        <div className="btn-right">
          <button className="btn btn-secondary"
            disabled={
              Boolean(errors.colLogicalName)
              || Boolean(errors.colPhysicalName)
              || Boolean(errors.colType)
            }
            onClick={()=>{
              const addCol={
                "physicalName":colPhysicalName,
                "logicalName":colLogicalName,
                "type":colType,
                "required":$('#'+editAPI.physical_name+'-required').prop('checked'),
                "delete":false,
                "editing":false,
                "edited":true
              }
              editAPI.outinfo[0].column[Number(editIndex)] = addCol
              setEditAPI({...editAPI})
              setEditIndex("")
              formReset()
            }}
          >
            更新する
          </button>
        </div>
      )
    }
  }

  // 削除と復元のボタン切り替え
  const deleteOrUndo = (item:Defs.EditColumn) => {
    if(!item.delete) {
      return(
        <i className="fa fa-times-circle" aria-hidden="true"
           onClick={()=>{
            editCancel(item)
            item.delete=true
            setEditAPI({...editAPI})
           }}
        ></i>
      )
    } else {
      return(
        <i className="fa fa-reply" aria-hidden="true"
           onClick={()=>{
            item.delete=false
            formReset()
            if(reflectLock){
              $('#'+editAPI.physical_name+'-error').text("")
            }
            setEditAPI({...editAPI})
           }}
        ></i>
      )
    }
  }

  // 編集中断処理
  const editCancel = (item:Defs.EditColumn) => {
    formReset()
    setEditIndex("")
    item.editing=false
  }

  // フォームの初期化
  const formReset = () => {
    $('#'+editAPI.physical_name+'-required').prop('checked',false);
    setColLogicalName("")
    setColPhysicalName("")
    setColType("")
    resetField('colLogicalName')
    resetField('colPhysicalName')
    resetField('colType')
  }

  // put api(add col or delete col)
  function putChangeAPI(){
        (async () => {
          let apiList = JSON.parse(JSON.stringify(editAPI))
          apiList.outinfo[0].column.map((item:any, index:number)=>{
            if(item.delete){
              apiList.outinfo[0].column[index] = {}
            } else {
              delete item["delete"]
              delete item["editing"]
              delete item["edited"]
            }
          })
          apiList.outinfo[0].column = apiList.outinfo[0].column.filter((tmp:any) => Object.keys(tmp).length)
          commonAjax
            .axios({swalFire: true, loading: true})
            .put(`/api/api/${editAPI.physical_name}`, apiList.outinfo[0].column)
            .then((res) => {
              apiList.outinfo[0].column.map((col:Defs.EditColumn)=>{
                col["delete"] = false;
                col["editing"] = false;
                col["edited"] = false;
              })
              setEditAPI(apiList)
              setUpdated(true)
            });
        })();
  }

  // モーダルが閉じられた場合、表示するカラムをもとに戻す
  $("#"+props.val.physical_name+"-dtl-modal").on("hidden.bs.modal",()=>{
    if(!updated){
      setEditAPI(JSON.parse(JSON.stringify(origin)))
      setEditIndex("")
      setReflectLock(true)
      formReset()
      $('#'+editAPI.physical_name+'-error').text("")
      $("#"+props.val.physical_name+"-dtl-modal").off("hidden.bs.modal")
    }else{
      setUpdated(false)
      window.location.reload()
    }
  })

  return (
    <>
    {/* Add API col Modal */}
    {/* Content Header (Page header) */}
    <div className="modal fade" id={editAPI.physical_name+"-dtl-modal"}>
      <div className="modal-dialog api-dtl-modal-width">
        <div className="modal-content">
          <div className="modal-header">
            <h4 className="modal-title">サンプルデータ挿入 / API連携項目追加</h4>
            <button type="button" className="close" data-dismiss="modal" aria-label="Close">
              <span aria-hidden="true">&times;</span>
            </button>
          </div>
          <div className="modal-body">
            {/* API name display area */}
            <div className="name-disp-area">
              <div className="api-name">
                <div className="dtl-content-width">API 論理名</div>
                <div className="dtl-modal-font-color">{editAPI.logical_name}</div>
              </div>
              <div className="api-name">
                <div className="dtl-content-width">API 物理名</div>
                <div className="dtl-modal-font-color">{editAPI.physical_name}</div>
              </div>
              <div className="btn-right">
                <button className="btn btn-secondary"
                onClick={()=>{
                  $("#"+editAPI.physical_name+"-dtl-modal").modal("hide").on("hidden.bs.modal",()=>{
                    setTimeout(()=>$('#'+editAPI.physical_name.replaceAll('_', '')+"sample-data").modal("show"))
                    $("#"+editAPI.physical_name+"-dtl-modal").off("hidden.bs.modal")
                  })
                }}>
                  サンプルデータ挿入
                </button>
              </div>
            </div>
            {Spacer({"size":10})}
            <div className="dividing-line"/>
            {/* API col display area */}
            {Spacer({"size":10})}
            <div className="col-disp-title">すでに連携されている連携項目</div>
            {Spacer({"size":20})}
            {editAPI.outinfo.map((val: Defs.EditTableInfo) => {
              const res: any[] = []
              val.column.map((item: Defs.EditColumn, index:number) => {
                res.push(
                  <div className={`add-col-grid col-disp-grid ${item.delete?"delete-col":item.editing?"editing-col":item.edited&&"edited-col"}`}>
                    <div className="add-item">
                      <div className="add-input-form-title"/>
                      <p className="adjust-indent-base adjust-indent-check">{item.required&&"✔"}</p>
                    </div>
                    <div className="add-item">
                      <div className="add-input-form-title"/>
                      <p className="adjust-indent-base adjust-indent-form">{item.logicalName}</p>
                    </div>
                    <div className="add-item">
                      <div className="add-input-form-title"/>
                      <p className="adjust-indent-base adjust-indent-form">{item.physicalName}</p>
                    </div>
                    <div className="add-item">
                      <div className="add-input-form-title"/>
                      <p className="adjust-indent-base adjust-indent-type">{item.type}</p>
                    </div>
                    <div className="btn-left">
                      <i className={`fa fa-solid fa-pen ${item.delete&&"not-applicable"}`}
                         aria-hidden="true"
                         onClick={()=>{
                          if(item.editing){
                            editCancel(item)
                          }else{
                            if(editIndex!==""){
                              editAPI.outinfo[0].column[Number(editIndex)].editing=false
                            }
                            formReset()
                            setColLogicalName(item.logicalName)
                            setColPhysicalName(item.physicalName)
                            setColType(item.type)
                            $('#'+editAPI.physical_name+'-required').prop('checked',item.required);
                            item.editing=true
                            setEditIndex(String(index))
                          }
                         }}
                      ></i>
                      {Spacer({"size":50,horizontal:true})}
                      {deleteOrUndo(item)}
                    </div>
                  </div>
                )
              })
              return res
            })}
            {Spacer({"size":30})}
            <div className="dividing-line"/>
            {/* Add col input area */}
            {Spacer({"size":30})}
            <div className="add-col-grid">
              <div className="add-item">
                <div className="add-input-form-title">必須</div>
                <input type="checkbox" id={editAPI.physical_name+"-required"} className="form-check-input" name={editAPI.physical_name+"-required"} value="1"/>
                <label className="form-check-label check-only input-data-check" htmlFor={editAPI.physical_name+"-required"}></label>
              </div>
              <div className="add-item">
                <div className="add-input-form-title">論理名</div>
                <input
                  type="text"
                  id={editAPI.physical_name+"-add-col-l-name"}
                  className="form-control add-input-form"
                  value={colLogicalName}
                  {...colLogicalNameField}
                  onChange={(e) => {
                    colLogicalNameField.onChange(e);
                    handleChange(e);
                  }}
                />
              </div>
              <div className="add-item">
                <div className="add-input-form-title">物理名</div>
                <input
                  type="text"
                  id={editAPI.physical_name+"-add-col-p-name"}
                  className="form-control add-input-form"
                  value={colPhysicalName}
                  {...colPhysicalNameField}
                  onChange={(e) => {
                    colPhysicalNameField.onChange(e);
                    handleChange(e);
                  }}
                />
              </div>
              <div className="add-item">
                <div className="add-input-form-title">データ型</div>
                <select
                  id={editAPI.physical_name+"-category-sel"}
                  className="form-control input-data-type"
                  required
                  value={colType}
                  {...colTypeField}
                  onChange={(e) => {
                    colTypeField.onChange(e);
                    selectChange(e);
                  }}
                >
                  <option hidden value="">選択する</option>
                  {gender.map((item, i) => (
                    <option
                      value={item.key}
                      key={item.key}
                    >
                      {item.label}
                    </option>
                  ))}
                </select>
              </div>
              {addOrUpd()}
            </div>
            {Spacer({"size":25})}
            <div className="error-msg-area">
              {errors.colLogicalName?.message && (
                  <div className="text-danger">{errors.colLogicalName.message}</div>
              )}
              {errors.colPhysicalName?.message && (
                  <div className="text-danger">{errors.colPhysicalName.message}</div>
              )}
              {errors.colType?.message && (
                  <div className="text-danger">{errors.colType.message}</div>
              )}
              <div className="text-danger" id={editAPI.physical_name+'-error'}></div>
            </div>
            <div className="reflect-col-btn">
              <button className="btn btn-primary"
                      onClick={()=>{
                        let flg = true
                        editAPI.outinfo[0].column.map((item:Defs.EditColumn)=>{
                          if(!item.delete){
                            flg = false
                            setReflectLock(false)
                          }
                        })
                        if(!flg){
                          putChangeAPI()
                        }else{
                          $('#'+editAPI.physical_name+'-error').text("連携項目は1つ以上必要です")
                        }
                      }}>
                反映する
              </button>
            </div>
            {Spacer({"size":30})}
          </div>
        </div>
        {/* <!-- /.modal-content --> */}
      </div>
      {/* <!-- /.modal-dialog --> */}
    </div>
    {/* <!-- /.modal --> */}
  </>
  )
}

export default NewAPIDtlModal;
kirin-ri commented 1 year ago
import React from "react"
import { Spacer } from "../spacer";
import { useState, useEffect } from 'react';
import NewAPIDtlModal from './newAPIDtlModal'
import NewSampleDataModal from './newSampleDataModal'
import CreateNewApi from './createNewApi'
import EditApiList from './editApiList'
import { commonAjax } from '../../../components/commonAjax';
import * as Defs from './apiDefs';

// main
function NewApiList() {
  // useState
  const [apiList, setApiList] = useState<Defs.Api[]>([]);
  const [tagList, setTags] = useState<Defs.Tag[]>([]);
  const [changeStatus, setChangeStatus] = useState<boolean>(false);

  /* API情報取得のリクエスト */
  useEffect(() => {
    (async () => {
       commonAjax
        .axios({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:true})
          })
          setApiList(data);
          setTags(resTags)
        });
    })();
  }, [changeStatus]);

  return (
    <div className="content-wrapper scm-api-list">
      <section className="page-cover">
        <h1>SCM API一覧</h1>
      </section>
      {/* Content Header (Page header) */}
      <section className="content-header">
        <div className="content-header-left">
          <h1>SCM API一覧</h1>
          <div className="content-header-desc">現在、設定されているAPI一覧です</div>
        </div>
        <div className="content-header-right">
          <button className="btn btn-secondary" data-toggle="modal" data-target='#editApiListModal'>編集</button>
          <button className="btn btn-primary" data-toggle="modal" data-target='#api-dtl-modal'>追加</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)}
      </section>
      <CreateNewApi
        changeStatus={changeStatus}
        setChangeStatus={setChangeStatus}
      />
      <EditApiList
        val={apiList}
        changeStatus={changeStatus}
        setChangeStatus={setChangeStatus}
      />
    </div>
  );
}

// fillter tag
function FilterTags (tags:Defs.Tag[],setTags:any){
    return(
      <div className="inline-form-group">
        <div className="inline-form-label">分類</div>
        {tags.map((item:Defs.Tag) => {
          if(item.select){
            return(
              <div className="btn btn-tag active"
                onClick={()=>{
                  item.select = false;
                  let t = tags.slice(0,tags.length);
                  setTags(t);
                }}
              >
                {item.tag_name}
              </div>
            )
          }else{
            return(
              <div className="btn btn-tag"
              onClick={()=>{
                item.select = true;
                let t = tags.slice(0,tags.length);
                setTags(t);
              }}
              >
                {item.tag_name}
              </div>
            )
          }
        })}
      </div>
    )
}

function viewList (item:Defs.Api[], tagList:Defs.Tag[], changeStatus:boolean, setChangeStatus:any){
  // if(item.length){
    const length = Math.ceil(item.length / 3)
    let listItem:any=[]
    if(length!=0){
      listItem = transpose(new Array(length).fill(0).map((_, i) => item.slice(i * 3, (i + 1) * 3)))
    }

    return(
        <div className="list-wrapper">
          <div className="apiList">
            {0 >= 0&&0<listItem.length?listItem[0].map((val: Defs.Api)=>ApiCard(val,tagList,changeStatus,setChangeStatus)):null}
          </div>
          <div className="apiList">
            {1>= 0&&1<listItem.length?listItem[1].map((val: Defs.Api)=>ApiCard(val,tagList,changeStatus,setChangeStatus)):null}
          </div>
          <div className="apiList">
            {2>= 0&&2<listItem.length?listItem[2].map((val: Defs.Api)=>ApiCard(val,tagList,changeStatus,setChangeStatus)):null}
          </div>
        </div>
    )
  // }
}

// API Card
function ApiCard(api: Defs.Api,tagList:Defs.Tag[],changeStatus:boolean,setChangeStatus:any) {
  let selected = false
  tagList.map((item:Defs.Tag)=>{
    api.tags.map((apiTag:string)=>{
      if(apiTag==item.tag_name){
        if(item.select){
          selected=true
        }
      }
    })
  })
  $('#'+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={'#'+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': undefined
            }`}
            aria-hidden="true"
          />
          {Spacer({"size":10, horizontal:true})}
          {api.physical_name+' / '+api.logical_name}
          </div>
        </div>
        <div className="card-body collapse" id={api.physical_name}>
          <div className="api-list-title">
            <p>
              API 連携項目 一覧
            </p>
            {Spacer({"size":20})}
          </div>
          {api.outinfo.map((val: Defs.TableInfo) => {
            const res: JSX.Element[] = []
            val.column.map((item: Defs.Column) => {
              res.push(
                <div className="list-contents-grid">
                  <p>{item.logicalName}</p>
                </div>
              )
            })
            return res
          })}
          {Spacer({"size":20})}
          <div className="btn-center">
            {api.provide
              ?
                <button
                  className="btn btn-secondary btn-secondary"
                  data-toggle="modal"
                  data-target={"#"+api.physical_name+"-dtl-modal"}
                >
                  サンプルデータ挿入 / API連携項目追加
                </button>
              :
                <button
                  className="btn btn-secondary btn-secondary"
                  onClick={()=>{putDeploy(api,changeStatus,setChangeStatus)}}
                >
                  Deploy
                </button>
            }
          </div>
          {/* <div>{NewAPIDtlModal(api)}</div> */}
          <NewAPIDtlModal val={api} />
          <NewSampleDataModal id={api.physical_name}/>
        </div>
      </div>
    </>
  )
}

// Sample Modal
function Modal() {
  return (
    <>
    {/* Content Header (Page header) */}
    <div className="modal fade" id="sampleModal1">
      <div className="modal-dialog modal-xl">
        <div className="modal-content">
          <div className="modal-header">
            <h4 className="modal-title">SAMPLE MODAL</h4>
            <button type="button" className="close" data-dismiss="modal" aria-label="Close">
              <span aria-hidden="true">&times;</span>
            </button>
          </div>
          <div className="modal-body">
            <p>BODY</p>
          </div>
        </div>
        {/* <!-- /.modal-content --> */}
      </div>
      {/* <!-- /.modal-dialog --> */}
    </div>
    {/* <!-- /.modal --> */}
    {/* /.content */}
  </>
  )
}

// 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){
  if (api) {
    if (!api.provide) {
      (async () => {
        commonAjax
          .axios({swalFire: true, loading: true})
          .put(`/api/api/${api.physical_name}`)
          .then((res) => {
            setChangeStatus(changeStatus?false:true);
          });
      })();
    }
  }
}

export default NewApiList;
kirin-ri commented 1 year ago
import React, { ChangeEvent, useCallback, useEffect, useLayoutEffect, useState } from "react";
import { useForm } from 'react-hook-form';
import { Spacer } from "../spacer";
import { commonAjax } from '../../../components/commonAjax';
import * as Defs from './apiDefs';
import { Colors } from "chart.js";

// Message Modal
const MessageModal = (props: { val: Defs.Api[], changeStatus:boolean,setChangeStatus:any }) => {

  // 遷移元画面の更新に使用
  const changeStatus = props.changeStatus
  const setChangeStatus = props.setChangeStatus

  // モーダルが閉じられた場合、表示するカラムをもとに戻す
  $("#MessageModal").on("hidden.bs.modal",()=>{
    // $('#'+editAPI.physical_name+'-error').text("")
    $("#MessageModal").off("hidden.bs.modal")
  })

  return (
    <>
    {/* Content Header (Page header) */}
    <div className="modal fade" id="MessageModal">
      <div className="modal-dialog modal-xl">
        <div className="modal-content">
          <div className="modal-header">
            <h4 className="modal-title">サンプルデータ挿入 / API連携項目追加</h4>
          </div>
          <div className="edit-api-disp-grid edit-api-grid">
              <div className="api-name edit-logical-input-form-title">
                <div className="dtl-content-width">API 論理名</div>
                完成品販売計画(年次)
              </div>
              <div className="api-name edit-physical-input-form-title">
                <div className="dtl-content-width">API 物理名</div>
                product_sales_y_plan
              </div>
              {Spacer({"size":30})}
            </div>
          <div className="modal-body">
            <div className="dividing-line"/>
          </div>
          <div className="modal-body">

            <div className="text-center">
              <div className="text-center">
                <div className="text-center"><p>API項目が更新されました。</p><p>再度マッピングを行う必要があります。</p></div>
              </div>
            </div>
            {Spacer({"size":25})}
            <div className="text-center">
              <div className="centering-btn">
                <button className="btn btn-primary">
                  OK
                </button>
              </div>
            </div>
            {Spacer({"size":25})}
          </div>
        </div>
        {/* <!-- /.modal-content --> */}
      </div>
      {/* <!-- /.modal-dialog --> */}
    </div>
    {/* <!-- /.modal --> */}
    {/* /.content */}
  </>
  )
}

export default MessageModal;
kirin-ri commented 1 year ago

キャプチャ