Open kirin-ri opened 1 year ago
メッセージモーダル部分が動かないため、修正してください。
<div>
販売計画誤差率:年次
</div>
販売計画誤差率:年次を押して、別ページでリンク先が開くように修正してください。
開いたモーダルがモーダル以外の部分をクリックしたら、モーダルが閉じれます。 閉じれないように修正してください。
{
$("#"+editAPI.physical_name+"-dtl-modal").modal("hide").on("hidden.bs.modal",()=>{
setTimeout(()=>$("#MessageModal").modal("show"))
$("#"+editAPI.physical_name+"-dtl-modal").off("hidden.bs.modal")
})
}
<button className="btn btn-primary" data-backdrop="false" data-target='#MessageModal'
onClick={()=>{
let flg = true
editAPI.outinfo[0].column.map((item:Defs.EditColumn)=>{
if(!item.delete){
flg = false
setReflectLock(false)
}
})
if(!flg){
putChangeAPI()
//メッセージモーダル
// if (updated){
{
$("#"+editAPI.physical_name+"-dtl-modal").modal("hide").on("hidden.bs.modal",()=>{
setTimeout(()=>$("#MessageModal").modal({backdrop:"static"}).modal("show"))
$("#"+editAPI.physical_name+"-dtl-modal").off("hidden.bs.modal")
})
}
}else{
$('#'+editAPI.physical_name+'-error').text("連携項目は1つ以上必要です")
}
}
}>
反映する
</button>
モーダル"#MessageModalがまだ閉じられます。
<button className="btn btn-primary"
data-toggle="modal"
data-target="#MessageModal"
data-backdrop="static"
onClick={()=>{
let flg = true
editAPI.outinfo[0].column.map((item:Defs.EditColumn)=>{
if(!item.delete){
flg = false
setReflectLock(false)
}
})
if(!flg){
putChangeAPI()
//メッセージモーダル
if (updated) {
{
$("#"+editAPI.physical_name+"-dtl-modal").modal("hide").on("hidden.bs.modal",()=>{
setTimeout(()=>$("#MessageModal").modal("show"))
$("#"+editAPI.physical_name+"-dtl-modal").off("hidden.bs.modal")
})
}
}else{
$('#'+editAPI.physical_name+'-error').text("連携項目は1つ以上必要です")
}
}
}}>
反映する
</button>
updatedがfalseの場合でも#MessageModalモーダルが出ました。なぜ?
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)
});
})();
ここに通信してきた戻り値を取得したい
バックエンドから以下を戻り値にしています。
res = {"meta": ["販売計画誤差率:年次","基準在庫(在庫数・金額):年次"]}
return res
res.data.metaがある度、listに挿入するコードは以下ですが、うまくいかないため、修正をお願い致します。
commonAjax
.axios({swalFire: true, loading: true})
.put(`/api/api/${editAPI.physical_name}`, apiList.outinfo[0].column)
.then((res) => {
const list = [];
list.add(res.data.meta)
apiList.outinfo[0].column.map((col:Defs.EditColumn)=>{
col["delete"] = false;
col["editing"] = false;
col["edited"] = false;
}
)
setEditAPI(apiList)
setUpdated(true)
});
})();
list.push(res.data.meta);
setList(list);
listを格納できるuseStateを定義してください。
const [List, setList] = useState<>();
listは['販売計画誤差率:年次', '基準在庫(在庫数・金額):年次']です。
const [List, setList] = useState([]);だとlistが入れない
<button className="btn btn-primary"
data-toggle="modal"
data-backdrop="static"
onClick={()=>{
let flg = true
editAPI.outinfo[0].column.map((item:Defs.EditColumn)=>{
if(!item.delete){
flg = false
setReflectLock(false)
}
})
if(!flg){
putChangeAPI()
if (updated) {
$("#"+editAPI.physical_name+"-dtl-modal").modal("hide").on("hidden.bs.modal",()=>{
setTimeout(()=>$("#"+editAPI.physical_name+"MessageModal").modal({backdrop: "static"}).modal("show"))
$("#"+editAPI.physical_name+"-dtl-modal").off("hidden.bs.modal")
})
}
}else{
$('#'+editAPI.physical_name+'-error').text("連携項目は1つ以上必要です")
}
}}>
反映する
</button>
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>("")
const [List, setList] = useState<any>();
// 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: false, loading: true})
.put(`/api/api/${editAPI.physical_name}`, apiList.outinfo[0].column)
.then((res) => {
const list = [];
list.push(res.data.meta);
console.log(list)
setList(list)
console.log(List)
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">×</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-backdrop="static"
onClick={()=>{
let flg = true
editAPI.outinfo[0].column.map((item:Defs.EditColumn)=>{
if(!item.delete){
flg = false
setReflectLock(false)
}
})
if(!flg){
putChangeAPI()
if (updated) {
$("#"+editAPI.physical_name+"-dtl-modal").modal("hide").on("hidden.bs.modal",()=>{
setTimeout(()=>$("#"+editAPI.physical_name+"MessageModal").modal({backdrop: "static"}).modal("show"))
$("#"+editAPI.physical_name+"-dtl-modal").off("hidden.bs.modal")
})
}
}else{
$('#'+editAPI.physical_name+'-error').text("連携項目は1つ以上必要です")
}
}}>
反映する
</button>
</div>
{Spacer({"size":30})}
</div>
</div>
{/* <!-- /.modal-content --> */}
</div>
{/* <!-- /.modal-dialog --> */}
</div>
{/* <!-- /.modal --> */}
</>
)
}
export default NewAPIDtlModal;
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>("")
//const [List, setList] = useState<any>();
// 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: false, loading: true})
.put(`/api/api/${editAPI.physical_name}`, apiList.outinfo[0].column)
.then((res) => {
// const list = [];
// list.push(...res.data.meta);
// setList(list)
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">×</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()
$("#"+editAPI.physical_name+"-dtl-modal").modal("hide").on("hidden.bs.modal",()=>{
setTimeout(()=>$("#"+editAPI.physical_name+"MessageModal").modal({backdrop: "static"}).modal("show"))
$("#"+editAPI.physical_name+"-dtl-modal").off("hidden.bs.modal")
})
}else{
$('#'+editAPI.physical_name+'-error').text("連携項目は1つ以上必要です")
}
}}>
反映する
</button>
</div>
{Spacer({"size":30})}
</div>
</div>
{/* <!-- /.modal-content --> */}
</div>
{/* <!-- /.modal-dialog --> */}
</div>
{/* <!-- /.modal --> */}
</>
)
}
export default NewAPIDtlModal;
{"values":[{"logicalName":"販売計画誤差率:年次","logicalName":"基準在庫(在庫数・金額):年次"}]}
res = {"values":[{"logicalName":"販売計画誤差率:年次"},{"logicalName":"基準在庫(在庫数・金額):年次"}]}
let resData = res.data; // Assuming this is your response object
resData.values.forEach((value: {logicalName: string}) => {
apiList.outinfo[0].column.push({logicalName: value.logicalName, delete: false, editing: false, edited: false})
})
[{…}, {…}]
プロダクトメトリクス 収益
プロダクトによって得られる収益
市場占有率
プロダクトが市場を占める相対的な割合
顧客満⾜度
プロダクトによって顧客の課題が解決された状態、もしくは、あるべき理想の状態、その満足度
顧客使⽤指標
プロダクトに対する、熱心さ・意欲・モチベーション・使い倒し度合い
利用/復帰顧客数
プロダクトの新規顧客や機能を新たに使い始めた、復帰した顧客数
タスク成功率
プロダクトの機能の達成度合いを示すもの。効率性、有効性、エラー率など
プロセスメトリクス デプロイの頻度
組織による正常な本番環境へのリリースの頻度
リードタイム
コミットから本番環境稼働までの所要時間
変更障害率
デプロイが原因で本番環境で障害が発生する割合
サービス復元時間
組織が本番環境での障害から回復するのにかかる時間
手戻り率
プロセスの前段階に戻ったタスクの割合
プルリク数
タスクのプルリクエスト数
従業員満足度
チームメンバーの仕事に対する満足度
snowflakeのデータベースで、カラムrelated_metricsに以下のjsonが格納しています。
[
{
"column": [
{
"logicalName": "一次部品(部品番号)",
"physicalName": "PARTS_NO",
"type": "文字列"
},
{
"logicalName": "基準日",
"physicalName": "REF_DATE",
"type": "日付"
},
{
"logicalName": "部品在庫欠品:欠品数",
"physicalName": "OUT_OF_STOCK_QTY",
"type": "数値"
},
{
"logicalName": "部品在庫欠品:欠品率",
"physicalName": "OUT_OF_STOCK_RATE",
"type": "数値"
},
{
"logicalName": "実行日",
"physicalName": "EXEC_DATE",
"type": "日付"
},
{
"logicalName": "ユニークキー",
"physicalName": "UID",
"type": "文字列"
}
]
}
]
import json
from tools.snowflakeAccessor import SnowflakeAccessor
def getApiList(id: str):
sf = SnowflakeAccessor()
out = []
# メタデータ取得クエリ
query = """
SELECT
API_PHYSICAL_NAME,
API_LOGICAL_NAME,
PROVIDE,
API_LINKAGE_ITEMS,
TAGS
FROM
API_META_INFO
ORDER BY
API_PHYSICAL_NAME
"""
try:
result = sf.execute(query)
for (physical_name, logical_name, provide, items, tags) in result:
outinfo = []
column = []
related_metrics = []
try:
for item in json.loads(items):
outinfo.append(
{
"physicalName": item["PHYSICAL_NAME"],
"logicalName": item["LOGICAL_NAME"],
"type": item["TYPE"],
"required": item["REQUIRED"],
}
)
except TypeError:
outinfo.append(None)
column.append({"column": outinfo})
if tags == "[]" or not tags:
tags = '["その他"]'
#dami
related_metrics.append()
out.append(
{
"related_metrics" : related_metrics,
"physical_name": physical_name,
"logical_name": logical_name,
"provide": provide,
"outinfo": column,
"tags": json.loads(tags)
}
)
# print({"api": out})
finally:
sf.close()
return {"api": out}
現在の状況はベタ書きです
販売計画誤差率:年次と基準在庫(在庫数・金額):年次の文字列は、test=['販売計画誤差率:年次', 基準在庫(在庫数・金額):年次の文字列']になってます。
下の表示は配列数がある度<div></div>を生成するように修正してください。
<div>
<div className="text-center">
<div>
-<a href="/#/scm-metrics-edit/販売計画誤差率:年次" target="_blank">販売計画誤差率:年次</a>
</div>
<div>
-<a href="/#/scm-metrics-edit/基準在庫(在庫数・金額):年次" target="_blank">基準在庫(在庫数・金額):年次 </a>
</div>
</div>
</div>
<div>
<div className="text-center">
{test.map((item, index) => (
<div key={index}>
-<a href={"/#/scm-metrics-edit/" + item} target="_blank">{item}</a>
</div>
))}
</div>
</div>
import React, { ChangeEvent, useEffect, useState } from "react";
import { useForm } from 'react-hook-form';
import { Spacer } from "../spacer";
import { commonAjax } from '../../../components/commonAjax';
import * as Defs from './apiDefs';
// Message Modal
const ApiUpdateResultModal = (props: { val: Defs.Api}) => {
const tmp = JSON.parse(JSON.stringify(props.val))
const [editAPI, setEditAPI] = useState<Defs.Api>(tmp);
return (
<>
{/* Content Header (Page header) */}
{/* <div className="modal fade" id={"MessageModal"}> */}
<div className="modal fade" data-backdrop="static" id={editAPI.physical_name+"MessageModal"}>
<div className="modal-dialog modal-xl">
<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">×</span>
</button>
</div>
<div className="modal-body">
<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>
</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">
<div>API項目が更新されました。</div>
<div>再度マッピングを行う必要があります。</div>
</div>
</div>
</div>
{Spacer({"size":50})}
<div>
<div className="text-center">
{editAPI.related_metrics.map((item, index) => (
<div key={index}>
<div style={{margin:"0 auto",textAlign:"left"}}>
-<a href={"/#/scm-metrics-edit/" + item} target="_blank">{item}</a>
</div>
</div>
))}
</div>
</div>
{Spacer({"size":50})}
<div className="text-center">
<div className="centering-btn">
<div style={{color : "red"}}>
※マッピングが完了するまで、このページを閉じないでください。
</div>
{Spacer({"size":25})}
<button className="btn btn-primary" data-dismiss="modal">
OK
</button>
</div>
</div>
{Spacer({"size":25})}
</div>
</div>
{/* <!-- /.modal-content --> */}
</div>
{/* <!-- /.modal-dialog --> */}
</div>
{/* <!-- /.modal --> */}
{/* /.content */}
</>
)
}
export default ApiUpdateResultModal;
再度マッピングを行う必要があります。 -基準在庫(在庫数・金額):年次 -販売計画誤差率:年次
<div key={index}>
<div style={{margin:"0 auto",textAlign:"left"}}>
-<a href={"/#/scm-metrics-edit/" + item} target="_blank">{item}</a>
</div>
</div>
<span style={{ display: "inline-block" }}>
<span style={{ whiteSpace: "nowrap" }}>-</span>
<span style={{ visibility: "hidden" }}> </span>
</span>
<div key={index} style={{ display: "flex", justifyContent: "center" }}> <div style={{ textAlign: "left" }}> <span style={{ marginRight: "5px" }}>- <a href={"/#/scm-metrics-edit/" + item} target="_blank">{item}
<div key={index} style={{ display: "flex", justifyContent: "center" }}>
<div style={{ textAlign: "left" }}>
<span style={{ marginRight: "5px" }}>-</span>
<a href={"/#/scm-metrics-edit/" + item} target="_blank">{item}</a>
</div>
</div>
<div>
<div className="text-center">
{editAPI.related_metrics.map((item, index) => (
<div key={index} style={{ display: "flex", justifyContent: "center" }}>
<div style={{ textAlign: "left" }}>
<span style={{ marginRight: "5px" }}>-</span>
<a href={"/#/scm-metrics-edit/" + item} target="_blank">{item}</a>
</div>
</div>
))}
</div>
</div>
<div>
<div className="text-center">
{editAPI.related_metrics.map((item, index) => (
<div key={index} style={{ display: "flex", justifyContent: "center" }}>
<div style={{ textAlign: "left", margin: "0 auto" }}>
<span style={{ marginRight: "5px" }}>-</span>
<a href={"/#/scm-metrics-edit/" + item} target="_blank">{item}</a>
</div>
</div>
))}
</div>
</div>
<div>
<div className="text-center">
{editAPI.related_metrics.map((item, index) => (
<div key={index} style={{ display: "flex", justifyContent: "center" }}>
<div style={{ textAlign: "left", margin: "0 auto", display: "flex", alignItems: "center" }}>
<span style={{ marginRight: "5px", alignSelf: "flex-start" }}>-</span>
<a href={"/#/scm-metrics-edit/" + item} target="_blank">{item}</a>
</div>
</div>
))}
</div>
</div>
<div className="text-center">
<div className="text-center">
<div className="text-center">
<div>API項目が更新されました。</div>
<div>再度マッピングを行う必要があります。</div>
</div>
</div>
</div>
<div className="text-center">
<div className="text-center">
<div className="text-center">
<div style={{ display: "flex", justifyContent: "center", flexDirection: "column" }}>
<div style={{ textAlign: "center" }}>API項目が更新されました。</div>
<div style={{ textAlign: "center" }}>再度マッピングを行う必要があります。</div>
</div>
</div>
</div>
</div>
<div className="text-center">
{editAPI.related_metrics.map((item, index) => (
<div key={index}>
<div style={{ textAlign: "center", margin: "0 auto" }}>
-<a href={"/#/scm-metrics-edit/" + item} target="_blank">{item}</a>
</div>
</div>
))}
</div>
<div className="text-center">
{editAPI.related_metrics.map((item, index) => (
<div key={index} style={{ textAlign: "center" }}>
-<a
href={"/#/scm-metrics-edit/" + item}
target="_blank"
style={{ display: "inline-block", textAlign: "left" }}
>
{item}
</a>
</div>
))}
</div>
import React, { ChangeEvent, useEffect, useState } from "react";
import { useForm } from 'react-hook-form';
import { Spacer } from "../spacer";
import { commonAjax } from '../../../components/commonAjax';
import * as Defs from './apiDefs';
// Message Modal
const ApiUpdateResultModal = (props: { val: Defs.Api}) => {
const tmp = JSON.parse(JSON.stringify(props.val))
const [editAPI, setEditAPI] = useState<Defs.Api>(tmp);
return (
<>
{/* Content Header (Page header) */}
{/* <div className="modal fade" id={"MessageModal"}> */}
<div className="modal fade" data-backdrop="static" id={editAPI.physical_name+"MessageModal"}>
<div className="modal-dialog modal-xl">
<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">×</span>
</button>
</div>
<div className="modal-body">
<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>
</div>
<div className="modal-body">
<div className="dividing-line"/>
</div>
<div className="modal-body">
<div className="text-center">
<div>API項目が更新されました。</div>
<div>再度マッピングを行う必要があります。</div>
</div>
</div>
<div className="modal-body">
{Spacer({"size":50})}
<div className="text-center">
{editAPI.related_metrics.map((item, index) => (
<div key={index} style={{textAlign:"center"}}>
-<a href={"/#/scm-metrics-edit/" + item} target="_blank" style={{display:"inline-block",textAlign:"left"}}>{item}</a>
</div>
))}
</div>
{Spacer({"size":50})}
<div className="text-center">
<div className="centering-btn">
<div style={{color : "red"}}>
※マッピングが完了するまで、このページを閉じないでください。
</div>
{Spacer({"size":25})}
<button className="btn btn-primary" data-dismiss="modal">
OK
</button>
</div>
</div>
{Spacer({"size":25})}
</div>
</div>
{/* <!-- /.modal-content --> */}
</div>
{/* <!-- /.modal-dialog --> */}
</div>
{/* <!-- /.modal --> */}
{/* /.content */}
</>
)
}
export default ApiUpdateResultModal;