Skip to content

Commit

Permalink
Merge pull request #7 from ridhlab/feat/result
Browse files Browse the repository at this point in the history
Feat/result
  • Loading branch information
ridhlab authored Jan 2, 2024
2 parents de9ae5e + e78a156 commit 8d75135
Show file tree
Hide file tree
Showing 14 changed files with 554 additions and 2 deletions.
11 changes: 11 additions & 0 deletions src/common/breadcrumb.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,5 +55,16 @@ export const Breadcrumbs = {
},
Result: {
Index: () => [{ label: "Result Predict", href: Routes.ResultIndex }],
Create: () => [
{ label: "Result Predict", href: Routes.ResultIndex },
{ label: "Predict", href: Routes.ResultCreate },
],
Detail: (id) => [
{ label: "Result Predict", href: Routes.ResultIndex },
{
label: "Detail",
href: parsingRoute(Routes.ResultDetail, { id }),
},
],
},
};
67 changes: 67 additions & 0 deletions src/components/modules/Result/ModalResultPredict.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import Button from "@/components/shared/Button/Button";
import { getDecimalPlace } from "@/helpers/number";
import { parsingRoute } from "@/helpers/route";
import { IPredict } from "@/interfaces/responses/Result";
import { Routes } from "@/routes/routes";
import { EyeOutlined } from "@ant-design/icons";
import { Modal, Space, Table } from "antd";
import { ColumnsType } from "antd/es/table";
import React from "react";

interface IProps {
open: boolean;
closeModal: () => void;
dataPredict: IPredict;
}

const ModalResultPredict: React.FC<IProps> = ({
open,
closeModal,
dataPredict,
}) => {
const footer = (
<Space>
<Button onClick={closeModal}>Close</Button>
<Button
icon={<EyeOutlined />}
href={parsingRoute(Routes.ResultDetail, {
id: dataPredict?.resultId,
})}
>
See Detail
</Button>
</Space>
);

const columns: ColumnsType = [
{
title: "Variable Output",
dataIndex: "variableOutputName",
},
{
title: "Value",
dataIndex: "value",
render: (value) => getDecimalPlace(value, 3),
},
];

return (
<Modal
open={open}
centered
closable={false}
title={`Result predict for ${dataPredict?.name}`}
footer={footer}
>
<Table
bordered
pagination={false}
size="small"
dataSource={dataPredict?.predict}
columns={columns}
/>
</Modal>
);
};

export default ModalResultPredict;
10 changes: 10 additions & 0 deletions src/interfaces/entities/InputValues/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { IVariableInput } from "../VariableInput";
import { IBaseEntity } from "../base";

export interface IInputValue extends IBaseEntity {
id: number;
value: number;
resultId: number;
variableInputId: number;
variableInput?: IVariableInput;
}
11 changes: 11 additions & 0 deletions src/interfaces/entities/Results/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { IInputValue } from "../InputValues";
import { IUser } from "../Users";
import { IBaseEntity } from "../base";

export interface IResult extends IBaseEntity {
id: number;
name: string;
userId: string;
user?: IUser;
inputValues?: IInputValue[];
}
4 changes: 4 additions & 0 deletions src/interfaces/requests/Result/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export interface IPredictRequest {
name: string;
dataInput: { variableInputId: number; value: number }[];
}
26 changes: 26 additions & 0 deletions src/interfaces/responses/Result/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { IResult } from "@/interfaces/entities/Results";
import { IBaseResponse } from "../base";

export interface IResultListResponse extends IBaseResponse<IResult[]> {}
export interface IResultDetailResponse extends IBaseResponse<IResult> {}

export interface IPredict {
resultId: number;
name: string;
predict: {
variableOutputId: number;
variableOutputName: string;
value: number;
}[];
}
export interface IResultPredictResponse extends IBaseResponse<IPredict> {}

export interface IResultPredictByResultIdResponse
extends IBaseResponse<{
result: IResult;
predict: {
variableOutputId: number;
variableOutputName: string;
value: number;
}[];
}> {}
152 changes: 152 additions & 0 deletions src/pages/Result/Create.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
import { Breadcrumbs } from "@/common/breadcrumb";
import ModalResultPredict from "@/components/modules/Result/ModalResultPredict";
import Button from "@/components/shared/Button/Button";
import ButtonAction from "@/components/shared/Form/ButtonActions";
import LoaderCenter from "@/components/shared/Loader/LoaderCenter";
import { getRequiredMessage } from "@/helpers/form";
import { modalConfirm } from "@/helpers/modal-confirm";
import { useFormUtility } from "@/hooks/useFormUtility";
import { IPredictRequest } from "@/interfaces/requests/Result";
import { IPredict } from "@/interfaces/responses/Result";
import MainLayout from "@/layouts/MainLayout";
import { Routes } from "@/routes/routes";
import { useResultPredictMutation } from "@/services/mutation/Result";
import { useVariableInputQueryIndex } from "@/services/query/VariableInput";
import { Card, Flex, Form, Input, InputNumber } from "antd";
import React from "react";
import * as yup from "yup";

const schema = yup.object().shape({
name: yup.string().required(getRequiredMessage("Name")),
dataInput: yup
.array()
.of(
yup.object().shape({
variableInputId: yup
.number()
.required(getRequiredMessage("Variable Input"))
.defined(),
value: yup
.number()
.required(getRequiredMessage("Value"))
.defined(),
})
)
.required(),
});

const ResultCreate: React.FC = () => {
const { form, yupSync } = useFormUtility({ schema });
const queryVariableInput = useVariableInputQueryIndex();

const [openModalResultPredict, setOpenModalResultPredict] =
React.useState(false);
const [resultPredict, setResultPredict] = React.useState<IPredict>(null);

const mutationPredict = useResultPredictMutation({
onSuccess: ({ data }) => {
setOpenModalResultPredict(true);
setResultPredict(data);
},
});

const closeModal = () => {
setOpenModalResultPredict(false);
setResultPredict(null);
};

const onFinish = async () => {
await form.validateFields();
modalConfirm({
onOk: () => {
const dataSubmitted = form.getFieldsValue();
mutationPredict.mutate(dataSubmitted as IPredictRequest);
},
});
};

const getLabelOutput = (idContext) => {
return queryVariableInput?.data?.data?.find(
({ id }) => id === idContext
)?.name;
};

const formListDataInput = (
<Form.List name="dataInput">
{(fields) =>
fields.map((field) => (
<React.Fragment key={field.key}>
<Form.Item
required
label={getLabelOutput(
form.getFieldValue([
"dataInput",
field.name,
"variableInputId",
])
)}
name={[field.name, "value"]}
rules={[yupSync]}
>
<InputNumber />
</Form.Item>
<Form.Item
name={[field.name, "variableInputId"]}
></Form.Item>
</React.Fragment>
))
}
</Form.List>
);

return (
<MainLayout title="Predict" breadcrumbs={Breadcrumbs.Result.Create()}>
<Card>
{queryVariableInput.isLoading ||
queryVariableInput.isFetching ? (
<LoaderCenter />
) : (
<Form
form={form}
onFinish={onFinish}
initialValues={{
name: "",
dataInput: queryVariableInput?.data?.data?.map(
({ id }) => ({ variableInputId: id, value: 0 })
),
}}
>
<Form.Item
required
label="Name"
name="name"
rules={[yupSync]}
>
<Input />
</Form.Item>
<Flex wrap="wrap" style={{ columnGap: "1rem" }}>
{formListDataInput}
</Flex>
<ButtonAction
actions={[
<Button href={Routes.ResultIndex}>
Cancel
</Button>,
<Button type="primary" htmlType="submit">
Predict
</Button>,
]}
/>
</Form>
)}
</Card>
<ModalResultPredict
open={openModalResultPredict}
closeModal={closeModal}
dataPredict={resultPredict}
/>
</MainLayout>
);
};

export default ResultCreate;
77 changes: 77 additions & 0 deletions src/pages/Result/Detail.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { Breadcrumbs } from "@/common/breadcrumb";
import LoaderCenter from "@/components/shared/Loader/LoaderCenter";
import { getDecimalPlace } from "@/helpers/number";
import { IInputValue } from "@/interfaces/entities/InputValues";
import MainLayout from "@/layouts/MainLayout";
import { useResultPredictByresultId } from "@/services/query/Result";
import { Card, Col, Row, Table } from "antd";
import { ColumnsType } from "antd/es/table";
import React from "react";
import { useParams } from "react-router-dom";

const DetailResultPredict: React.FC = () => {
const { id } = useParams();
const query = useResultPredictByresultId(id);
const columnsResultPredict: ColumnsType = [
{
title: "Variable Output",
dataIndex: "variableOutputName",
},
{
title: "Value",
dataIndex: "value",
render: (value) => getDecimalPlace(value, 3),
},
];

const columnsVariableInput: ColumnsType<IInputValue> = [
{
title: "Variable Input",
render: (_, record) => record?.variableInput?.name,
},
{
title: "Value",
render: (_, record) => record?.value,
},
];

const mainContent = (
<Row gutter={20} justify="center">
<Col>
<Table
pagination={false}
bordered
size="small"
dataSource={query?.data?.data?.result?.inputValues}
columns={columnsVariableInput}
/>
</Col>
<Col>
<Table
pagination={false}
bordered
size="small"
dataSource={query?.data?.data?.predict}
columns={columnsResultPredict}
/>
</Col>
</Row>
);

return (
<MainLayout
title="Detail Predict"
breadcrumbs={Breadcrumbs.Result.Detail(id)}
>
<Card>
{query?.isLoading || query?.isFetching ? (
<LoaderCenter />
) : (
mainContent
)}
</Card>
</MainLayout>
);
};

export default DetailResultPredict;
Loading

0 comments on commit 8d75135

Please sign in to comment.