Merge remote-tracking branch 'origin/develop'

# Conflicts:
#	src/component/BreadcumbComponent.js
#	src/component/ProductComponent.js
#	src/config/app.js
#	src/index.css
#	src/pages/App/App.js
#	src/pages/App/DesktopLayout.js
#	src/pages/App/MenuList.js
#	src/pages/Home/Home.js
#	src/pages/Login/Login.js
#	src/pages/Membership/Membership.js
#	src/pages/Membership/MembershipModal.js
#	src/pages/Product/Product.js
#	src/pages/Transaction/Pulsa.js
#	src/pages/Transaction/Transaction.js
#	src/store/authentication.js
#	src/store/index.js
#	src/store/membership.js
#	src/store/product.js
#	src/store/role.js
#	src/store/ui.js
#	src/store/user.js
#	src/utils/http.js
This commit is contained in:
ilham 2022-01-28 15:58:48 +07:00
commit a4160ae2bd
76 changed files with 14076 additions and 5390 deletions

1
.dockerignore Normal file
View File

@ -0,0 +1 @@
node_modules

1
.env Normal file
View File

@ -0,0 +1 @@
process.env.NEXT_PUBLIC_BASE_URL=https://ppob-backend.k3s.bangun-kreatif.com/v1

1
.gitignore vendored
View File

@ -24,3 +24,4 @@ yarn-error.log*
/.idea /.idea
/package-lock.json /package-lock.json

68
.gitlab-ci.yml Normal file
View File

@ -0,0 +1,68 @@
variables:
REGISTRY_URL: registry-harbor.app.bangun-kreatif.com
REGISTRY_IMAGE: $REGISTRY_URL/empatnusabangsa/ppob-frontend
VERSION_STAGING: $CI_COMMIT_REF_NAME-$CI_PIPELINE_ID-$CI_COMMIT_SHORT_SHA
VERSION_PRODUCTION: $CI_COMMIT_REF_NAME-$CI_PIPELINE_ID-$CI_COMMIT_SHORT_SHA-production
DOCKER_HOST: tcp://docker:2375
DOCKER_TLS_CERTDIR: ""
DOCKER_DRIVER: overlay2
stages:
- build-staging
- deploy-staging
- build-production
- deploy-production
build-staging:
stage: build-staging
image: appuio/gitlab-runner-oc:3.11.0
only:
- devops-staging
services:
- docker:dind
script:
- docker login $REGISTRY_URL -u $BKA_REGISTRY_USER -p $BKA_REGISTRY_PASS
- docker build -t $REGISTRY_IMAGE:$VERSION_STAGING .
- docker push $REGISTRY_IMAGE:$VERSION_STAGING
- docker rmi $REGISTRY_IMAGE:$VERSION_STAGING
deploy-staging:
stage: deploy-staging
image: dtzar/helm-kubectl
only:
- devops-staging
script:
- kubectl config set-cluster k8s --server="${BKA_CLUSTER_HOST}"
- kubectl config set clusters.k8s.certificate-authority-data ${BKA_CLUSTER_CA}
- kubectl config set-credentials gitlab --token="${BKA_CLUSTER_TOKEN}"
- kubectl config set-context default --cluster=k8s --user=gitlab
- kubectl config use-context default
- sed -i "s/<VERSION>/${VERSION_STAGING}/g" k8s/staging/deployment.yaml
- kubectl apply -f k8s/staging/deployment.yaml
build-production:
stage: build-production
image: appuio/gitlab-runner-oc:3.11.0
only:
- devops-production
services:
- docker:dind
script:
- docker login $REGISTRY_URL -u $BKA_REGISTRY_USER -p $BKA_REGISTRY_PASS
- docker build -t $REGISTRY_IMAGE:$VERSION_PRODUCTION .
- docker push $REGISTRY_IMAGE:$VERSION_PRODUCTION
- docker rmi $REGISTRY_IMAGE:$VERSION_PRODUCTION
deploy-production:
stage: deploy-production
image: dtzar/helm-kubectl
only:
- devops-production
script:
- kubectl config set-cluster k8s --server="${BKA_CLUSTER_HOST}"
- kubectl config set clusters.k8s.certificate-authority-data ${BKA_CLUSTER_CA}
- kubectl config set-credentials gitlab --token="${BKA_CLUSTER_TOKEN}"
- kubectl config set-context default --cluster=k8s --user=gitlab
- kubectl config use-context default
- sed -i "s/<VERSION>/${VERSION_PRODUCTION}/g" k8s/production/deployment.yaml
- kubectl apply -f k8s/production/deployment.yaml

12
Dockerfile Normal file
View File

@ -0,0 +1,12 @@
FROM node:14-alpine as build-deps
WORKDIR /usr/src/app
COPY package.json yarn.lock ./
RUN yarn
COPY . ./
RUN yarn build
FROM nginx:stable-alpine
COPY --from=build-deps /usr/src/app/build /usr/share/nginx/html
COPY nginx/nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

View File

@ -0,0 +1,22 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: ppob-frontend
namespace: empatnusabangsa-production
spec:
selector:
matchLabels:
app: ppob-frontend
replicas: 1
template:
metadata:
labels:
app: ppob-frontend
spec:
containers:
- name: ppob-frontend
image: registry-harbor.app.bangun-kreatif.com/empatnusabangsa/ppob-frontend:<VERSION>
ports:
- containerPort: 80
imagePullSecrets:
- name: regcred

View File

@ -0,0 +1,25 @@
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ppob-frontend-ingress
namespace: empatnusabangsa-production
annotations:
kubernetes.io/ingress.class: "traefik"
cert-manager.io/cluster-issuer: letsencrypt-prod
traefik.ingress.kubernetes.io/router.middlewares: empatnusabangsa-production-redirect-https@kubernetescrd
spec:
rules:
- host: "www.wndsolutions.id"
http:
paths:
- pathType: Prefix
path: /
backend:
service:
name: ppob-frontend
port:
number: 80
tls:
- hosts:
- "www.wndsolutions.id"
secretName: www-wndsolutions-id-tls

View File

@ -0,0 +1,9 @@
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
name: redirect-https
namespace: empatnusabangsa-production
spec:
redirectScheme:
scheme: https
permanent: true

View File

@ -0,0 +1,4 @@
apiVersion: v1
kind: Namespace
metadata:
name: empatnusabangsa-staging

View File

@ -0,0 +1,13 @@
apiVersion: v1
kind: Service
metadata:
name: ppob-frontend
namespace: empatnusabangsa-production
labels:
run: ppob-frontend
spec:
ports:
- port: 80
protocol: TCP
selector:
app: ppob-frontend

View File

@ -0,0 +1,22 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: ppob-frontend
namespace: empatnusabangsa-staging
spec:
selector:
matchLabels:
app: ppob-frontend
replicas: 1
template:
metadata:
labels:
app: ppob-frontend
spec:
containers:
- name: ppob-frontend
image: registry-harbor.app.bangun-kreatif.com/empatnusabangsa/ppob-frontend:<VERSION>
ports:
- containerPort: 80
imagePullSecrets:
- name: regcred

25
k8s/staging/ingress.yaml Normal file
View File

@ -0,0 +1,25 @@
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ppob-frontend-ingress
namespace: empatnusabangsa-staging
annotations:
kubernetes.io/ingress.class: "traefik"
cert-manager.io/cluster-issuer: letsencrypt-prod
traefik.ingress.kubernetes.io/router.middlewares: empatnusabangsa-staging-redirect-https@kubernetescrd
spec:
rules:
- host: "ppob-frontend.k3s.bangun-kreatif.com"
http:
paths:
- pathType: Prefix
path: /
backend:
service:
name: ppob-frontend
port:
number: 80
tls:
- hosts:
- "ppob-frontend.k3s.bangun-kreatif.com"
secretName: ppob-frontend-k3s-bangun-kreatif-com-tls

View File

@ -0,0 +1,9 @@
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
name: redirect-https
namespace: empatnusabangsa-staging
spec:
redirectScheme:
scheme: https
permanent: true

View File

@ -0,0 +1,4 @@
apiVersion: v1
kind: Namespace
metadata:
name: empatnusabangsa-staging

13
k8s/staging/service.yaml Normal file
View File

@ -0,0 +1,13 @@
apiVersion: v1
kind: Service
metadata:
name: ppob-frontend
namespace: empatnusabangsa-staging
labels:
run: ppob-frontend
spec:
ports:
- port: 80
protocol: TCP
selector:
app: ppob-frontend

0
kern.log Normal file
View File

17
nginx/nginx.conf Normal file
View File

@ -0,0 +1,17 @@
server {
listen 80;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}

View File

@ -5,6 +5,9 @@
"dependencies": { "dependencies": {
"@ant-design/icons": "^4.6.2", "@ant-design/icons": "^4.6.2",
"@babel/core": "7.14.5", "@babel/core": "7.14.5",
"@fortawesome/fontawesome-svg-core": "^1.2.36",
"@fortawesome/free-solid-svg-icons": "^6.0.0-beta3",
"@fortawesome/react-fontawesome": "^0.1.16",
"@pmmmwh/react-refresh-webpack-plugin": "0.4.3", "@pmmmwh/react-refresh-webpack-plugin": "0.4.3",
"@svgr/webpack": "5.5.0", "@svgr/webpack": "5.5.0",
"@testing-library/jest-dom": "^5.14.1", "@testing-library/jest-dom": "^5.14.1",
@ -72,6 +75,7 @@
"semver": "7.3.5", "semver": "7.3.5",
"style-loader": "1.3.0", "style-loader": "1.3.0",
"superagent": "^6.1.0", "superagent": "^6.1.0",
"superagent-intercept": "^0.1.2",
"terser-webpack-plugin": "4.2.3", "terser-webpack-plugin": "4.2.3",
"ts-pnp": "1.2.0", "ts-pnp": "1.2.0",
"url-loader": "4.1.1", "url-loader": "4.1.1",
@ -170,7 +174,7 @@
[ [
"@babel/plugin-proposal-class-properties", "@babel/plugin-proposal-class-properties",
{ {
"loose": false "loose": true
} }
] ]
] ]

View File

@ -26,7 +26,7 @@
work correctly both with client-side routing and a non-root public URL. work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`. Learn how to configure a non-root public URL by running `npm run build`.
--> -->
<title>React App</title> <title>PPOB</title>
</head> </head>
<body> <body>
<noscript>You need to enable JavaScript to run this app.</noscript> <noscript>You need to enable JavaScript to run this app.</noscript>

View File

@ -1,6 +1,6 @@
{ {
"short_name": "React App", "short_name": "PPOB",
"name": "Create React App Sample", "name": "PPOB",
"icons": [ "icons": [
{ {
"src": "favicon.ico", "src": "favicon.ico",

View File

@ -5,7 +5,7 @@ import {Link} from "react-router-dom";
export const BreadcumbComponent = (props) => { export const BreadcumbComponent = (props) => {
return ( return (
<div> <div>
<Breadcrumb style={{marginBottom: 10}}> <Breadcrumb>
{props.data.map((e, index) => ( {props.data.map((e, index) => (
<Breadcrumb.Item key={index}> <Breadcrumb.Item key={index}>
<Link to={e.route}> <Link to={e.route}>

View File

@ -0,0 +1,249 @@
import React, { useContext, useState } from "react";
import {
Button,
Form,
Input,
message,
Modal,
Space,
Table,
List,
Divider
} from "antd";
import { observer } from "mobx-react-lite";
import { useHistory } from "react-router-dom";
import { useStore } from "../utils/useStore";
import { LINKS } from "../routes/app";
import { ModalLoaderContext } from "../utils/modal";
export const CategoryComponent = observer((props) => {
const store = useStore();
const [form] = Form.useForm();
const history = useHistory();
const [idData, setIdData] = useState("");
const modalLoader = useContext(ModalLoaderContext);
const handleEditButton = (data) => {
console.log(data, "isi data");
form.setFieldsValue({
name: data.name,
code: data.code,
});
store.category.visibleModalCategory = true;
setIdData(data.id);
};
const columns = [
{
title: "Kode",
dataIndex: "code",
key: "code",
},
{
title: "Category Name",
dataIndex: "name",
key: "name",
},
{
title: "Action",
key: "action",
render: (text, record) => (
<Space size="middle">
<Button onClick={() => handleEditButton(record)}>Edit</Button>
</Space>
),
},
];
const deleteData = async (id) => {
try {
console.log(id);
modalLoader.setLoading(true);
await store.category.delete(id);
modalLoader.setLoading(true);
message.success("Data Berhasil Dihapus");
history.push(LINKS.PRODUCT);
} catch (err) {
console.log("error", err);
message.error("Gagal menghapus");
}
};
const handleCancel = () => {
setIdData("");
store.category.visibleModalCategory = false;
};
const handleSubmit = async (data) => {
console.log(data, "isi data2");
if (idData !== "") {
modalLoader.setLoading(true);
try {
const response = await store.category.update(idData, data);
response?.body?.statusCode === 201 || response?.body?.statusCode === 200
? message.success(
response?.body?.message || "Berhasil Ubah Data Kategori"
)
: message.error(
response?.body?.message || "Gagal Ubah Data Kategori"
);
} catch (e) {
message.error(e.response?.body?.message || "Gagal Ubah Data Kategori");
}
modalLoader.setLoading(false);
store.category.visibleModalCategory = false;
setIdData("");
form.resetFields();
} else {
modalLoader.setLoading(true);
try {
const response = await store.category.create(data);
response?.body?.statusCode === 201 || response?.body?.statusCode === 200
? message.success(
response?.body?.message || "Berhasil Tambah Kategori"
)
: message.error(response?.body?.message || "Gagal Tambah Kategori");
} catch (e) {
console.log(e, "apa errornya");
message.error(e.response?.body?.message || "Gagal Tambah Kategori");
}
modalLoader.setLoading(false);
store.category.visibleModalCategory = false;
setIdData("");
form.resetFields();
}
};
return (
<div>
{store.ui.mediaQuery.isDesktop && (
<Table
style={{ textAlign: "center" }}
columns={columns}
dataSource={store.category.data}
bordered
pagination={{
pageSize: store.category.pageSize,
total: store.category.total_data,
current: store.category.page + 1,
showSizeChanger: true,
simple: false,
}}
onChange={async (page) => {
let pageNumber = page.current;
store.category.pageSize = page.pageSize;
store.category.page = pageNumber - 1;
modalLoader.setLoading(true);
await store.category.getData();
modalLoader.setLoading(false);
}}
/>
)}
{store.ui.mediaQuery.isMobile && (
<List
itemLayout="horizontal"
position={"top"}
pagination={{
onChange: async (page, pageSize) => {
store.category.pageSize = pageSize;
store.category.page = page - 1;
modalLoader.setLoading(true);
await store.category.getData();
modalLoader.setLoading(false);
},
pageSize: store.category.pageSize,
total: store.category.total_data,
current: store.category.page + 1,
style: { marginBottom: "1rem", marginRight: "1rem" },
}}
dataSource={store.category.data}
style={{ padding: 0 }}
renderItem={(item) => {
return (
<div>
<List.Item
key={item.id}
style={{
backgroundColor: "#ffffff",
paddingTop: 0,
paddingBottom: 0,
display: "flex",
flexDirection: "row",
alignItems: "center",
justifyContent: "center",
}}
>
<List.Item.Meta
className={[""].join(" ")}
title={item.code}
description={
<div style={{}}>
<p>
<small>Category : {item.name}</small>
<br />
</p>
<p></p>
</div>
}
/>
<div style={{ marginRight: 16 }}>
<p
style={{
fontSize: 9,
margin: 0,
}}
>
<Button onClick={() => handleEditButton(item)}>
Edit
</Button>
</p>
</div>
</List.Item>
<Divider plain style={{ margin: 0 }} />
</div>
);
}}
/>
)}
<Modal
visible={store.category.visibleModalCategory}
title={idData ? "Edit Category" : "Create a new Category"}
okText={idData ? "Edit" : "Create"}
cancelText="Cancel"
onCancel={() => {
form.resetFields();
handleCancel();
}}
onOk={() => {
form
.validateFields()
.then((values) => {
console.log(values, "isi form");
handleSubmit(values);
form.resetFields();
})
.catch((info) => {
console.error("Validate Failed:", info);
});
}}
>
<Form form={form} layout="vertical">
<Form.Item
name="code"
label="Code"
rules={[{ required: true, message: "Please input name code!" }]}
>
<Input />
</Form.Item>
<Form.Item
name="name"
label="Name"
rules={[{ required: true, message: "Please input name category!" }]}
>
<Input />
</Form.Item>
</Form>
</Modal>
</div>
);
});

View File

@ -0,0 +1,126 @@
import React, { useContext, useState } from "react";
import { Button, Form, Input, message, Modal, Space, Table } from "antd";
import { observer } from "mobx-react-lite";
import { useHistory } from "react-router-dom";
import { useStore } from "../utils/useStore";
import { ModalLoaderContext } from "../utils/modal";
export const CommissionComponent = observer((props) => {
const store = useStore();
const [form] = Form.useForm();
const history = useHistory();
const [idData, setIdData] = useState("");
const modalLoader = useContext(ModalLoaderContext);
const handleEditButton = (data) => {
console.log(data, "isi data");
form.setFieldsValue({
value: data.commission,
});
store.commission.visibleModalCommission = true;
setIdData(data.id);
};
const columns = [
{
title: "Name",
dataIndex: "name",
key: "name",
},
{
title: "Amount",
dataIndex: "commission",
key: "commission",
render: (text) => <span>{text}%</span>,
},
{
title: "Action",
key: "action",
render: (text, record) => (
<Space size="middle">
<Button onClick={() => handleEditButton(record)}>Edit</Button>
</Space>
),
},
];
const handleCancel = () => {
setIdData("");
store.commission.visibleModalCommission = false;
};
const handleSubmit = async (data) => {
modalLoader.setLoading(true);
try {
const response = await store.commission.update(idData, data);
response?.body?.statusCode === 201 || response?.body?.statusCode === 200
? message.success(response?.body?.message || "Berhasil Ubah Komisi")
: message.error(response?.body?.message || "Gagal Ubah Komisi");
} catch (e) {
message.error(e.response?.body?.message || "Gagal Ubah Komisi");
}
modalLoader.setLoading(false);
store.commission.visibleModalCommission = false;
setIdData("");
form.resetFields();
};
return (
<div>
<Table
style={{ textAlign: "center" }}
columns={columns}
dataSource={store.commission.data}
bordered
pagination={{
pageSize: store.product.pageSize,
total: store.product.total_data,
current: store.product.page + 1,
showSizeChanger: true,
simple: false,
}}
onChange={async (page) => {
let pageNumber = page.current;
store.commission.pageSize = page.pageSize;
store.commission.page = pageNumber - 1;
modalLoader.setLoading(true);
await store.commission.getData();
modalLoader.setLoading(false);
}}
/>
<Modal
visible={store.commission.visibleModalCommission}
title={"Edit Commission"}
okText={"Edit"}
cancelText="Cancel"
onCancel={() => {
form.resetFields();
handleCancel();
}}
onOk={() => {
form
.validateFields()
.then((values) => {
console.log(values, "isi form");
handleSubmit(values);
form.resetFields();
})
.catch((info) => {
console.error("Validate Failed:", info);
});
}}
>
<Form form={form} layout="vertical">
<Form.Item
name="value"
label="Commission"
rules={[{ required: true, message: "Please input commission!" }]}
>
<Input />
</Form.Item>
</Form>
</Modal>
</div>
);
});

View File

@ -0,0 +1,19 @@
import React from 'react';
import {Modal, Spin} from "antd";
import {observer} from 'mobx-react-lite';
export const ModalLoader = observer(({isOpen, text}) => {
return <Modal
title={null}
footer={null}
visible={isOpen}
closable={false}
centered
style={{zIndex: 9999999999}}
>
<div style={{display: 'flex', alignItems: 'center', justifyContent: 'center', flexDirection: 'column'}}>
<Spin size={'large'}/>
<span style={{marginTop: 5}}>{text || 'Loading ...'}</span>
</div>
</Modal>
});

View File

@ -0,0 +1,471 @@
import React, { useContext, useState } from "react";
import {
Button,
Divider,
Form,
Input,
List,
message,
Modal,
Space,
Table,
Tag,
} from "antd";
import { observer } from "mobx-react-lite";
import { ExclamationCircleOutlined } from "@ant-design/icons";
import { useHistory } from "react-router-dom";
import { useStore } from "../utils/useStore";
import { LINKS } from "../routes/app";
import { ModalLoaderContext } from "../utils/modal";
export const PartnerComponent = observer((props) => {
const store = useStore();
const [value, setValue] = useState();
const [form] = Form.useForm();
const history = useHistory();
const [idData, setIdData] = useState("");
const [isChangePassword, setIsChangePassword] = useState(false);
const modalLoader = useContext(ModalLoaderContext);
const changeStatus = async (id, isActive) => {
const status = isActive ? "inactive" : "active";
const status2 = isActive ? "Inactivating" : "Activating";
modalLoader.setLoading(true);
try {
const response = await store.partner.changeStatus(id, status);
modalLoader.setLoading(false);
response?.body?.statusCode === 201
? message.success(`Success ${status2} Partner`)
: message.error(`Failed ${status2} Partner`);
} catch (err) {
console.log("error", err);
message.error(`Failed ${status2} Partner`);
}
};
const handleEditButton = (data) => {
console.log(data, "isi data");
form.setFieldsValue({
name: data.name,
npwp: data.npwp,
address: data.address,
code: data.code,
});
setIsChangePassword(false);
store.partner.visibleModalPartner = true;
setIdData(data.id);
};
const changePassword = (data) => {
// form.setFieldsValue({
// name: data.name,
// npwp: data.npwp,
// address: data.address,
// });
setIsChangePassword(true);
store.partner.visibleModalPartner = true;
setIdData(data.id);
console.log(data.id);
};
const columns = [
{
title: "Name",
dataIndex: "name",
key: "name",
},
{
title: "Kode",
dataIndex: "code",
key: "code",
},
{
title: "Npwp",
dataIndex: "npwp",
key: "npwp",
},
{
title: "Address",
dataIndex: "address",
key: "address",
},
{
title: "Status",
dataIndex: "status",
key: "status",
render: (text, record) => (
<Tag
color={record?.status === true ? "processing" : "#E3E8EE"}
style={{ color: "#4F566B", cursor: "pointer" }}
>
{record?.status === true ? " ACTIVE" : "INACTIVE"}
</Tag>
),
},
{
title: "Action",
key: "action",
render: (text, record) => (
<Space size="middle">
<Button
type={record?.status === true ? "danger" : "primary"}
onClick={() => changeStatus(record?.id, record?.status)}
>
{record?.status === true ? "Inactive" : "Active"}
</Button>
<Button onClick={() => handleEditButton(record)}>Edit</Button>
{/* <Button onClick={() => handleDelete(record.id)}>Delete</Button> */}
<Button onClick={() => changePassword(record)}>
Change Password
</Button>
</Space>
),
},
];
const deleteData = async (id) => {
try {
modalLoader.setLoading(true);
await store.partner.delete(id);
modalLoader.setLoading(false);
message.success("Data Berhasil Dihapus");
history.push(LINKS.PARTNER);
} catch (err) {
console.log("error", err);
message.error("Gagal menghapus");
}
};
const handleDelete = (id) => {
Modal.confirm({
title: "Are you sure delete this record?",
icon: <ExclamationCircleOutlined />,
okText: "Yes",
okType: "primary",
cancelText: "Cancel",
onOk() {
return deleteData(id);
},
onCancel() {
console.log("Cancel");
},
});
};
const handleCancel = () => {
setIdData("");
store.partner.visibleModalPartner = false;
};
const handleSubmit = async (data) => {
console.log(data, "isi data2");
if (idData !== "") {
modalLoader.setLoading(true);
try {
isChangePassword
? await store.partner.updatePassword(idData, data)
: await store.partner.update(idData, data);
modalLoader.setLoading(false);
message.success(
isChangePassword
? "Success Change Password"
: "Success Update Data Partner"
);
//await store.partner.getData()
} catch (e) {
modalLoader.setLoading(false);
message.error(
isChangePassword
? "Failed Change Password"
: "Failed Update Data Partner"
);
}
store.partner.visibleModalPartner = false;
setIdData("");
form.resetFields();
} else {
modalLoader.setLoading(true);
try {
const response = await store.partner.create(data);
modalLoader.setLoading(false);
response?.body?.statusCode === 201 || response?.body?.statusCode === 200
? message.success(
response?.body?.message || "Berhasil Tambah Partner"
)
: message.error(response?.body?.message || "Gagal Tambah Partner");
} catch (e) {
console.log(e, "apa errornya");
message.error(e.response?.body?.message || "Gagal Tambah Partner");
}
modalLoader.setLoading(false);
store.partner.visibleModalPartner = false;
setIdData("");
form.resetFields();
}
};
return (
<div>
{store.ui.mediaQuery.isDesktop && (
<Table
style={{ textAlign: "center" }}
columns={columns}
dataSource={store.partner.data}
bordered
pagination={{
pageSize: store.partner.pageSize,
total: store.partner.total_data,
current: store.partner.page + 1,
showSizeChanger: true,
simple: false,
}}
onChange={async (page) => {
let pageNumber = page.current;
store.partner.pageSize = page.pageSize;
store.partner.page = pageNumber - 1;
modalLoader.setLoading(true);
await store.partner.getData();
modalLoader.setLoading(false);
}}
/>
)}
{store.ui.mediaQuery.isMobile && (
<List
itemLayout="horizontal"
position={"top"}
pagination={{
onChange: async (page, pageSize) => {
store.partner.pageSize = pageSize;
store.partner.page = page - 1;
modalLoader.setLoading(true);
await store.partner.getData();
modalLoader.setLoading(false);
},
pageSize: store.partner.pageSize,
total: store.partner.total_data,
current: store.partner.page + 1,
style: { marginBottom: "1rem", marginRight: "1rem" },
}}
dataSource={store.partner.data}
style={{ padding: 0 }}
renderItem={(item) => {
return (
<div>
<List.Item
key={item.id}
style={{
backgroundColor: "#ffffff",
paddingTop: 0,
paddingBottom: 0,
display: "flex",
flexDirection: "row",
alignItems: "center",
justifyContent: "center",
}}
>
<List.Item.Meta
className={[""].join(" ")}
title={item.code}
description={
<div style={{}}>
<p>
<div style={{ marginBottom: 10 }}>
<small>Nama : {item.name}</small> <br />
<small>Npwp : {item.npwp}</small> <br />
<small>Address : {item.address}</small>
</div>
<Space size="small">
<Button
type={
item?.status === true ? "danger" : "primary"
}
onClick={() =>
changeStatus(item?.id, item?.status)
}
>
{item?.status === true ? "Inactive" : "Active"}
</Button>
<Button
onClick={(owner, name) => handleEditButton(item)}
>
Edit
</Button>
{/* <Button onClick={() => handleDelete(record.id)}>Delete</Button> */}
<Button onClick={() => changePassword(item)}>
Change Password
</Button>
</Space>
</p>
<p></p>
</div>
}
/>
<div style={{ marginRight: 16 }}>
<p
style={{
fontSize: 9,
marginBottom: 40,
}}
>
<Tag
color={item?.status === true ? "processing" : "#E3E8EE"}
style={{ color: "#4F566B", cursor: "pointer" }}
>
{item?.status === true ? " ACTIVE" : "INACTIVE"}
</Tag>
</p>
</div>
</List.Item>
<Divider plain style={{ margin: 0 }} />
</div>
);
}}
/>
)}
<Modal
visible={store.partner.visibleModalPartner}
title={
isChangePassword
? "Change Member Password"
: idData
? "Edit Partner"
: "Create a new partner"
}
okText={idData ? "Edit" : "Create"}
cancelText="Cancel"
onCancel={() => {
form.resetFields();
handleCancel();
}}
onOk={() => {
form
.validateFields()
.then((values) => {
console.log(values, "isi form");
handleSubmit(values);
form.resetFields();
})
.catch((info) => {
console.error("Validate Failed:", info);
});
}}
>
<Form form={form} layout="vertical">
{!isChangePassword && (
<Form.Item
name="name"
label="Name"
rules={[
idData
? { required: false }
: { required: true, message: "Please input name!" },
]}
>
<Input />
</Form.Item>
)}
{!idData && (
<Form.Item
name="code"
label="Kode"
rules={[
idData
? { required: false }
: { required: true, message: "Please input code partner!" },
]}
>
<Input />
</Form.Item>
)}
{!idData && (
<Form.Item
name="owner"
label="Owner"
rules={[
idData
? { required: false }
: { required: true, message: "Please input owner name!" },
]}
>
<Input />
</Form.Item>
)}
{((idData && isChangePassword) || !idData) && (
<Form.Item
name="password_account"
label="Password Account"
rules={[
idData
? { required: false }
: {
required: true,
message: "Please input password account!",
},
]}
>
<Input.Password />
</Form.Item>
)}
{!idData && (
<Form.Item
name="phone_number"
label="Phone Number"
rules={[
idData
? { required: false }
: {
required: true,
message: "Please input password phone number!",
},
{
pattern: /^(?:\d*)$/,
message: "Phone number should contain just number",
},
{
pattern: /^[\d]{10,12}$/,
message: "Phone number should be 10 - 12 character",
},
]}
>
<Input
onChange={(value) => {
setValue(value);
}}
/>
</Form.Item>
)}
{!isChangePassword && (
<>
<Form.Item
name="npwp"
label="Npwp"
rules={[
idData
? { required: false }
: { required: true, message: "Please input npwp!" },
]}
>
<Input />
</Form.Item>
<Form.Item
name="address"
label="Address"
rules={[
idData
? { required: false }
: {
required: true,
message: "Please input address!",
},
]}
>
<Input />
</Form.Item>
</>
)}
</Form>
</Modal>
</div>
);
});

View File

@ -1,64 +1,103 @@
import React, { useEffect, useState, useParams } from "react"; import React, { useContext, useState } from "react";
import { Button, Space, Table, Tag, Modal, message } from "antd"; import {
import { useStore } from "../../utils/useStore"; Button,
Col,
Divider,
Form,
Input,
InputNumber,
List,
message,
Modal,
Row,
Select,
Table,
Tag,
Typography,
} from "antd";
import { observer } from "mobx-react-lite"; import { observer } from "mobx-react-lite";
import { ExclamationCircleOutlined } from "@ant-design/icons"; import { ExclamationCircleOutlined } from "@ant-design/icons";
import { useHistory } from "react-router-dom"; import { useHistory } from "react-router-dom";
import { useStore } from "../utils/useStore";
import { LINKS } from "../routes/app";
import { ModalLoaderContext } from "../utils/modal";
export const Pulsa = observer(() => { const { Title, Text } = Typography;
export const ProductComponent = observer((props) => {
const store = useStore(); const store = useStore();
const [form] = Form.useForm();
const { Option } = Select;
const history = useHistory(); const history = useHistory();
const [visibleModal, setVisibleModal] = useState(false); const [idData, setIdData] = useState("");
const [initialData, setInitialData] = useState({}); const [filterSupplier, setFilterSupplier] = useState([]);
const [confirmLoading, setConfirmLoading] = useState(false); const [filterSubCategories, setFilterSubCategories] = useState([]);
const [isLoading, setIsLoading] = useState(false); const modalLoader = useContext(ModalLoaderContext);
useEffect(() => {
const init = async () => { const handleEditButton = (data) => {
try { console.log(data, "isi data");
setIsLoading(true); form.setFieldsValue({
await store.product.getData(); name: data.name,
setIsLoading(false); price: data.price,
} catch (e) { markUpPrice: data.basePrice,
setIsLoading(false); code: data.code,
} status: data.status,
}; subCategoriesId: data.sub_categories.id,
});
store.product.visibleModalProduct = true;
setIdData(data.id);
};
init();
}, []);
const columns = [ const columns = [
{ {
title: "Kode", title: "Kode",
dataIndex: "code", dataIndex: "product_code",
key: "code", key: "product_code",
}, },
{ {
title: "Produk", title: "Produk",
dataIndex: "name", dataIndex: "product_name",
key: "name", key: "product_name",
}, },
{ {
title: "Harga Beli", title: "Harga Beli",
dataIndex: "price", dataIndex: "current_price_price",
key: "price", key: "current_price_price",
render: (text) =>
new Intl.NumberFormat("id-ID", {
style: "currency",
currency: "IDR",
}).format(text),
}, },
,
{ {
title: "Harga Jual", title: "Harga Jual",
dataIndex: "mark_up_price", dataIndex: "mark_up_price",
key: "mark_up_price", key: "mark_up_price",
render: (text) =>
new Intl.NumberFormat("id-ID", {
style: "currency",
currency: "IDR",
}).format(text),
}, },
{ {
title: "Gangguan", title: "Harga",
dataIndex: "status", dataIndex: "price",
key: "status", key: "price",
render: (text, record) => ( render: (text) =>
<Tag new Intl.NumberFormat("id-ID", {
color={record?.status === "AKTIF" ? "processing" : "#E3E8EE" } style: "currency",
style={{ color: "#4F566B" }} currency: "IDR",
> }).format(text),
{record?.status} },
</Tag> {
), title: "Supplier",
dataIndex: "supplier_name",
key: "supplier_name",
},
{
title: "Sub Category",
dataIndex: "sub_categories_name",
key: "sub_categories_name",
}, },
{ {
title: "Tersedia", title: "Tersedia",
@ -66,10 +105,10 @@ export const Pulsa = observer(() => {
key: "tersedia", key: "tersedia",
render: (text, record) => ( render: (text, record) => (
<Tag <Tag
color={record?.status === "AKTIF" ? "processing" : "#E3E8EE" } color={record?.product_status === "ACTIVE" ? "blue" : "#E3E8EE"}
style={{ color: "#4F566B" }} style={{ color: "#4F566B" }}
> >
{record?.status === "AKTIF" ? " Ya" : "Tidak"} {record?.product_status === "ACTIVE" ? " Tersedia" : "Tidak"}
</Tag> </Tag>
), ),
}, },
@ -77,30 +116,41 @@ export const Pulsa = observer(() => {
title: "Action", title: "Action",
key: "action", key: "action",
render: (text, record) => ( render: (text, record) => (
<Space size="middle"> <Button
<Button>Edit</Button> onClick={async () => {
<Button history.push(
onClick={async () => { LINKS.PRODUCT_DETAIL.replace(":id", record.product_id)
handleDelete(record.id); );
}} }}
> >
Delete Detail
</Button> </Button>
</Space>
), ),
}, },
]; ];
if (store.authentication.userData.role !== "Admin") columns.pop();
//if (store.authentication.userData.role === "Admin Partner") delete columns[2];
if (store.authentication.userData.role === "Admin Partner") delete columns[3];
if (store.authentication.userData.role != "Admin Partner") delete columns[4];
if (store.authentication.userData.role !== "Admin") delete columns[6];
if (store.authentication.userData.role !== "Admin") delete columns[2];
if (store.authentication.userData.role !== "Admin") delete columns[5];
if (store.authentication.userData.role === "Admin Partner") delete columns[7];
if (store.authentication.userData.role !== "Admin") delete columns[8];
//if (store.authentication.userData.role === "Admin Partner") delete columns[5];
const deleteData = async (id) => { const deleteData = async (id) => {
try { try {
console.log(id); console.log(id);
await store.product.delete(id); await store.product.delete(id);
message.success("Data Berhasil Dihapus"); message.success("Data Berhasil Dihapus");
history.push("/app/product"); history.push(LINKS.PRODUCT);
} catch (err) { } catch (err) {
console.log("error", err); console.log("error", err);
message.error("Gagal menghapus"); message.error("Gagal menghapus");
} }
}; };
const handleDelete = (id) => { const handleDelete = (id) => {
Modal.confirm({ Modal.confirm({
title: "Are you sure delete this record?", title: "Are you sure delete this record?",
@ -116,14 +166,372 @@ export const Pulsa = observer(() => {
}, },
}); });
}; };
const handleCancel = () => {
setIdData("");
store.product.visibleModalProduct = false;
};
const handleSubmit = async (data) => {
console.log(data, "isi data2");
if (idData !== "") {
modalLoader.setLoading(true);
try {
const response = await store.product.update(idData, data);
response?.body?.statusCode === 201 || response?.body?.statusCode === 200
? message.success(
response?.body?.message || "Berhasil Ubah Data Produk"
)
: message.error(response?.body?.message || "Gagal Ubah Data Produk");
} catch (e) {
message.error(e.response?.body?.message || "Gagal Ubah Data Produk");
}
modalLoader.setLoading(false);
store.product.visibleModalProduct = false;
setIdData("");
form.resetFields();
} else {
modalLoader.setLoading(true);
try {
const response = await store.product.create(data);
response?.body?.statusCode === 201 || response?.body?.statusCode === 200
? message.success(response?.body?.message || "Berhasil Tambah Produk")
: message.error(response?.body?.message || "Gagal Tambah Produk");
} catch (e) {
console.log(e, "apa errornya");
message.error(e.response?.body?.message || "Gagal Tambah Produk");
}
modalLoader.setLoading(false);
store.product.visibleModalProduct = false;
setIdData("");
form.resetFields();
}
};
const handleRemoveFilter = async () => {
store.product.filterSupplier = null;
store.product.filterSubCategory = null;
setFilterSupplier([]);
setFilterSubCategories([]);
await store.product.getData();
store.product.visibleModalFilterProduct = false;
};
const handleCancelFilter = async () => {
store.product.filterSubCategory = null;
store.product.filterSupplier = null;
store.product.visibleModalFilterProduct = false;
await store.product.getData();
};
const handleSubmitFilter = async () => {
store.product.filterSupplier = filterSupplier;
store.product.filterSubCategory = filterSubCategories;
modalLoader.setLoading(true);
await store.product.getData();
modalLoader.setLoading(false);
store.product.visibleModalFilterProduct = false;
};
const footerLayoutFilter = [
<Button
key={"remove"}
onClick={handleRemoveFilter}
style={{
backgroundColor: "#e74e5e",
color: "#fff",
}}
>
Remove Filter
</Button>,
<Button key={"cancel"} onClick={handleCancelFilter}>
Cancel
</Button>,
<Button
key={"submit"}
onClick={handleSubmitFilter}
style={{
backgroundColor: "#4e79e7",
color: "#fff",
}}
>
Apply
</Button>,
];
const handleClickRow = (record, index) => ({
onClick: (event) => {
history.push(LINKS.PRODUCT_DETAIL.replace(":id", record.product_id));
},
});
return ( return (
<div> <div>
<Table {store.ui.mediaQuery.isDesktop && (
style={{ textAlign: "center" }} <Table
columns={columns} style={
dataSource={store.product.data} store.authentication.userData.role === "Admin"
bordered ? {
/> cursor: "pointer",
textAlign: "center",
width: store.ui.mediaQuery.isMobile ? 250 : "",
}
: {
textAlign: "center",
width: store.ui.mediaQuery.isMobile ? 250 : "",
}
}
columns={columns}
dataSource={
store.authentication.userData.role === "Admin Partner"
? store.product.dataProductPartner
: store.product.data
}
onRow={
store.authentication.userData.role === "Admin" ? handleClickRow : ""
}
pagination={{
pageSize: store.product.pageSize,
total:
store.authentication.userData.role === "Admin Partner"
? store.product.total_data_partner
: store.product.total_data,
current: store.product.page + 1,
showSizeChanger: true,
simple: false,
}}
onChange={async (page) => {
let pageNumber = page.current;
store.product.pageSize = page.pageSize;
store.product.page = pageNumber - 1;
modalLoader.setLoading(true);
await store.product.getData();
modalLoader.setLoading(false);
}}
/>
)}
{store.ui.mediaQuery.isMobile && (
<List
itemLayout="horizontal"
position={"top"}
pagination={{
onChange: async (page, pageSize) => {
store.product.pageSize = pageSize;
store.product.page = page - 1;
modalLoader.setLoading(true);
await store.product.getData();
modalLoader.setLoading(false);
},
pageSize: store.product.pageSize,
total: store.product.total_data,
current: store.product.page + 1,
style: { marginBottom: "1rem", marginRight: "1rem" },
}}
dataSource={store.product.data}
style={{ padding: 0 }}
renderItem={(item) => {
return (
<div>
<List.Item
key={item.id}
style={{
backgroundColor: "#ffffff",
paddingTop: 0,
paddingBottom: 0,
display: "flex",
flexDirection: "row",
alignItems: "center",
justifyContent: "center",
}}
>
<List.Item.Meta
className={[""].join(" ")}
title={item.product_code}
description={
<div style={{}}>
<p>
<small>{item.product_name}</small> <br />
<small>Harga Beli : {item.current_price_price}</small>
<br />
<small>Harga Jual : {item.mark_up_price}</small>
</p>
<p></p>
</div>
}
/>
<div style={{ marginRight: 16 }}>
<p
style={{
fontSize: 9,
margin: 0,
}}
>
<Tag
color={
item?.product_status === "ACTIVE" ? "blue" : "#E3E8EE"
}
style={{ color: "#4F566B" }}
>
{item?.product_status === "ACTIVE"
? " Tersedia"
: "Tidak"}
</Tag>
</p>
</div>
</List.Item>
<Divider plain style={{ margin: 0 }} />
</div>
);
}}
/>
)}
<Modal
visible={store.product.visibleModalProduct}
title={idData ? "Edit Product" : "Create a new Product"}
okText={idData ? "Edit" : "Create"}
cancelText="Cancel"
onCancel={() => {
form.resetFields();
handleCancel();
}}
onOk={() => {
form
.validateFields()
.then((values) => {
console.log(values, "isi form");
handleSubmit(values);
form.resetFields();
})
.catch((info) => {
console.error("Validate Failed:", info);
});
}}
>
<Form form={form} layout="vertical">
<Form.Item
name="name"
label="Name"
rules={[{ required: true, message: "Please input name!" }]}
>
<Input />
</Form.Item>
<Form.Item
name="price"
label="Price"
rules={[{ required: true, message: "Please input price!" }]}
>
<InputNumber
style={{ width: "100%" }}
formatter={(value) =>
`Rp. ${value}`.replace(/\B(?=(\d{3})+(?!\d))/g, ",")
}
parser={(value) => value.replace(/\Rp.\s?|(,*)/g, "")}
/>
</Form.Item>
<Form.Item
name="markUpPrice"
label="Mark Up Price"
rules={[{ required: true, message: "Please input mark up price!" }]}
>
<InputNumber
style={{ width: "100%" }}
formatter={(value) =>
`Rp. ${value}`.replace(/\B(?=(\d{3})+(?!\d))/g, ",")
}
parser={(value) => value.replace(/\Rp.\s?|(,*)/g, "")}
/>
</Form.Item>
<Form.Item
name="code"
label="Code"
rules={[{ required: true, message: "Please input code!" }]}
>
<Input />
</Form.Item>
<Form.Item
name="status"
label="Status"
rules={[{ required: true, message: "Please select Status!" }]}
>
<Select placeholder="Select Sub Category" allowClear>
<Option value="ACTIVE">ACTIVE</Option>
<Option value="INACTIVE">INACTIVE</Option>
</Select>
</Form.Item>
<Form.Item
name="subCategoriesId"
label="Sub categories"
rules={[
{ required: true, message: "Please select sub categories!" },
]}
>
<Select placeholder="Select Sub Category" allowClear>
{store.category.dataSubCategories.map((item) => (
<Option value={item.id} key={item.id}>
{item.name}
</Option>
))}
</Select>
</Form.Item>
</Form>
</Modal>
<Modal
visible={store.product.visibleModalFilterProduct}
title={"Filter"}
footer={footerLayoutFilter}
onCancel={async () => {
// setFilterSupplier([]);
// setFilterSubCategories([]);
store.product.filterSupplier = null;
store.product.filterSubCategory = null;
store.product.visibleModalFilterProduct = false;
await store.product.getData();
}}
>
<Row>
{store.authentication.userData.role === "Admin" && (
<Col span={24}>
<Title level={5} type={"secondary"} strong>
Filter Supplier
</Title>
<Select
mode={"multiple"}
placeholder="Choose Supplier"
onChange={(val) => {
setFilterSupplier(val);
}}
style={{ marginBottom: "20px", width: "100%" }}
value={filterSupplier}
>
{store.supplier.data.map((item) => (
<Option value={item.id} key={item.id}>
{item.name}
</Option>
))}
</Select>
</Col>
)}
<Col span={24}>
<Title level={5} type={"secondary"} strong>
Filter Sub-Categories
</Title>
<Select
mode={"multiple"}
placeholder="Choose Sub-Category"
onChange={(val) => {
setFilterSubCategories(val);
}}
style={{ marginBottom: "20px", width: "100%" }}
value={filterSubCategories}
>
{store.product.dataSubCategories.map((item) => (
<Option value={item.id} key={item.id}>
{item.name}
</Option>
))}
</Select>
</Col>
</Row>
</Modal>
</div> </div>
); );
}); });

View File

@ -0,0 +1,273 @@
import React, { useContext, useEffect, useState } from "react";
import {
Button,
Divider,
Form,
Input,
List,
message,
Modal,
Select,
Space,
Table,
} from "antd";
import { observer } from "mobx-react-lite";
import { useStore } from "../utils/useStore";
import { ModalLoaderContext } from "../utils/modal";
export const SubcategoryComponent = observer((props) => {
const store = useStore();
const [form] = Form.useForm();
const { Option } = Select;
const [idData, setIdData] = useState("");
const modalLoader = useContext(ModalLoaderContext);
useEffect(() => {
getData();
}, []);
const getData = async () => {
await store.category.getData();
await store.subcategory.getData();
};
const handleEditButton = (data) => {
console.log(data, "isi data");
form.setFieldsValue({
code: data.code,
name: data.name,
categoryId: data.categoryId,
});
store.subcategory.visibleModalSubcategory = true;
setIdData(data.id);
};
const columns = [
{
title: "Code",
dataIndex: "code",
key: "code",
},
{
title: "Sub Category Name",
dataIndex: "name",
key: "name",
},
{
title: "Category",
dataIndex: "categoryName",
key: "categoryName",
},
{
title: "Action",
key: "action",
render: (text, record) => (
<Space size="middle">
<Button onClick={() => handleEditButton(record)}>Edit</Button>
</Space>
),
},
];
const handleCancel = () => {
setIdData("");
store.subcategory.visibleModalSubcategory = false;
};
const handleSubmit = async (data) => {
console.log(data, "isi data2");
if (idData !== "") {
modalLoader.setLoading(true);
try {
const response = await store.subcategory.update(idData, data);
response?.body?.statusCode === 201 || response?.body?.statusCode === 200
? message.success(
response?.body?.message || "Berhasil Ubah Data Sub Kategori"
)
: message.error(
response?.body?.message || "Gagal Ubah Data Sub Kategori"
);
await getData();
} catch (e) {
message.error(
e.response?.body?.message || "Gagal Ubah Data Sub Kategori"
);
}
modalLoader.setLoading(false);
store.subcategory.visibleModalSubcategory = false;
setIdData("");
form.resetFields();
} else {
modalLoader.setLoading(true);
try {
const response = await store.subcategory.create(data);
response?.body?.statusCode === 201 || response?.body?.statusCode === 200
? message.success(
response?.body?.message || "Berhasil Tambah Sub Kategori"
)
: message.error(
response?.body?.message || "Gagal Tambah Sub Kategori"
);
await getData();
} catch (e) {
console.log(e, "apa errornya");
message.error(e.response?.body?.message || "Gagal Tambah Sub Kategori");
}
modalLoader.setLoading(false);
store.subcategory.visibleModalSubcategory = false;
setIdData("");
form.resetFields();
}
};
return (
<div>
{store.ui.mediaQuery.isDesktop && (
<Table
style={{ textAlign: "center" }}
columns={columns}
dataSource={store.subcategory.data}
bordered
pagination={{
pageSize: store.subcategory.pageSize,
total: store.subcategory.total_data,
current: store.subcategory.page + 1,
showSizeChanger: true,
simple: false,
}}
onChange={async (page) => {
let pageNumber = page.current;
store.subcategory.pageSize = page.pageSize;
store.subcategory.page = pageNumber - 1;
modalLoader.setLoading(true);
await getData();
modalLoader.setLoading(false);
}}
/>
)}
{store.ui.mediaQuery.isMobile && (
<List
itemLayout="horizontal"
position={"top"}
pagination={{
onChange: async (page,pageSize) => {
store.subcategory.pageSize = pageSize;
store.subcategory.page = page - 1;
modalLoader.setLoading(true);
await store.subcategory.getData();
modalLoader.setLoading(false);
},
pageSize: store.subcategory.pageSize,
total: store.subcategory.total_data,
current: store.subcategory.page + 1,
style: { marginBottom: "1rem", marginRight: "1rem" },
}}
dataSource={store.subcategory.data}
style={{ padding: 0 }}
renderItem={(item) => {
return (
<div>
<List.Item
key={item.id}
style={{
backgroundColor: "#ffffff",
paddingTop: 0,
paddingBottom: 0,
display: "flex",
flexDirection: "row",
alignItems: "center",
justifyContent: "center",
}}
>
<List.Item.Meta
className={[""].join(" ")}
title={item.code}
description={
<div style={{}}>
<p>
<small>Sub Category : {item.name}</small> <br />
<small>Category : {item.categoryName}</small>
<br />
</p>
<p></p>
</div>
}
/>
<div style={{ marginRight: 16 }}>
<p
style={{
fontSize: 9,
margin: 0,
}}
>
<Button onClick={() => handleEditButton(item)}>
Edit
</Button>
</p>
</div>
</List.Item>
<Divider plain style={{ margin: 0 }} />
</div>
);
}}
/>
)}
<Modal
visible={store.subcategory.visibleModalSubcategory}
title={idData ? "Edit Sub Category" : "Create a new sub category"}
okText={idData ? "Edit" : "Create"}
cancelText="Cancel"
onCancel={() => {
form.resetFields();
handleCancel();
}}
onOk={() => {
form
.validateFields()
.then((values) => {
console.log(values, "isi form");
handleSubmit(values);
form.resetFields();
})
.catch((info) => {
console.error("Validate Failed:", info);
});
}}
>
<Form form={form} layout="vertical">
{idData === "" && (
<Form.Item
name="code"
label="Code"
rules={[
{ required: true, message: "Please input code category!" },
]}
>
<Input />
</Form.Item>
)}
<Form.Item
name="name"
label="Name"
rules={[{ required: true, message: "Please input name category!" }]}
>
<Input />
</Form.Item>
{/* {!idData && ( */}
<Form.Item
name="categoryId"
label="Categories"
rules={[{ required: true, message: "Please input category id!" }]}
>
<Select placeholder="Select Category" allowClear>
{store.category.data.map((item) => (
<Option value={item.id} key={item.id} defaultValue={item.name}>
{item.name}
</Option>
))}
</Select>
</Form.Item>
{/* )} */}
</Form>
</Modal>
</div>
);
});

View File

@ -0,0 +1,385 @@
import React, { useContext, useState } from "react";
import {
Button,
Divider,
Form,
Input,
List,
message,
Modal,
Space,
Table,
Tag,
} from "antd";
import { observer } from "mobx-react-lite";
import { ExclamationCircleOutlined } from "@ant-design/icons";
import { useHistory } from "react-router-dom";
import { useStore } from "../utils/useStore";
import { LINKS } from "../routes/app";
import { TopupsaldoModal } from "./TopupsaldoModal";
import { ModalLoaderContext } from "../utils/modal";
export const SupplierComponent = observer((props) => {
const store = useStore();
const [form] = Form.useForm();
const history = useHistory();
const [idData, setIdData] = useState("");
const modalLoader = useContext(ModalLoaderContext);
const handleEditButton = (data) => {
console.log(data, "isi data");
form.setFieldsValue({
name: data.name,
code: data.code,
status: data.status,
});
store.supplier.visibleModalSupplier = true;
setIdData(data.id);
};
const handleTopup = (data) => {
console.log(data, "isi data");
form.setFieldsValue({
code: data.code,
});
store.supplier.visibleModalTransaction = true;
store.supplier.code = data.code;
};
const changeStatus = async (id, isActive) => {
const status = isActive ? "inactive" : "active";
const status2 = isActive ? "Inactivating" : "Activating";
try {
modalLoader.setLoading(true);
const response = await store.supplier.changeStatus(id, status);
modalLoader.setLoading(false);
response?.body?.statusCode === 201
? message.success(`Success ${status2} Supplier`)
: message.error(`Failed ${status2} Supplier`);
} catch (err) {
modalLoader.setLoading(false);
console.log("error", err);
message.error(`Failed ${status2} Supplier`);
}
};
const columns = [
{
title: "Name",
dataIndex: "name",
key: "name",
},
{
title: "Kode",
dataIndex: "code",
key: "code",
width: "5%",
},
{
title: "Saldo di Supplier",
dataIndex: ["coa", "amount"],
key: ["coa", "amount"],
width: "20%",
render: (text, record) =>
new Intl.NumberFormat("id-ID", {
style: "currency",
currency: "IDR",
}).format(text),
},
{
title: "Saldo di System",
dataIndex: ["coa_undistribute", "amount"],
key: ["coa_undistribute", "amount"],
width: "20%",
render: (text, record) =>
new Intl.NumberFormat("id-ID", {
style: "currency",
currency: "IDR",
}).format(text),
},
{
title: "Status",
dataIndex: "status",
key: "status",
width: "5%",
render: (text, record) => (
<Tag
color={record?.status === true ? "processing" : "#E3E8EE"}
style={{ color: "#4F566B" }}
>
{record?.status === true ? " ACTIVE" : "INACTIVE"}
</Tag>
),
},
{
title: "Action",
key: "action",
width: "20%",
render: (text, record) => (
<Space size="middle">
<Button
type={record?.status === true ? "danger" : "primary"}
onClick={() => changeStatus(record?.id, record?.status)}
>
{record?.status === true ? "INACTIVE" : "ACTIVE"}
</Button>
<Button onClick={() => handleTopup(record)}> Top Up Saldo</Button>
<Button onClick={() => handleEditButton(record)}>Edit</Button>
</Space>
),
},
];
const deleteData = async (id) => {
try {
console.log(id);
modalLoader.setLoading(true);
await store.supplier.delete(id);
modalLoader.setLoading(false);
message.success("Data Berhasil Dihapus");
history.push(LINKS.PRODUCT);
} catch (err) {
modalLoader.setLoading(false);
console.log("error", err);
message.error("Gagal menghapus");
}
};
const handleDelete = (id) => {
Modal.confirm({
title: "Are you sure delete this record?",
icon: <ExclamationCircleOutlined />,
okText: "Yes",
okType: "primary",
cancelText: "Cancel",
onOk() {
return deleteData(id);
},
onCancel() {
console.log("Cancel");
},
});
};
const handleCancel = () => {
setIdData("");
store.supplier.visibleModalSupplier = false;
};
const handleSubmit = async (data) => {
console.log(data, "isi data2");
if (idData !== "") {
try {
modalLoader.setLoading(true);
const response = await store.supplier.update(idData, data);
response?.body?.statusCode === 201 || response?.body?.statusCode === 200
? message.success(
response?.body?.message || "Berhasil Ubah Data Supplier"
)
: message.error(
response?.body?.message || "Gagal Ubah Data Supplier"
);
} catch (e) {
modalLoader.setLoading(false);
message.error(e.response?.body?.message || "Gagal Ubah Data Supplie");
}
modalLoader.setLoading(false);
store.supplier.visibleModalSupplier = false;
setIdData("");
form.resetFields();
} else {
try {
const response = await store.supplier.create(data);
if (response.status === 201) {
message.success(
response?.body?.message || "Berhasil Tambah Supplier"
);
await store.supplier.getData();
} else {
message.error(response?.body?.error || "Gagal Tambah Supplier", 3);
}
} catch (e) {
console.log("testingan");
console.log(e.response, "testingan");
console.log(e.result, "testingan1");
if (e.response?.body?.error) {
message.error(e.response.body.error);
modalLoader.setLoading(false);
store.supplier.visibleModalSupplier = false;
return;
}
console.log(e, "apa errornya");
message.error("Gagal Tambah Supplier");
}
modalLoader.setLoading(false);
store.supplier.visibleModalSupplier = false;
setIdData("");
form.resetFields();
await store.supplier.getData();
}
};
return (
<div>
{store.ui.mediaQuery.isDesktop && (
<Table
style={{ textAlign: "center" }}
columns={columns}
dataSource={store.supplier.data}
bordered
pagination={{
pageSize: store.supplier.pageSize,
total: store.supplier.total_data,
current: store.supplier.page + 1,
showSizeChanger: true,
simple: false,
}}
onChange={async (page) => {
let pageNumber = page.current;
store.supplier.pageSize = page.pageSize;
store.supplier.page = pageNumber - 1;
modalLoader.setLoading(true);
await store.supplier.getData();
modalLoader.setLoading(false);
}}
/>
)}
{store.ui.mediaQuery.isMobile && (
<List
itemLayout="horizontal"
position={"top"}
pagination={{
onChange: async (page,pageSize) => {
store.supplier.pageSize = pageSize;
store.supplier.page = page - 1;
modalLoader.setLoading(true);
await store.supplier.getData();
modalLoader.setLoading(false);
},
pageSize: store.supplier.pageSize,
total: store.supplier.total_data,
current: store.supplier.page + 1,
style: { marginBottom: "1rem", marginRight: "1rem" },
}}
dataSource={store.supplier.data}
style={{ padding: 0 }}
renderItem={(item) => {
return (
<div>
<List.Item
key={item.id}
style={{
backgroundColor: "#ffffff",
paddingTop: 0,
paddingBottom: 0,
display: "flex",
flexDirection: "row",
alignItems: "center",
justifyContent: "center",
}}
>
<List.Item.Meta
className={[""].join(" ")}
title={item.code}
description={
<div style={{}}>
<p>
<small>Name : {item.name}</small> <br />
<small>Saldo Supplier : {item.coa.amount}</small>
<br />
<small>
Saldo System : {item.coa_undistribute.amount}
</small>
<br />
<Button
style={{
marginRight: 10,
}}
type={item?.status === true ? "danger" : "primary"}
onClick={() => changeStatus(item?.id, item?.status)}
>
{item?.status === true ? "INACTIVE" : "ACTIVE"}
</Button>
<Button
onClick={() => handleTopup(item)}
style={{
marginRight: 10,
}}
>
Top Up Saldo
</Button>
<Button
onClick={() => handleEditButton(item)}
style={{
marginBottom: 5,
}}
>
Edit
</Button>
<Tag
color={
item?.status === true ? "processing" : "#E3E8EE"
}
style={{ color: "#4F566B" }}
>
{item?.status === true ? " ACTIVE" : "INACTIVE"}
</Tag>
</p>
<p></p>
</div>
}
/>
</List.Item>
<Divider plain style={{ margin: 0 }} />
</div>
);
}}
/>
)}
<Modal
visible={store.supplier.visibleModalSupplier}
title={idData ? "Edit Supplier" : "Create a new Supplier"}
okText={idData ? "Edit" : "Create"}
cancelText="Cancel"
onCancel={() => {
form.resetFields();
handleCancel();
}}
onOk={() => {
form
.validateFields()
.then((values) => {
console.log(values, "isi form");
handleSubmit(values);
form.resetFields();
})
.catch((info) => {
console.error("Validate Failed:", info);
});
}}
>
<Form form={form} layout="vertical">
<Form.Item
name="name"
label="Name"
rules={[{ required: true, message: "Please input name!" }]}
>
<Input />
</Form.Item>
{!idData && (
<Form.Item
name="code"
label="Kode"
rules={[{ required: true, message: "Please input kode!" }]}
>
<Input />
</Form.Item>
)}
</Form>
</Modal>
<TopupsaldoModal code={store.supplier.code} />
</div>
);
});

View File

@ -0,0 +1,84 @@
import React, { useContext } from "react";
import { Form, Input, message, Modal, InputNumber } from "antd";
import { observer } from "mobx-react-lite";
import { useStore } from "../utils/useStore";
import { ModalLoaderContext } from "../utils/modal";
export const TopupsaldoModal = observer((props) => {
const store = useStore();
const [form] = Form.useForm();
const modalLoader = useContext(ModalLoaderContext);
const handleCancelTransaction = () => {
store.supplier.visibleModalTransaction = false;
};
const handleSubmitTransaction = async (data) => {
try {
modalLoader.setLoading(true);
const response = await store.supplier.createTransaction(data);
message.success(response?.body?.message || "Success Top Up");
} catch (e) {
console.log(e, "apa errornya");
message.error(e.response?.body?.message || "Failed Top Up");
}
modalLoader.setLoading(false);
store.supplier.visibleModalTransaction = false;
form.resetFields();
};
return (
<div>
<Modal
visible={store.supplier.visibleModalTransaction}
title="Top Up Saldo"
okText="Top Up"
cancelText="Cancel"
onCancel={() => {
form.resetFields();
handleCancelTransaction();
}}
onOk={() => {
form
.validateFields()
.then((values) => {
console.log(values, "isi form");
handleSubmitTransaction(values);
form.resetFields();
})
.catch((info) => {
console.error("Validate Failed:", info);
});
}}
>
<Form
form={form}
layout="vertical"
initialValues={{ supplier: store.supplier.code }}
>
<Form.Item
name="supplier"
label="Supplier"
rules={[{ required: true, message: "Please input supplier!" }]}
>
<Input disabled={true} />
</Form.Item>
<Form.Item
name="amount"
label="Amount"
rules={[{ required: true, message: "Please input amount!" }]}
>
<InputNumber
style={{ width: "100%" }}
formatter={(value) =>
`Rp. ${value}`.replace(/\B(?=(\d{3})+(?!\d))/g, ",")
}
parser={(value) => value.replace(/\Rp.\s?|(,*)/g, "")}
/>
</Form.Item>
</Form>
</Modal>
</div>
);
});

View File

@ -1,5 +1,3 @@
export const appConfig = { export const appConfig = {
apiUrl: 'https://ppob-backend.k3s.bangun-kreatif.com/v1' apiUrl: process.env.NEXT_PUBLIC_BASE_URL || 'https://ppob-backend.k3s.bangun-kreatif.com/v1'
}; };
//export default appConfig;

7
src/constants/payback.js Normal file
View File

@ -0,0 +1,7 @@
export const PAYBACK_STATUS = {
0: 'Pending',
1: 'Success',
2: 'Failed',
3: 'Approved',
4: 'Rejected',
}

View File

@ -56,4 +56,30 @@ code {
.shadow { .shadow {
box-shadow: 0 7px 14px 0 rgba(60, 66, 87, 0.05), 0 3px 6px 0 rgba(0, 0, 0, 0.05); box-shadow: 0 7px 14px 0 rgba(60, 66, 87, 0.05), 0 3px 6px 0 rgba(0, 0, 0, 0.05);
} }
.ant-menu-sub.ant-menu-inline{
background-color: #e3e8ee !important;
}
.ant-menu-submenu-arrow{
padding-right: 40px !important;
}
.ant-breadcrumb {
margin-bottom: 10px !important;
}
.ant-tabs-tab.ant-tabs-tab-active .ant-tabs-tab-btn {
color: #ed1f24 !important;
border-color: #ed1f24 !important;
}
.profile-container .ant-popover-inner-content {
padding: 0 !important;
}
.ant-menu-item-divider{
border-style: transparent !important;
}
.ant-table-tbody > tr.ant-table-row-level-0:hover > td {
background: #f0faf8 !important;
}

View File

@ -1,12 +1,11 @@
import React, {useEffect} from "react"; import React, {useEffect, useState} from "react";
import {DesktopLayout} from "./DesktopLayout"; import {DesktopLayout} from "./DesktopLayout";
import {useMediaQuery} from 'react-responsive'; import {useMediaQuery} from 'react-responsive';
import {useStore} from "../../utils/useStore"; import {useStore} from "../../utils/useStore";
import {useHistory} from "react-router-dom"; import {ModalLoader} from "../../component/Modal/ModalLoader";
import {ModalLoaderContext} from "../../utils/modal";
export const App = () => { export const App = () => {
// TODO: add mobile layout
const store = useStore(); const store = useStore();
const mediaQuery = { const mediaQuery = {
isDesktop: useMediaQuery({minWidth: 1024}), isDesktop: useMediaQuery({minWidth: 1024}),
@ -15,11 +14,23 @@ export const App = () => {
isNotMobile: useMediaQuery({minWidth: 768}), isNotMobile: useMediaQuery({minWidth: 768}),
}; };
const [modalLoading, setModalLoading] = useState(false);
const [modalText, setModalText] = useState(undefined);
const modalContextValue = {
setLoading: setModalLoading,
setText: setModalText,
};
useEffect(() => { useEffect(() => {
store.ui.setMediaQuery(mediaQuery); store.ui.setMediaQuery(mediaQuery);
}); });
// const isMobileDevice = useMediaQuery({
// query: "(min-device-width: 480px)", return (
// }); <>
return <DesktopLayout/>; <ModalLoader isOpen={modalLoading} text={modalText}/>
<ModalLoaderContext.Provider value={modalContextValue}>
<DesktopLayout/>
</ModalLoaderContext.Provider>
</>
);
}; };

File diff suppressed because it is too large Load Diff

View File

@ -1,69 +1,264 @@
import React, {useEffect, useState} from "react"; import React, { useEffect, useState } from "react";
import {Menu} from "antd"; import { Menu } from "antd";
import {Link} from "react-router-dom"; import { Link } from "react-router-dom";
import {HomeOutlined,} from "@ant-design/icons"; import {
import {observer} from "mobx-react-lite"; AppstoreOutlined,
import {useStore} from "../../utils/useStore"; DatabaseOutlined,
FileAddOutlined,
FileProtectOutlined,
FileSyncOutlined,
HomeOutlined,
MenuUnfoldOutlined,
ProfileOutlined,
ShoppingCartOutlined,
UserOutlined,
SlackOutlined,
CodepenOutlined,
WindowsOutlined,
AliyunOutlined,
SettingOutlined,
IdcardOutlined,
AppstoreAddOutlined,
DollarCircleOutlined,
PieChartOutlined,
UsergroupAddOutlined,
UnorderedListOutlined,
FormOutlined,
} from "@ant-design/icons";
import { observer } from "mobx-react-lite";
import { useStore } from "../../utils/useStore";
import { LINKS } from "../../routes/app";
const {SubMenu} = Menu; const { SubMenu } = Menu;
export const MenuList = observer((props) => { export const MenuList = observer((props) => {
const store = useStore(); const store = useStore();
useEffect(() => { useEffect(() => {}, []);
}, []);
const [setKeys, setSetKeys] = useState(["dashboard"]); const [setKeys, setSetKeys] = useState(["dashboard"]);
return ( return (
<Menu <Menu
defaultOpenKeys={["sub4"]} defaultOpenKeys={["sub4"]}
theme="light" theme="light"
style={{ style={{
backgroundColor: "transparent", backgroundColor: "transparent",
borderRightWidth: 0, borderRightWidth: 0,
fontWeight: 400, fontWeight: 400,
paddingLeft: 0, paddingLeft: 0,
}} }}
onClick={({keyPath, item}) => { onClick={({ keyPath, item }) => {
props.closeLeftDrawer(); props.closeLeftDrawer();
}} }}
mode="inline" mode="inline"
selectedKeys={setKeys} selectedKeys={setKeys}
onSelect={({setKeys, item, selectedKeys}) => setSetKeys(selectedKeys)} onSelect={({ setKeys, item, selectedKeys }) => setSetKeys(selectedKeys)}
overflowedIndicator={0} overflowedIndicator={0}
forceSubMenuRender={true} forceSubMenuRender={true}
> >
<Menu.Item key="home"> {store.authentication.userData.role === "Admin" && (
<Link to={'/app/home'}> <Menu.Item key="home">
<HomeOutlined/> <Link to={LINKS.HOME}>
<span>Home</span> <HomeOutlined />
</Link> <span>Beranda</span>
</Menu.Item> </Link>
{store.authentication.userData.role !== 'Retail' && <Menu.Item key="membership"> </Menu.Item>
<Link to={'/app/membership'}> )}
<HomeOutlined/> {store.authentication.userData.role === "Admin Partner" && (
<span>Membership</span> <Menu.Item key="home">
</Link> <Link to={LINKS.HOME}>
</Menu.Item>} <HomeOutlined />
{store.authentication.userData.role !== 'Retail' && <Menu.Item key="product"> <span>Beranda</span>
<Link to={'/app/product'}> </Link>
<HomeOutlined/> </Menu.Item>
<span>Product</span> )}
</Link> {store.authentication.userData.role === "Admin" && (
</Menu.Item>} <SubMenu
{store.authentication.userData.role === 'Retail' && <Menu.Item key="transaction"> key="keanggotaan"
<Link to={'/app/transaction'}> icon={<UsergroupAddOutlined />}
<HomeOutlined/> title="Keanggotaan"
<span>Transaction</span> >
</Link> <Menu.Item key="membership">
</Menu.Item>} <Link to={LINKS.MEMBERSHIP}>
{/*<Menu.Item key="about">*/} <UnorderedListOutlined />
{/* <Link to={'/app/about'}>*/} <span>List Anggota</span>
{/* <CalendarOutlined/>*/} </Link>
{/* <span>About</span>*/} </Menu.Item>
{/* </Link>*/} <Menu.Item key="konfirmasi">
{/*</Menu.Item>*/} <Link to={LINKS.KONFIRMASI}>
<Menu.Divider style={{background: "transparent", paddingTop: 15}}/> <FormOutlined />
</Menu> <span>Konfirm Retail</span>
); </Link>
</Menu.Item>
</SubMenu>
)}
{store.authentication.userData.role === "Supervisor" && (
<SubMenu
key="keanggotaan"
icon={<UsergroupAddOutlined />}
title="Keanggotaan"
>
<Menu.Item key="membership">
<Link to={LINKS.MEMBERSHIP}>
<UnorderedListOutlined />
<span>List Anggota</span>
</Link>
</Menu.Item>
<Menu.Item key="konfirmasi">
<Link to={LINKS.KONFIRMASI}>
<FormOutlined />
<span>Konfirm Retail</span>
</Link>
</Menu.Item>
</SubMenu>
)}
{/* {store.authentication.userData.role === "Sales" && (
<SubMenu
key="keanggotaan"
icon={<UsergroupAddOutlined />}
title="Keanggotaan"
>
<Menu.Item key="membership">
<Link to={LINKS.MEMBERSHIP}>
<UnorderedListOutlined />
<span>List Anggota</span>
</Link>
</Menu.Item>
</SubMenu>
)} */}
{/* {store.authentication.userData.role === "Supervisor" && (
<Menu.Item key="membership">
<Link to={LINKS.MEMBERSHIP}>
<IdcardOutlined />
<span>Keanggotaan</span>
</Link>
</Menu.Item>
)} */}
{store.authentication.userData.role === "Sales" && (
<Menu.Item key="membership">
<Link to={LINKS.MEMBERSHIP}>
<IdcardOutlined />
<span>Keanggotaan</span>
</Link>
</Menu.Item>
)}
{store.authentication.userData.role === "Admin" && (
<SubMenu key="config" icon={<SettingOutlined />} title="Config">
<Menu.Item key="partner">
<Link to={LINKS.PARTNER}>
<WindowsOutlined />
<span>Rekanan</span>
</Link>
</Menu.Item>
<Menu.Item key="commision">
<Link to={LINKS.COMMISSION}>
<SlackOutlined />
<span>Komisi</span>
</Link>
</Menu.Item>
<Menu.Item key="supplier">
<Link to={LINKS.SUPPLIER}>
<AppstoreOutlined />
<span>Supplier</span>
</Link>
</Menu.Item>
</SubMenu>
)}
{store.authentication.userData.role === "Admin" && (
<SubMenu
key="product-main"
icon={<AppstoreAddOutlined />}
title="Produk"
>
<Menu.Item key="produk">
<Link to={LINKS.PRODUCT}>
<PieChartOutlined />
<span>Produk</span>
</Link>
</Menu.Item>
{store.authentication.userData.role === "Admin" && (
<Menu.Item key="sub-category">
<Link to={LINKS.SUBCATEGORY}>
<FileSyncOutlined />
<span>Sub Kategori</span>
</Link>
</Menu.Item>
)}
{store.authentication.userData.role === "Admin" && (
<Menu.Item key="category">
<Link to={LINKS.CATEGORY}>
<FileAddOutlined />
<span>Kategori</span>
</Link>
</Menu.Item>
)}
</SubMenu>
)}
{store.authentication.userData.role === "Admin Partner" && (
<Menu.Item key="retail">
<Link to={LINKS.PRODUCT}>
<PieChartOutlined />
<span>Produk</span>
</Link>
</Menu.Item>
)}
{store.authentication.userData.role === "Sales" && (
<Menu.Item key="retail">
<Link to={LINKS.PRODUCT}>
<PieChartOutlined />
<span>Produk</span>
</Link>
</Menu.Item>
)}
{store.authentication.userData.role === "Supervisor" && (
<Menu.Item key="retail">
<Link to={LINKS.PRODUCT}>
<PieChartOutlined />
<span>Produk</span>
</Link>
</Menu.Item>
)}
{store.authentication.userData.role === "Retail" && (
<Menu.Item key="transaction">
<Link to={LINKS.TRANSACTION}>
<ShoppingCartOutlined />
<span>Transaksi</span>
</Link>
</Menu.Item>
)}
{store.authentication.userData.role !== "Admin Partner" && (
<SubMenu
key="payback-main"
icon={<DollarCircleOutlined />}
title="Pembayaran"
>
{store.authentication.userData.role !== "Retail" && (
<Menu.Item key="payback-to-user">
<Link to={LINKS.PAYBACK}>
<FileProtectOutlined />
<span>Konfirmasi</span>
</Link>
</Menu.Item>
)}
{store.authentication.userData.role !== "Admin" && (
<Menu.Item key="payback-from-user">
<Link to={LINKS.PAYBACK_CREATED}>
<FileProtectOutlined />
<span>Dibuat oleh Saya</span>
</Link>
</Menu.Item>
)}
</SubMenu>
)}
{store.authentication.userData.role !== "Admin" && (
<Menu.Item key="profile">
<Link to={LINKS.PROFILE}>
<UserOutlined />
<span>Profil</span>
</Link>
</Menu.Item>
)}
<Menu.Divider style={{ background: "transparent", paddingTop: 15 }} />
</Menu>
);
}); });

View File

@ -0,0 +1,79 @@
import React, {useContext, useEffect, useState} from "react";
import { Button, Card, Col, Input, Row, Tabs ,message} from "antd";
import { FilterOutlined, PlusSquareOutlined } from "@ant-design/icons";
import { BreadcumbComponent } from "../../component/BreadcumbComponent";
import { useStore } from "../../utils/useStore";
import { observer } from "mobx-react-lite";
import { CommissionComponent } from "../../component/CommissionComponent";
import { LINKS } from "../../routes/app";
import {ModalLoaderContext} from "../../utils/modal";
const { TabPane } = Tabs;
const { Search } = Input;
export const Commission = observer(() => {
const store = useStore();
const modalLoader = useContext(ModalLoaderContext);
useEffect(() => {
const init = async () => {
try {
modalLoader.setLoading(true);
//await store.commission.getDataCategories();
await store.commission.getData();
modalLoader.setLoading(false);
} catch (e) {
modalLoader.setLoading(false);
if (e.response?.body?.message) {
message.error(e.response.body.message);
return;
}
message.error(e.message);
}
};
init();
}, []);
const routeData = [
{
route: LINKS.HOME,
name: "Beranda",
},
{
route: LINKS.COMMISSION,
name: <span style={{ fontWeight: "bold" }}>Commission</span>,
},
];
return (
<div className={["ppob-container"].join(" ")}>
<BreadcumbComponent data={routeData} />
<Card>
{/* <Row style={{ marginBottom: 20 }}> */}
{/* <Col span={12}>
<Button>
<FilterOutlined />
Filter
</Button>
</Col> */}
{/* <Col span={24} style={{ textAlign: "right" }}>
<Search
placeholder="input search text"
style={{
width: store.ui.mediaQuery.isMobile ? 160 : 200,
marginRight: store.ui.mediaQuery.isMobile ? 0 : 10,
marginBottom: store.ui.mediaQuery.isMobile ? 10 : 0,
}}
/>
<Button onClick={() => (store.commission.visibleModalCommission = true)}>
<PlusSquareOutlined /> New
</Button>
</Col>
</Row> */}
<div style={{marginTop:20}}> <CommissionComponent/></div>
</Card>
</div>
);
});

View File

@ -0,0 +1,76 @@
import React, {useContext, useEffect, useState} from "react";
import {Button, Card, Col, Input, Row, Tabs,message} from "antd";
import {FilterOutlined, PlusSquareOutlined,} from "@ant-design/icons";
import {BreadcumbComponent} from "../../component/BreadcumbComponent";
import {useStore} from "../../utils/useStore";
import {observer} from "mobx-react-lite";
import {PartnerComponent} from "../../component/PartnerComponent";
import {LINKS} from "../../routes/app";
import {ModalLoaderContext} from "../../utils/modal";
const {Search} = Input;
export const Partner = observer(() => {
const store = useStore();
const modalLoader = useContext(ModalLoaderContext);
useEffect(() => {
const init = async () => {
try {
modalLoader.setLoading(true);
await store.partner.getData();
modalLoader.setLoading(false);
} catch (e) {
modalLoader.setLoading(false);
if (e.response?.body?.message) {
message.error(e.response.body.message);
return;
}
message.error(e.message);
}
};
init();
}, []);
const routeData = [
{
route: LINKS.HOME,
name: "Beranda",
},
{
route: LINKS.PARTNER,
name: <span style={{fontWeight: 'bold'}}>Rekanan</span>,
},
];
return (
<div className={["ppob-container"].join(" ")}>
<BreadcumbComponent data={routeData}/>
<Card>
<Row style={{marginBottom: 20}}>
{/* <Col span={12}>
<Button>
<FilterOutlined/>
Filter
</Button>
</Col> */}
<Col span={24} style={{textAlign: "right"}}>
{/* <Search
placeholder="input search text"
style={{
width: store.ui.mediaQuery.isMobile ? 160 : 200,
marginRight: store.ui.mediaQuery.isMobile ? 0 : 10,
marginBottom: store.ui.mediaQuery.isMobile ? 10 : 0,
}}
/> */}
<Button onClick={() => store.partner.visibleModalPartner = true}>
<PlusSquareOutlined/> New
</Button>
</Col>
</Row>
<PartnerComponent/>
</Card>
</div>
);
});

View File

@ -0,0 +1,94 @@
import React, {useContext, useEffect, useState} from "react";
import {
Button,
Card,
Col,
Input,
Row,
Tabs,
Form,
message,
Modal,
Select,
} from "antd";
import {
FilterOutlined,
PlusSquareOutlined,
DownloadOutlined,
} from "@ant-design/icons";
import { BreadcumbComponent } from "../../component/BreadcumbComponent";
import { useStore } from "../../utils/useStore";
import { observer } from "mobx-react-lite";
import { SupplierComponent } from "../../component/SupplierComponent";
import { LINKS } from "../../routes/app";
import {ModalLoaderContext} from "../../utils/modal";
const { Search } = Input;
export const Supplier = observer(() => {
const store = useStore();
const modalLoader = useContext(ModalLoaderContext);
useEffect(() => {
const init = async () => {
try {
modalLoader.setLoading(true);
await store.supplier.getData();
modalLoader.setLoading(false);
} catch (e) {
modalLoader.setLoading(false);
if (e.response?.body?.message) {
message.error(e.response.body.message);
return;
}
message.error(e.message);
}
};
init();
}, []);
const routeData = [
{
route: LINKS.HOME,
name: "Beranda",
},
{
route: LINKS.SUPPLIER,
name: <span style={{ fontWeight: "bold" }}>Supplier</span>,
},
];
return (
<div className={["ppob-container"].join(" ")}>
<BreadcumbComponent data={routeData} />
<Card>
<Row style={{ marginBottom: 20 }}>
{/* <Col span={12}>
<Button>
<FilterOutlined />
Filter
</Button>
</Col> */}
<Col span={24} style={{ textAlign: "right" }}>
{/* <Search
placeholder="input search text"
style={{
width: store.ui.mediaQuery.isMobile ? 160 : 200,
marginRight: store.ui.mediaQuery.isMobile ? 10 : 10,
marginBottom: store.ui.mediaQuery.isMobile ? 10 : 0,
}}
onSearch={(value) => console.log(value)}
/> */}
<Button
onClick={() => (store.supplier.visibleModalSupplier = true)}
>
<PlusSquareOutlined /> New
</Button>
</Col>
</Row>
<SupplierComponent />
</Card>
</div>
);
});

View File

@ -1,25 +1,493 @@
import React from "react"; import React, { useContext, useEffect } from "react";
import {Button, PageHeader} from "antd"; import { Button, PageHeader, Card, Row, Col, message, Table } from "antd";
import {store} from "../../utils/useStore"; import { useStore } from "../../utils/useStore";
import {observer} from "mobx-react-lite"; import { observer } from "mobx-react-lite";
import {
DropboxOutlined,
DollarCircleOutlined,
BarChartOutlined,
} from "@ant-design/icons";
import { ModalLoaderContext } from "../../utils/modal";
import Title from "antd/lib/skeleton/Title";
export const Home = observer(() => { export const Home = observer(() => {
return <div className={["ppob-container"].join(" ")}> const modalLoader = useContext(ModalLoaderContext);
<PageHeader const store = useStore();
useEffect(() => {
const init = async () => {
try {
modalLoader.setLoading(true);
const isAdmin = store.authentication.userData.role === "Admin";
await getData();
await store.membership.getDataBySuperior();
await store.partner.getData();
if (isAdmin) {
await store.transaction.getDataTransaction();
await store.transaction.getDataTransactionB2B();
} else {
await store.transaction.getDataTransactionPartner();
}
await store.role.getData(isAdmin);
modalLoader.setLoading(false);
} catch (e) {
console.error(e);
modalLoader.setLoading(false);
if (e.response?.body?.message) {
message.error(e.response.body.message);
return;
}
message.error(e.message);
}
};
init();
}, []);
const getData = async () => {
store.authentication.userData.role === "Admin"
? await store.membership.getData()
: await store.membership.getDataBySuperior();
};
return (
<div>
{store.ui.mediaQuery.isDesktop &&
store.authentication.userData.role === "Admin" && (
<Row
style={{ style={{
padding: 0, marginTop: 30,
margin: 0, marginLeft: 30,
height: 40,
backgroundColor: "transparent",
}} }}
title={"Home"} >
> <Card
</PageHeader> className={"shadow"}
<Button onClick={() => { hoverable
store.ui.setTestValue(); title={
}}>{store.ui.testValue}</Button> <span
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Accusamus aut recusandae velit! Consequatur corporis, style={{
eum fuga, harum incidunt laboriosam minus necessitatibus neque non nostrum pariatur tempore. Dignissimos impedit fontSize: 20,
rem tempora! fontStyle: "bold",
textAlign: "center",
}}
>
{store.transaction.dataTransaction.total_transaction}{" "}
Transaksi B2C
</span>
}
style={{
marginLeft: 20,
height: 200,
marginBottom: 10,
borderColor: "salmon",
width: "45%",
}}
>
<Row>
<Row style={{ marginTop: 20 }}>
<Col style={{ marginRight: 40 }}>
<p>
<h4>
Total Penjualan :{" "}
{new Intl.NumberFormat("id-ID", {
style: "currency",
currency: "IDR",
}).format(
store.transaction.dataTransaction.total_amount
)}
</h4>
<h4>
Total Modal :{" "}
{new Intl.NumberFormat("id-ID", {
style: "currency",
currency: "IDR",
}).format(
store.transaction.dataTransaction.total_modal
)}
</h4>
</p>
</Col>
<Col>
<p>
<h4>
Total Profit :{" "}
{new Intl.NumberFormat("id-ID", {
style: "currency",
currency: "IDR",
}).format(
store.transaction.dataTransaction.total_profit
)}
</h4>
<h4>
Total Komisi :{" "}
{new Intl.NumberFormat("id-ID", {
style: "currency",
currency: "IDR",
}).format(
store.transaction.dataTransaction.total_commission
)}
</h4>
</p>
</Col>
</Row>
</Row>
</Card>
<Card
className={"shadow"}
hoverable
title={
<span
style={{
fontSize: 20,
fontStyle: "bold",
textAlign: "center",
}}
>
{store.transaction.dataTransactionB2B.total_transaction}{" "}
Transaksi B2B
</span>
}
style={{
marginLeft: 20,
height: 200,
marginBottom: 10,
borderColor: "salmon",
width: "45%",
}}
>
<Row>
<Row style={{ marginTop: 20 }}>
<Col style={{ marginRight: 40 }}>
<p>
<h4>
Total Penjualan :{" "}
{new Intl.NumberFormat("id-ID", {
style: "currency",
currency: "IDR",
}).format(
store.transaction.dataTransactionB2B.total_amount
)}
</h4>
<h4>
Total Modal :{" "}
{new Intl.NumberFormat("id-ID", {
style: "currency",
currency: "IDR",
}).format(
store.transaction.dataTransactionB2B.total_modal
)}
</h4>
</p>
</Col>
<Col>
<p>
<h4>
Total Profit :{" "}
{new Intl.NumberFormat("id-ID", {
style: "currency",
currency: "IDR",
}).format(
store.transaction.dataTransactionB2B.total_profit
)}
</h4>
</p>
</Col>
</Row>
</Row>
</Card>
{/*<Card*/}
{/* className={"shadow"}*/}
{/* hoverable*/}
{/* style={{*/}
{/* marginLeft: 20,*/}
{/* height: 200,*/}
{/* marginBottom: 10,*/}
{/* borderColor: "salmon",*/}
{/* width: "30%",*/}
{/* }}*/}
{/*>*/}
{/* <Row>*/}
{/* <PageHeader title={<span>Total Keuntungan B2C</span>}>*/}
{/* <span>*/}
{/* {new Intl.NumberFormat("id-ID", {*/}
{/* style: "currency",*/}
{/* currency: "IDR",*/}
{/* }).format(store.authentication.profileData?.wallet || 0)}*/}
{/* </span>*/}
{/* </PageHeader>*/}
{/* </Row>*/}
{/*</Card>*/}
</Row>
)}
{store.ui.mediaQuery.isDesktop &&
store.authentication.userData.role === "Admin Partner" && (
<Row
style={{
marginTop: 30,
marginLeft: 30,
}}
>
<Card
className={"shadow"}
hoverable
style={{
marginLeft: 20,
height: 200,
marginBottom: 10,
borderColor: "salmon",
width: "45%",
}}
>
<Row>
<PageHeader title={<span>Total Transaksi</span>}>
<p>
<h4>
{
store.transaction.dataTransactionPartner
.total_transaction
}
</h4>
</p>
</PageHeader>
</Row>
</Card>
<Card
className={"shadow"}
hoverable
style={{
marginLeft: 20,
height: 200,
marginBottom: 10,
borderColor: "salmon",
width: "45%",
}}
>
<Row>
<PageHeader title={<span>Total Penjualan</span>}>
<p>
<h4>
{new Intl.NumberFormat("id-ID", {
style: "currency",
currency: "IDR",
}).format(
store.transaction.dataTransactionPartner.total_amount
)}
</h4>
</p>
</PageHeader>
</Row>
</Card>
</Row>
)}
{store.ui.mediaQuery.isMobile &&
store.authentication.userData.role === "Admin" && (
<Row
style={{
marginTop: 20,
marginLeft: 10,
}}
>
<Card
className={"shadow"}
hoverable
title={
<span
style={{
fontSize: 20,
fontStyle: "bold",
textAlign: "center",
}}
>
{store.transaction.dataTransaction.total_transaction}{" "}
Transaksi B2C
</span>
}
style={{
marginLeft: 10,
height: 220,
marginBottom: 10,
borderColor: "salmon",
width: "90%",
}}
>
<Row>
<Row>
<Col style={{ marginRight: 40 }}>
<p>
<h4>
Total Penjualan :{" "}
{new Intl.NumberFormat("id-ID", {
style: "currency",
currency: "IDR",
}).format(
store.transaction.dataTransaction.total_amount
)}
</h4>
<h4>
Total Modal :{" "}
{new Intl.NumberFormat("id-ID", {
style: "currency",
currency: "IDR",
}).format(
store.transaction.dataTransaction.total_modal
)}
</h4>
</p>
</Col>
<Col>
<p>
<h4>
Total Profit :{" "}
{new Intl.NumberFormat("id-ID", {
style: "currency",
currency: "IDR",
}).format(
store.transaction.dataTransaction.total_profit
)}
</h4>
<h4>
Total Komisi :{" "}
{new Intl.NumberFormat("id-ID", {
style: "currency",
currency: "IDR",
}).format(
store.transaction.dataTransaction.total_commission
)}
</h4>
</p>
</Col>
</Row>
</Row>
</Card>
<Card
className={"shadow"}
hoverable
title={
<span
style={{
fontSize: 20,
fontStyle: "bold",
textAlign: "center",
}}
>
{store.transaction.dataTransactionB2B.total_transaction}{" "}
Transaksi B2B
</span>
}
style={{
marginLeft: 10,
height: 200,
borderColor: "salmon",
width: "90%",
}}
>
<Row>
<Row>
<Col style={{ marginRight: 40 }}>
<p>
<h4>
Total Penjualan :{" "}
{new Intl.NumberFormat("id-ID", {
style: "currency",
currency: "IDR",
}).format(
store.transaction.dataTransactionB2B.total_amount
)}
</h4>
<h4>
Total Modal :{" "}
{new Intl.NumberFormat("id-ID", {
style: "currency",
currency: "IDR",
}).format(
store.transaction.dataTransactionB2B.total_modal
)}
</h4>
</p>
</Col>
<Col>
<p>
<h4>
Total Profit :{" "}
{new Intl.NumberFormat("id-ID", {
style: "currency",
currency: "IDR",
}).format(
store.transaction.dataTransactionB2B.total_profit
)}
</h4>
</p>
</Col>
</Row>
</Row>
</Card>
</Row>
)}
{store.ui.mediaQuery.isMobile &&
store.authentication.userData.role === "Admin Partner" && (
<Row
style={{
marginTop: 20,
marginLeft: 10,
}}
>
<Card
className={"shadow"}
hoverable
style={{
marginLeft: 10,
height: 200,
marginBottom: 10,
borderColor: "salmon",
width: "90%",
}}
>
<Row>
<PageHeader title={<span>Total Transaksi</span>}>
<p>
<h4>
{
store.transaction.dataTransactionPartner
.total_transaction
}
</h4>
</p>
</PageHeader>
</Row>
</Card>
<Card
className={"shadow"}
hoverable
style={{
marginLeft: 10,
height: 200,
marginBottom: 10,
borderColor: "salmon",
width: "90%",
}}
>
<Row>
<PageHeader title={<span>Total Penjualan</span>}>
<p>
<h4>
{new Intl.NumberFormat("id-ID", {
style: "currency",
currency: "IDR",
}).format(
store.transaction.dataTransactionPartner.total_amount
)}
</h4>
</p>
</PageHeader>
</Row>
</Card>
</Row>
)}
</div> </div>
);
}); });

View File

@ -1,85 +1,119 @@
import React from "react"; import React from "react";
import {observer} from 'mobx-react-lite'; import { observer } from "mobx-react-lite";
import {useStore} from "../../utils/useStore"; import { useStore } from "../../utils/useStore";
import {Button, Card, Col, Form, Input, message, Row, Typography} from 'antd'; import { Button, Card, Col, Form, Input, message, Row, Typography } from "antd";
import {useHistory} from "react-router-dom"; import { useHistory } from "react-router-dom";
import { LINKS } from "../../routes/app";
export const Login = observer(() => { export const Login = observer(() => {
const store = useStore(); const store = useStore();
let history = useHistory(); let history = useHistory();
const [form] = Form.useForm(); const [form] = Form.useForm();
const handleLogin = async (params) => { const handleLogin = async (params) => {
try { try {
await store.authentication.login({ await store.authentication.login({
username: params.username, username: params.username,
password: params.password, password: params.password,
}); });
history.push('/app/home'); } catch (e) {
} catch (e) { if (e.response?.body?.message) {
if (e.response?.body?.message) { message.error(e.response.body.message);
message.error(e.response.body.message); return;
return; }
} message.error(e.message);
message.error(e.message);
}
} }
if (
store.authentication.userData.role === "Sales" ||
store.authentication.userData.role === "Supervisor"
) {
history.push(LINKS.MEMBERSHIP);
} else if (store.authentication.userData.role === "Retail") {
history.push(LINKS.TRANSACTION);
} else {
history.push(LINKS.HOME);
}
};
return ( return (
<div style={{width: '100vw', display: 'flex', justifyContent: 'center'}}> <div style={{ width: "100vw", display: "flex", justifyContent: "center" }}>
<Row justify={'center'}> <Row justify={"center"}>
<Col> <Col>
<div style={{ <div
display: 'flex', style={{
justifyContent: 'flex-start', display: "flex",
marginTop: '5vh', justifyContent: "flex-start",
flexDirection: 'column', marginTop: "5vh",
alignItems: 'center', flexDirection: "column",
}}> alignItems: "center",
<div style={{display: 'flex', flexDirection: 'column', alignItems: 'stretch'}}> }}
<Typography.Paragraph >
style={{ <div
margin: 0, style={{
padding: 0, display: "flex",
fontSize: 20, flexDirection: "column",
marginLeft: 5, alignItems: "stretch",
fontWeight: 600, }}
color: "#413d3e", >
}} <Typography.Paragraph
> style={{
Boilerplate margin: 0,
</Typography.Paragraph> padding: 0,
</div> fontSize: 20,
<Card marginLeft: 5,
style={{width: 320, textAlign: 'center'}} fontWeight: 600,
headStyle={{fontSize: 13, fontWeight: 200}} color: "#413d3e",
className={"shadow"} }}
bordered={true} >
title={'Sign in to your account'} PPOB
> </Typography.Paragraph>
<Form </div>
layout={"vertical"} <Card
form={form} style={{ width: 320, textAlign: "center" }}
onFinish={handleLogin} headStyle={{ fontSize: 13, fontWeight: 200 }}
className={"w-9/12"} className={"shadow"}
> bordered={true}
<Form.Item label="Username" name="username" title={"Sign in to your account"}
rules={[{required: true, message: 'Please input your username!'}]}> >
<Input/> <Form
</Form.Item> layout={"vertical"}
<Form.Item label="Password" name="password" form={form}
rules={[{required: true, message: 'Please input your password!'}]}> onFinish={handleLogin}
<Input.Password/> className={"w-9/12"}
</Form.Item> >
<div className={"flex flex-row justify-between content-center"}> <Form.Item
<Button type="primary" htmlType="submit" label="Username"
loading={store.authentication.isLoginLoading}>Submit</Button> name="username"
</div> rules={[
</Form> { required: true, message: "Please input your username!" },
</Card> ]}
</div> >
</Col> <Input />
</Row> </Form.Item>
</div> <Form.Item
); label="Password"
name="password"
rules={[
{ required: true, message: "Please input your password!" },
]}
>
<Input.Password />
</Form.Item>
<div className={"flex flex-row justify-between content-center"}>
<Button
type="primary"
htmlType="submit"
loading={store.authentication.isLoginLoading}
>
Submit
</Button>
</div>
</Form>
</Card>
</div>
</Col>
</Row>
</div>
);
}); });

View File

@ -0,0 +1,940 @@
import React, { useContext, useEffect, useState } from "react";
import {
Button,
Card,
Col,
message,
Row,
Space,
Table,
Typography,
Tabs,
List,
Tag,
Divider,
Image,
Modal,
Form,
DatePicker,
} from "antd";
import { UserOutlined, FilterOutlined } from "@ant-design/icons";
import { BreadcumbComponent } from "../../component/BreadcumbComponent";
import { LINKS } from "../../routes/app";
import { useStore } from "../../utils/useStore";
import { observer } from "mobx-react-lite";
import { format, parseISO } from "date-fns";
import { ModalLoaderContext } from "../../utils/modal";
import { useParams } from "react-router-dom";
import { appConfig } from "../../config/app";
import { MembershipModal } from "./MembershipModal";
import moment from "moment";
const { Title, Text } = Typography;
const { TabPane } = Tabs;
export const DetailUser = observer(() => {
const store = useStore();
const [form] = Form.useForm();
const modalLoader = useContext(ModalLoaderContext);
const { id } = useParams();
const [visibleModal, setVisibleModal] = useState(false);
const [initialData, setInitialData] = useState({});
const [confirmLoading, setConfirmLoading] = useState(false);
const [filterStart, setFilterStart] = useState([]);
const [filterEnd, setFilterEnd] = useState([]);
const [visibleHis, setVisibleHis] = useState(true);
useEffect(() => {
(async () => {
modalLoader.setLoading(true);
await getData();
modalLoader.setLoading(false);
})();
return () => {
store.membership.dataDetail = {};
store.transaction.dataHistoryTopUp = [];
store.authentication.dataProfit = [];
store.transaction.dataDetailHistoryTransaction = [];
};
}, []);
const getData = async () => {
const isAdmin = store.authentication.userData.role === "Admin";
await Promise.allSettled([
store.transaction.getDataHistoryTopUp(id),
store.authentication.getProfit(id),
store.authentication.getProfile(),
store.transaction.getDetailHistoryTransaction(id),
store.membership.getDetail(id),
store.role.getData(isAdmin),
]);
};
const changeStatus = async (id, is_active) => {
let status = is_active ? "inactive" : "active";
let status2 = is_active ? "Inactivating" : "Activating";
console.log(status, "status terbaru");
try {
modalLoader.setLoading(true);
const response = await store.membership.changeStatus(id, status);
modalLoader.setLoading(false);
response?.body?.statusCode === 201 || response?.body?.statusCode === 200
? message.success(`Success ${status2} Membership`)
: message.error(`Failed ${status2} Membership`);
await getData();
} catch (err) {
modalLoader.setLoading(false);
message.error(`Failed ${status2} Membership`);
}
};
const withdrawProfit = async (id) => {
try {
modalLoader.setLoading(true);
const response = await store.membership.withdrawProfit(id);
modalLoader.setLoading(false);
response?.body?.statusCode === 201 || response?.body?.statusCode === 200
? message.success(`Success Withdraw Profit`)
: message.error(`Failed Withdraw Profit`);
await getData();
} catch (err) {
modalLoader.setLoading(false);
message.error(`Failed Withdraw Profit`);
}
};
const onSubmit = async (data) => {
data.superior = true;
setConfirmLoading(true);
modalLoader.setLoading(true);
try {
let response;
if (initialData.isChangePassword) {
response = await store.membership.changePassword(initialData.id, data);
} else {
response = await store.membership.update(initialData.id, data);
}
setVisibleModal(false);
if (response?.body?.statusCode === 200) {
message.success(
initialData.isChangePassword
? "Success Change Member Password"
: "Success Update Data Member"
);
} else {
message.error(
initialData.isChangePassword
? "Failed Change Member Password"
: "Failed Update Data Member"
);
}
await getData();
} catch (e) {
modalLoader.setLoading(false);
message.error(
initialData.isChangePassword
? "Failed Update Member Password"
: "Failed Update Data Member"
);
}
modalLoader.setLoading(false);
setConfirmLoading(false);
};
const handleResend = async (id) => {
modalLoader.setLoading(true);
try {
const response = await store.approval.resendUser(id);
console.log(response);
response.body.statusCode !== 201 && response.body.statusCode !== 200
? message.error(response?.body?.message || `Failed Approve`)
: message.success(response?.body?.message || `Success Approve`);
await getData();
} catch (e) {
console.error(e, "apa errornya");
message.error(e.response?.body?.message || "Fail Approve");
}
modalLoader.setLoading(false);
};
const columns = [
{
title: "Amount",
dataIndex: "amount",
key: "amount",
render: (text) =>
new Intl.NumberFormat("id-ID", {
style: "currency",
currency: "IDR",
}).format(text),
},
{
title: "Transaction Date",
dataIndex: "transaction_date",
key: "transaction_date",
render: (text, record) => {
return (
<Text>
{format(parseISO(record.transaction_date), "dd MMMM yyyy")}
</Text>
);
},
},
];
const column = [
{
title: "Nama Produk",
dataIndex: "name",
key: "name",
},
{
title: "Price",
dataIndex: "price",
key: "price",
render: (text) =>
new Intl.NumberFormat("id-ID", {
style: "currency",
currency: "IDR",
}).format(text),
},
{
title: "Pembeli",
dataIndex: "buyer",
key: "buyer",
},
{
title: "Tujuan",
dataIndex: "transaction_destination",
key: "transaction_destination",
},
{
title: "Kode Transaksi",
dataIndex: "transaction_code",
key: "transaction_code",
},
{
title: "Status",
dataIndex: "status",
key: "status",
render: (text, record) => {
return (
<Tag
color={
record.status === 1
? "success"
: record.status === 0
? "warning"
: "processing"
}
>
{record.status === 1
? "Success"
: record.status === 0
? "Pending"
: "Failed"}
</Tag>
);
},
},
{
title: "No Seri",
dataIndex: "seri_number",
key: "seri_number",
},
{
title: "IDTrx Mitra",
dataIndex: "partner_transaction_code",
key: "partner_transaction_code",
},
{
title: "Tanggal Transaksi",
dataIndex: "created_at",
key: "created_at",
width: "15%",
render: (text, record) => {
return (
<Text>
{format(parseISO(record.created_at), "dd-MM-yyyy HH:mm:ss")}
</Text>
);
},
},
{
title: "Alasan Gagal",
dataIndex: "failed_reason",
key: "failed_reason",
},
];
const routeData = [
{
route: LINKS.HOME,
name: "Beranda",
},
{
route: LINKS.MEMBERSHIP,
name: <span style={{ fontWeight: "bold" }}>Keanggotaan</span>,
},
{
route: LINKS.USER_DETAIL.replace(":id", id),
name: <span style={{ fontWeight: "bold" }}>Detail User</span>,
},
];
const dataRoute = [
{
route: LINKS.MEMBERSHIP,
name: "Keanggotaan",
},
{
route: LINKS.USER_DETAIL.replace(":id", id),
name: <span style={{ fontWeight: "bold" }}>Detail User</span>,
},
];
const styleSaldoTitle = store.ui.mediaQuery.isDesktop
? {
display: "flex",
justifyContent: "center",
}
: { fontSize: "0.75rem" };
const styleSaldoContent = store.ui.mediaQuery.isDesktop
? {
fontSize: "1.25rem",
display: "flex",
justifyContent: "center",
}
: null;
const data = store.authentication.listImage;
console.log(data, "ini data");
console.log(
store.authentication.dataProfit.userDetail?.image_identity,
"detail"
);
const handleRemoveFilter = async () => {
store.transaction.filterStart = null;
store.transaction.filterEnd = null;
form.resetFields();
setFilterStart([]);
setFilterEnd([]);
await store.transaction.getDetailHistoryTransaction(
store.authentication.dataProfit.id
);
store.transaction.visibleModalFilterTransaction = false;
};
const handleCancelFilter = async () => {
store.transaction.filterStart = null;
store.transaction.filterEnd = null;
store.transaction.visibleModalFilterTransaction = false;
await store.transaction.getDetailHistoryTransaction(
store.authentication.dataProfit.id
);
};
const handleSubmitFilter = async () => {
const data = form.getFieldsValue();
store.transaction.filterStart = moment(data.start_date).format(
"YYYY-MM-DD HH:mm:ss"
);
store.transaction.filterEnd = moment(data.end_date).format(
"YYYY-MM-DD HH:mm:ss"
);
modalLoader.setLoading(true);
await store.transaction.getDetailHistoryTransaction(
store.authentication.dataProfit.id
);
modalLoader.setLoading(false);
store.transaction.filterStart = null;
store.transaction.filterEnd = null;
store.transaction.visibleModalFilterTransaction = false;
};
const footerLayoutFilter = [
<Button
key={"remove"}
onClick={handleRemoveFilter}
style={{
backgroundColor: "#e74e5e",
color: "#fff",
}}
>
Remove Filter
</Button>,
<Button key={"cancel"} onClick={handleCancelFilter}>
Cancel
</Button>,
<Button
key={"submit"}
onClick={handleSubmitFilter}
style={{
backgroundColor: "#4e79e7",
color: "#fff",
}}
>
Apply
</Button>,
];
return (
<div className={["ppob-container"].join(" ")}>
<BreadcumbComponent
data={
store.authentication.userData.role === "Admin" ? routeData : dataRoute
}
/>
<Card>
<Row style={{ marginBottom: 10 }}>
<Title strong level={2}>
Detail User
</Title>
<Col
lg={store.authentication.userData.role === "Admin" ? 18 : 17}
xs={store.authentication.userData.role === "Admin" ? 18 : 17}
style={{ textAlign: "right" }}
>
<Space
size="small"
align={"center"}
wrap={true}
style={{ textAlign: "center" }}
>
<Button
type={
store.membership.dataDetail.is_active === true
? "danger"
: "primary"
}
onClick={() =>
changeStatus(
store.membership.dataDetail.id,
store.membership.dataDetail.is_active
)
}
>
{store.membership.dataDetail.is_active === true
? "Inactive"
: "Active"}
</Button>
<Button
onClick={() => {
setInitialData({
id: store.membership.dataDetail.id,
name: store.membership.dataDetail.userDetail.name,
username: store.membership.dataDetail.username,
identity_number:
store.membership.dataDetail.userDetail.identity_number,
image_identity:
store.membership.dataDetail?.userDetail.image_identity,
image_store:
store.membership.dataDetail?.userDetail.image_store,
phone_number:
store.membership.dataDetail.userDetail.phone_number,
roleId: store.membership.dataDetail.roles.id,
isChangePassword: false,
});
console.log(
store.membership.dataDetail.userDetail.identity_number
);
console.log(store.membership.dataDetail.userDetail.id);
console.log(
store.membership.dataDetail.userDetail.image_identity
);
console.log(
store.membership.dataDetail.userDetail.image_store,
"ini store"
);
console.log(store.membership.dataDetail.username);
setVisibleModal(true);
}}
>
Edit
</Button>
<Button
onClick={() => {
setInitialData({
id: store.membership.dataDetail.id,
name: store.membership.dataDetail.userDetail.name,
username: store.membership.dataDetail.username,
phone_number:
store.membership.dataDetail.userDetail.phone_number,
roleId: store.membership.dataDetail.roles.id,
isChangePassword: true,
});
setVisibleModal(true);
}}
>
Ganti Password
</Button>
{((store.authentication.userData.role === "Sales" &&
store.membership.dataDetail.is_rejected === true &&
store.membership.dataDetail.is_active === false) ||
(store.authentication.userData.role === "Supervisor" &&
store.membership.dataDetail.is_rejected === true &&
store.membership.dataDetail.is_active === false)) && (
<Button
style={{
backgroundColor: "#1bb91d",
color: "#fff",
}}
onClick={() => handleResend(store.membership.dataDetail.id)}
>
Resend
</Button>
)}
{store.authentication.userData.role === "Admin" && (
<Button
onClick={() => withdrawProfit(store.membership.dataDetail.id)}
>
Withdraw Profit
</Button>
)}
</Space>
</Col>
</Row>
<Row style={{ marginBottom: 20 }}>
<Col lg={12} xs={24}>
<Row>
<Col span={12}>
<Text strong>Name</Text>
</Col>
<Col span={12}>
<Text>{store.authentication.dataProfit.userDetail?.name}</Text>
</Col>
<Col span={12}>
<Text strong>Username</Text>
</Col>
<Col span={12}>
<Text>{store.authentication.dataProfit.username}</Text>
</Col>
<Col span={12}>
<Text strong>Role</Text>
</Col>
<Col span={12}>
<Text>{store.authentication.dataProfit.roles?.name}</Text>
</Col>
<Col span={12}>
<Text strong>Phone Number</Text>
</Col>
<Col span={12}>
<Text>
{store.authentication.dataProfit.userDetail?.phone_number}
</Text>
</Col>
<Col span={12}>
<Text strong>Id Number</Text>
</Col>
<Col span={12}>
<Text>
{store.authentication.dataProfit.userDetail?.identity_number}
</Text>
</Col>
<Col span={12}>
<Text strong>Status</Text>
</Col>
<Col span={12}>
<Text>
{store.authentication.dataProfit.is_active === true
? "Active"
: "Inactive"}
</Text>
</Col>
{store.authentication.userData.role === "Admin" &&
store.authentication.dataProfit.roles?.name !==
"Admin Partner" && (
<Row>
<Col span={12}>
<Text strong>Foto Identitas</Text>
</Col>
<Col span={12}></Col>
<Col span={12}>
<Text>
<Image
src={
store.authentication.dataProfit.userDetail
?.image_identity
? `${appConfig.apiUrl}/config/image/${store.authentication.dataProfit.userDetail?.image_identity}`
: "https://st4.depositphotos.com/14953852/24787/v/600/depositphotos_247872612-stock-illustration-no-image-available-icon-vector.jpg"
}
style={{ width: "10vw" }}
/>
</Text>
</Col>
{store.authentication.dataProfit.roles?.name ===
"Retail" && (
<Col span={24}>
<Text strong>Foto Toko</Text>
<Text>
<Row>
{store.authentication.listImage
? store.authentication.listImage.map(
(item, index) => (
<Image
key={index}
src={
item
? `${appConfig.apiUrl}/config/image/${item}`
: "https://st4.depositphotos.com/14953852/24787/v/600/depositphotos_247872612-stock-illustration-no-image-available-icon-vector.jpg"
}
style={{ width: "10vw", marginRight: 15 }}
/>
)
)
: ""}
</Row>
</Text>
</Col>
)}
</Row>
)}
{store.authentication.userData.role === "Sales" && (
<Row>
<Col span={12}>
<Text strong>Foto Identitas</Text>
</Col>
<Col span={12}></Col>
<Col span={12}>
<Text>
<Image
src={
store.authentication.dataProfit.userDetail
?.image_identity
? `${appConfig.apiUrl}/config/image/${store.authentication.dataProfit.userDetail?.image_identity}`
: "https://st4.depositphotos.com/14953852/24787/v/600/depositphotos_247872612-stock-illustration-no-image-available-icon-vector.jpg"
}
style={{ width: "10vw" }}
/>
</Text>
</Col>
<Col span={24}>
<Text strong>Foto Toko</Text>
<Text>
<Row>
{store.authentication.listImage
? store.authentication.listImage.map(
(item, index) => (
<Image
key={index}
src={
item
? `${appConfig.apiUrl}/config/image/${item}`
: "https://st4.depositphotos.com/14953852/24787/v/600/depositphotos_247872612-stock-illustration-no-image-available-icon-vector.jpg"
}
style={{ width: "10vw", marginRight: 15 }}
/>
)
)
: ""}
</Row>
</Text>
</Col>
</Row>
)}
{store.authentication.userData.role === "Supervisor" && (
<Row>
<Col span={12}>
<Text strong>Foto Identitas</Text>
</Col>
<Col span={12}></Col>
<Col span={12}>
<Text>
<Image
src={
store.authentication.dataProfit.userDetail
?.image_identity
? `${appConfig.apiUrl}/config/image/${store.authentication.dataProfit.userDetail?.image_identity}`
: "https://st4.depositphotos.com/14953852/24787/v/600/depositphotos_247872612-stock-illustration-no-image-available-icon-vector.jpg"
}
style={{ width: "10vw" }}
/>
</Text>
</Col>
</Row>
)}
</Row>
</Col>
<Col lg={12} xs={24}>
<Row>
<Col lg={24} xs={24}>
<Row>
<Col span={store.ui.mediaQuery.isMobile ? 24 : 10}>
<Row justify={"center"}>
<Col lg={12} xs={12}>
<Title strong level={3} style={styleSaldoTitle}>
Saldo
</Title>
</Col>
<Col lg={24} xs={12}>
<Text style={styleSaldoContent}>
{new Intl.NumberFormat("id-ID", {
style: "currency",
currency: "IDR",
}).format(
store.authentication.dataProfit.wallet || 0
)}
</Text>
</Col>
<Col></Col>
</Row>
</Col>
<Col span={store.ui.mediaQuery.isMobile ? 24 : 10}>
<Row justify={"center"}>
<Col lg={12} xs={12}>
<Title strong level={3} style={styleSaldoTitle}>
Profit
</Title>
</Col>
<Col lg={24} xs={12}>
<Text style={styleSaldoContent}>
{new Intl.NumberFormat("id-ID", {
style: "currency",
currency: "IDR",
}).format(
store.authentication.dataProfit?.profit || 0
)}
</Text>
</Col>
<Col></Col>
</Row>
</Col>
</Row>
</Col>
</Row>
</Col>
</Row>
<Row>
<Col span={24}>
<Tabs defaultActiveKey="1">
<TabPane tab="History Top Up" key="1">
<Table
key="1"
hasEmpty
columns={columns}
dataSource={store.transaction.dataHistoryTopUp}
bordered
pagination={{
pageSize: store.transaction.pageSizeHistoryTransaction,
total: store.transaction.total_dataHistoryTransaction,
current: store.transaction.pageHistoryTransaction + 1,
showSizeChanger: true,
simple: false,
}}
onChange={async (page) => {
let pageNumber = page.current;
store.transaction.pageSize = page.pageSize;
store.transaction.page = pageNumber - 1;
modalLoader.setLoading(true);
await getData();
modalLoader.setLoading(false);
}}
/>
</TabPane>
<TabPane tab="History Transaction" key="2">
{store.ui.mediaQuery.isDesktop && (
<div>
<Button
style={{ marginBottom: "1rem", marginLeft: 5 }}
onClick={() => {
store.transaction.visibleModalFilterTransaction = true;
}}
>
<FilterOutlined />
Filter
</Button>
<Table
key="1"
hasEmpty
scroll={{ x: 1300 }}
columns={column}
dataSource={
store.transaction.dataDetailHistoryTransactionDetailUser
}
bordered
pagination={{
pageSize: store.transaction.pageSize,
total: store.transaction.total_data,
current: store.transaction.page + 1,
showSizeChanger: true,
simple: false,
}}
onChange={async (page) => {
let pageNumber = page.current;
store.transaction.pageSize = page.pageSize;
store.transaction.page = pageNumber - 1;
modalLoader.setLoading(true);
await getData();
modalLoader.setLoading(false);
}}
/>
</div>
)}
{store.ui.mediaQuery.isMobile && (
<div>
<Button
style={{ marginBottom: "1rem" }}
onClick={() => {
store.transaction.visibleModalFilterTransaction = true;
}}
>
<FilterOutlined />
Filter
</Button>
<List
itemLayout="horizontal"
position={"top"}
pagination={{
onChange: async (page, pageSize) => {
store.transaction.pageSize = pageSize;
store.transaction.page = page - 1;
modalLoader.setLoading(true);
await getData();
modalLoader.setLoading(false);
},
pageSize: store.transaction.pageSize,
total: store.transaction.total_data,
current: store.transaction.page + 1,
style: { marginBottom: "1rem", marginRight: "1rem" },
}}
dataSource={
store.transaction.dataDetailHistoryTransactionDetailUser
}
style={{ padding: 0 }}
renderItem={(item) => {
return (
<div>
<List.Item
key={item.id}
style={{
backgroundColor: "#ffffff",
paddingTop: 0,
paddingBottom: 0,
display: "flex",
flexDirection: "row",
alignItems: "center",
justifyContent: "center",
}}
>
<List.Item.Meta
className={["cariparkir-container"].join(" ")}
title={item.name}
description={
<div style={{}}>
<p>
<small>Pembeli : {item.buyer}</small>{" "}
<br />
<small>Price : {item.price}</small> <br />
<small>
Tujuan : {item.transaction_destination}
</small>{" "}
<br />
<small>
Kode Transaksi : {item.transaction_code}
</small>{" "}
<br />
<small>
Status :{" "}
{
<Tag
color={
item.status === 1
? "success"
: item.status === 0
? "warning"
: "processing"
}
>
{item.status === 1
? "Success"
: item.status === 0
? "Pending"
: "Failed"}
</Tag>
}
</small>{" "}
<br />
<small>
No.Seri : {item.seri_number}
</small>{" "}
<br />
<small>
IDTrx Mitra :{" "}
{item.partner_transaction_code}
</small>{" "}
<br />
<small>
Transaction Date :{" "}
{format(
parseISO(item.created_at),
"dd-MM-yyyy hh:mm:ss"
)}
</small>{" "}
<br />
</p>
</div>
}
/>
</List.Item>
<Divider plain style={{ margin: 0 }} />
</div>
);
}}
/>
</div>
)}
</TabPane>
</Tabs>
</Col>
</Row>
<div />
</Card>
<MembershipModal
visible={visibleModal}
confirmLoading={confirmLoading}
initialData={initialData}
onCreate={async (data) => {
onSubmit(data);
}}
onCancel={() => {
setInitialData({});
setVisibleModal(false);
}}
/>
<Modal
visible={store.transaction.visibleModalFilterTransaction}
title={"Filter"}
footer={footerLayoutFilter}
onCancel={async () => {
//form.resetFields();
store.transaction.filterStart = null;
store.transaction.filterEnd = null;
store.transaction.visibleModalFilterTransaction = false;
await store.transaction.getDetailHistoryTransaction();
}}
>
<Row>
<Col span={24}>
<Form layout="vertical" name="filter" form={form}>
<Form.Item
name="start_date"
label="Dari"
rules={[{ required: true, message: "Please input Date!" }]}
>
<DatePicker style={{ width: "100%" }} />
</Form.Item>
<Form.Item
name="end_date"
label="Sampai"
rules={[{ required: true, message: "Please input Date!" }]}
>
<DatePicker style={{ width: "100%" }} />
</Form.Item>
</Form>
</Col>
</Row>
</Modal>
</div>
);
});

View File

@ -0,0 +1,74 @@
import React from "react";
import {
Modal,
Select,
Row,
Title,
Col,
Option,
} from "antd";
import { useStore } from "../../utils/useStore";
import { observer } from "mobx-react-lite";
export const FilterUser = observer(() => {
const store = useStore();
return (
<Modal
visible={store.membership.visibleModalFilterMembership}
title={"Filter"}
//footer={footerLayoutFilter}
>
<Row>
<Col span={24}>
<Title level={5} type={"secondary"} strong>
Filter Supplier
</Title>
<Select
mode={"multiple"}
placeholder="Choose Supplier"
style={{ marginBottom: "20px", width: "100%" }}
>
{store.supplier.data.map((item) => (
<Option value={item.id} key={item.id}>
{item.name}
</Option>
))}
</Select>
</Col>
<Col span={24}>
<Title level={5} type={"secondary"} strong>
Filter Categories
</Title>
<Select
mode={"multiple"}
placeholder="Choose Category"
style={{ marginBottom: "20px", width: "100%" }}
value={store.product.filterCategory || []}
>
{store.category.data.map((item) => (
<Option value={item.id} key={item.id}>
{item.name}
</Option>
))}
</Select>
</Col>
<Col span={24}>
<Title level={5} type={"secondary"} strong>
Filter Sub-Categories
</Title>
<Select
mode={"multiple"}
placeholder="Choose Sub-Category"
style={{ marginBottom: "20px", width: "100%" }}
>
{store.product.dataSubCategories.map((item) => (
<Option value={item.id} key={item.id}>
{item.name}
</Option>
))}
</Select>
</Col>
</Row>
</Modal>
);
});

View File

@ -0,0 +1,472 @@
import React, { useContext, useEffect, useState } from "react";
import {
Button,
Card,
Col,
Divider,
Image,
Input,
List,
message,
Modal,
Row,
Space,
Table,
Tag,
Select,
Typography,
DatePicker,
Form,
Divinder,
} from "antd";
import { useStore } from "../../utils/useStore";
import { observer } from "mobx-react-lite";
import {
CheckCircleOutlined,
CheckOutlined,
CloseOutlined,
FilterOutlined,
StopOutlined,
} from "@ant-design/icons";
import { BreadcumbComponent } from "../../component/BreadcumbComponent";
import { LINKS } from "../../routes/app";
import { ModalLoaderContext } from "../../utils/modal";
import { appConfig } from "../../config/app";
import { capitalize } from "lodash";
import { PAYBACK_STATUS } from "../../constants/payback";
import moment from "moment";
import { useHistory } from "react-router-dom";
export const Konfirmasi = observer(() => {
const history = useHistory();
const { Option } = Select;
const { Title } = Typography;
const [form] = Form.useForm();
const store = useStore();
const modalLoader = useContext(ModalLoaderContext);
const [filterMembership, setFilterMembership] = useState([]);
const [filterSubCategories, setFilterSubCategories] = useState([]);
const [visibleModalToko, setVisibleModalToko] = useState(false);
const [VisibleModalIdentitas, setVisibleModalIdentitas] = useState(false);
const [toko, setToko] = useState({});
const [identitas, setIdentitas] = useState({});
useEffect(() => {
const init = async () => {
try {
modalLoader.setLoading(true);
await store.approval.getData();
modalLoader.setLoading(false);
} catch (e) {
modalLoader.setLoading(false);
if (e.response?.body?.message) {
message.error(e.response.body.message);
return;
}
message.error(e.message);
}
};
init();
}, []);
const columns = [
{
title: "Name",
dataIndex: "username",
key: "username",
},
{
title: "Foto Identitas",
dataIndex: ["user_detail", "image_identity"],
key: "user_detail.image_identity",
render: (text, record) =>
record.user_detail?.image_identity ? (
<Image
src={`${appConfig.apiUrl}/config/image/${text}`}
style={{ width: "5vw" }}
alt={record.image_identity}
/>
) : (
<Image
src="https://st4.depositphotos.com/14953852/24787/v/600/depositphotos_247872612-stock-illustration-no-image-available-icon-vector.jpg"
style={{ width: "5vw" }}
/>
),
},
{
title: "Foto Toko",
render: (text, record) =>
record.roles?.name !== "Sales" &&
record.user_detail?.image_store !== "[]" &&
record.user_detail?.image_store !== '""' ? (
<Button
onClick={async () => {
setToko(record);
setVisibleModalToko(true);
}}
>
Lihat Foto
</Button>
) : (
""
),
},
{
title: "Action",
dataIndex: "amount",
key: "action",
width: "10%",
render: (text, record) =>
record.is_active === false ? (
<Space size="small">
<Button
onClick={async () => {
Modal.confirm({
title: `Are you sure Accept this submission?`,
icon: <CheckOutlined />,
okText: "Accept",
cancelText: "Cancel",
okType: "primary",
onOk() {
handleApprove(record.id);
console.log(record.id);
},
onCancel() {
console.log("Cancel");
},
});
}}
icon={<CheckCircleOutlined />}
style={{
backgroundColor: "#1bb91d",
color: "#fff",
borderColor: "#1bb91d",
}}
>
Accept
</Button>
<Button
onClick={async () => {
Modal.confirm({
title: `Are you sure Reject this submission?`,
icon: <StopOutlined />,
okText: "Reject",
cancelText: "Cancel",
okType: "primary",
onOk() {
handleReject(record.id);
},
onCancel() {
console.log("Cancel");
},
});
}}
icon={<CloseOutlined />}
style={{
backgroundColor: "#ff1c1c",
color: "#fff",
borderColor: "#ff1c1c",
}}
>
Reject
</Button>
</Space>
) : (
<Tag
color={record.is_active === false ? "red" : "cyan"}
style={{ color: "#4F566B" }}
>
{record.is_active === false ? "Tidak Aktif" : "Aktif"}
</Tag>
),
},
];
const routeData = [
{
route: LINKS.HOME,
name: "Beranda",
},
{
route: LINKS.KONFIRMASI,
name: <span style={{ fontWeight: "bold" }}>Konfirmasi Retail</span>,
},
];
const dataRoute = [
{
route: LINKS.PAYBACK,
name: "Konfirmasi Retail",
},
];
const handleApprove = async (id) => {
modalLoader.setLoading(true);
try {
const response = await store.approval.approveUser(id);
console.log(response);
response.body.statusCode !== 201 && response.body.statusCode !== 200
? message.error(response?.body?.message || `Failed Approve`)
: message.success(response?.body?.message || `Success Approve`);
} catch (e) {
console.error(e, "apa errornya");
message.error(e.response?.body?.message || "Fail Approve");
}
modalLoader.setLoading(false);
};
const handleReject = async (id) => {
modalLoader.setLoading(true);
try {
const response = await store.approval.rejectUser(id);
console.log(response);
response.body.statusCode !== 201 && response.body.statusCode !== 200
? message.error(response?.body?.message || `Failed Reject`)
: message.success(response?.body?.message || `Success Reject`);
} catch (e) {
console.error(e, "apa errornya");
message.error(e.response?.body?.message || "Fail Approve");
}
modalLoader.setLoading(false);
};
//if (store.approval.user_detail.image_store === []) delete columns[2];
return (
<div className={["ppob-container"].join(" ")}>
<BreadcumbComponent
data={
store.authentication.userData.role === "Admin" ? routeData : dataRoute
}
/>
<Card>
<div style={{ marginTop: 30 }}>
{store.ui.mediaQuery.isDesktop && (
<Table
key="1"
hasEmpty
columns={columns}
dataSource={store.approval.data}
bordered
pagination={{
pageSize: store.approval.pageSize,
total: store.approval.total_data,
current: store.approval.pageSize + 1,
showSizeChanger: true,
simple: false,
}}
onChange={async (page) => {
let pageNumber = page.current;
store.approval.pageSize = page.pageSize;
store.approval.page = pageNumber - 1;
modalLoader.setLoading(true);
await store.approval.getData();
modalLoader.setLoading(false);
}}
/>
)}
{store.ui.mediaQuery.isMobile && (
<List
itemLayout="horizontal"
position={"top"}
pagination={{
onChange: async (page) => {
store.approval.pageSize = page.pageSize;
store.approval.page = page.current - 1;
modalLoader.setLoading(true);
await store.approval.getData();
modalLoader.setLoading(false);
},
pageSize: store.approval.pageSize,
total: store.approval.total_data,
current: store.approval.page + 1,
style: { marginBottom: "1rem", marginRight: "1rem" },
}}
dataSource={store.approval.data}
style={{ padding: 0 }}
renderItem={(item) => {
return (
<div>
<List.Item
key={item.id}
style={{
backgroundColor: "#ffffff",
paddingTop: 0,
paddingBottom: 0,
display: "flex",
flexDirection: "row",
alignItems: "center",
justifyContent: "center",
}}
>
<List.Item.Meta
className={[""].join(" ")}
description={
<div style={{}}>
<p>
<b>Username: {item.username}</b>
<br />
{item.is_active === false ? (
<Space size="small">
<Button
onClick={async () => {
Modal.confirm({
title: `Are you sure Accept this submission?`,
icon: <CheckOutlined />,
okText: "Accept",
cancelText: "Cancel",
okType: "primary",
onOk() {
handleApprove(item.id);
},
onCancel() {
console.log("Cancel");
},
});
}}
icon={<CheckCircleOutlined />}
style={{
backgroundColor: "#1bb91d",
color: "#fff",
borderColor: "#1bb91d",
}}
>
Accept
</Button>
<Button
onClick={async () => {
Modal.confirm({
title: `Are you sure Reject this submission?`,
icon: <StopOutlined />,
okText: "Reject",
cancelText: "Cancel",
okType: "primary",
onOk() {
handleReject(item.id);
},
onCancel() {
console.log("Cancel");
},
});
}}
icon={<CloseOutlined />}
style={{
backgroundColor: "#ff1c1c",
color: "#fff",
borderColor: "#ff1c1c",
}}
>
Reject
</Button>
</Space>
) : (
<Tag
color={
item.is_active === false ? "red" : "cyan"
}
style={{ color: "#4F566B" }}
>
{item.is_active === false
? "Tidak Aktif"
: "Aktif"}
</Tag>
)}
<Button
style={
item.is_active === true
? {
marginTop: 5,
}
: { marginLeft: 10, marginTop: 5 }
}
onClick={async () => {
setIdentitas(item);
setVisibleModalIdentitas(true);
}}
>
Foto Identitas
</Button>
{item.user_detail?.image_store !== '""' ? (
<Button
style={
item.is_active === true
? {
marginLeft: 10,
}
: { marginTop: 10 }
}
onClick={async () => {
setToko(item);
setVisibleModalToko(true);
}}
>
Foto Toko
</Button>
) : (
""
)}
</p>
</div>
}
/>
<div style={{ marginRight: 16 }}></div>
</List.Item>
<Divider plain style={{ margin: 0 }} />
</div>
);
}}
/>
)}
</div>
</Card>
<Modal
visible={
visibleModalToko === true ? visibleModalToko : VisibleModalIdentitas
}
okText={"Confirm"}
onCancel={() => {
form.resetFields();
setVisibleModalToko(false);
setVisibleModalIdentitas(false);
}}
width={1000}
footer={false}
>
{visibleModalToko === true ? (
<Row>
{JSON.parse(toko.user_detail.image_store).map((gmbr, idx) => (
<Card
style={
store.ui.mediaQuery.isDesktop
? {
width: "20vw",
borderColor: "salmon",
marginLeft: "10px",
}
: {
width: "75vw",
borderColor: "salmon",
marginBottom: "10px",
}
}
>
<Image
src={`${appConfig.apiUrl}/config/image/${gmbr}`}
alt={idx}
preview={false}
/>
</Card>
))}
</Row>
) : (
<Image
src={`${appConfig.apiUrl}/config/image/${identitas.user_detail?.image_identity}`}
alt="No image"
preview={false}
/>
)}
</Modal>
</div>
);
});

View File

@ -1,58 +1,199 @@
import React, {useEffect, useState} from "react"; import React, { useContext, useEffect, useState } from "react";
import {Button, Card, Col, Divider, Input, List, message, Modal, Row, Space, Table, Tag,} from "antd"; import {
import {useStore} from "../../utils/useStore"; Button,
import {observer} from "mobx-react-lite"; Card,
import {ExclamationCircleOutlined, FilterOutlined, PlusSquareOutlined,} from "@ant-design/icons"; Col,
import {MembershipModal} from "./MembershipModal"; Divider,
import {BreadcumbComponent} from "../../component/BreadcumbComponent"; Form,
InputNumber,
const {Search} = Input; List,
message,
Modal,
Row,
Space,
Table,
Tag,
Select,
Option,
Typography,
} from "antd";
import { useStore } from "../../utils/useStore";
import { observer } from "mobx-react-lite";
import {
DownloadOutlined,
PlusSquareOutlined,
FilterOutlined,
} from "@ant-design/icons";
import { MembershipModal } from "./MembershipModal";
import { BreadcumbComponent } from "../../component/BreadcumbComponent";
import { LINKS } from "../../routes/app";
import { useHistory } from "react-router-dom";
import { ModalLoaderContext } from "../../utils/modal";
export const Membership = observer(() => { export const Membership = observer(() => {
const history = useHistory();
const { Option } = Select;
const { Title } = Typography;
const [form] = Form.useForm();
const store = useStore(); const store = useStore();
const [visibleModal, setVisibleModal] = useState(false); const [visibleModal, setVisibleModal] = useState(false);
const [isVisibleTopUpModal, setIsVisibleTopUpModal] = useState(false);
const [destination, setDestination] = useState(null);
const [initialData, setInitialData] = useState({}); const [initialData, setInitialData] = useState({});
const [confirmLoading, setConfirmLoading] = useState(false); const [confirmLoading, setConfirmLoading] = useState(false);
const [isLoading, setIsLoading] = useState(false); const modalLoader = useContext(ModalLoaderContext);
const [filterMembership, setFilterMembership] = useState([]);
const [filterPartner, setFilterPartner] = useState([]);
useEffect(() => { useEffect(() => {
const init = async () => { const init = async () => {
try { try {
setIsLoading(true); modalLoader.setLoading(true);
const isAdmin = store.authentication.userData.role === "Admin";
await getData();
await store.membership.getData(); await store.membership.getData();
await store.role.getData(); await store.membership.getDataBySuperior();
setIsLoading(false); await store.partner.getData();
await store.role.getData(isAdmin);
modalLoader.setLoading(false);
} catch (e) { } catch (e) {
setIsLoading(false); console.error(e);
modalLoader.setLoading(false);
if (e.response?.body?.message) {
message.error(e.response.body.message);
return;
}
message.error(e.message);
} }
}; };
init(); init();
}, []); }, []);
const getData = async () => {
store.authentication.userData.role === "Admin"
? await store.membership.getData()
: await store.membership.getDataBySuperior();
};
const handleRemoveFilter = async () => {
store.membership.filterMembership = null;
store.membership.filterPartner = null;
setFilterMembership([]);
setFilterPartner([]);
store.membership.visibleModalFilterMembership = false;
await store.membership.getData();
};
const handleCancelFilter = async () => {
store.membership.filterMembership = null;
store.membership.filterPartner = null;
store.membership.visibleModalFilterMembership = false;
await store.membership.getData();
};
const handleSubmitFilter = async () => {
store.membership.filterMembership = filterMembership;
store.membership.filterPartner = filterPartner;
modalLoader.setLoading(true);
await store.membership.getData();
modalLoader.setLoading(false);
setFilterMembership([]);
setFilterPartner([]);
store.membership.visibleModalFilterMembership = false;
};
const footerLayoutFilter = [
<Button
key={"remove"}
onClick={handleRemoveFilter}
style={{
backgroundColor: "#e74e5e",
color: "#fff",
}}
>
Remove Filter
</Button>,
<Button key={"cancel"} onClick={handleCancelFilter}>
Cancel
</Button>,
<Button
key={"submit"}
onClick={handleSubmitFilter}
style={{
backgroundColor: "#4e79e7",
color: "#fff",
}}
>
Apply
</Button>,
];
const handleCancelTransaction = () => {
setIsVisibleTopUpModal(false);
setDestination(null);
};
const handleSubmitTransaction = async (data) => {
modalLoader.setLoading(true);
try {
data.destination = destination;
if (data.amount) {
data = {
...data,
amount: Number(data.amount),
};
}
let response = null;
(await store.authentication.userData.role) === "Admin"
? (response = await store.transaction.distributeAdmin(data))
: (response = await store.transaction.distribute(data));
response?.body?.statusCode === 201
? message.success("Sukses Top Up")
: message.error("Saldo Tidak Mencukupi");
modalLoader.setLoading(false);
await getData();
} catch (e) {
console.log(e, "apa errornya");
modalLoader.setLoading(false);
message.error("Gagal Top Up");
}
setConfirmLoading(false);
setIsVisibleTopUpModal(false);
form.resetFields();
setDestination(null);
};
const columns = [ const columns = [
{ {
title: "Name", title: "Name",
dataIndex: "username", dataIndex: "name",
key: "username", key: "name",
render: (text, record) => record?.name ?? record?.username,
}, },
{ {
title: "Username", title: "Username",
dataIndex: "username", dataIndex: "username",
key: "username", key: "username",
render: (text, record) => record?.name ?? record?.username,
}, },
{ {
title: "Status", title: "Role",
dataIndex: "status", dataIndex: ["roles", "name"],
key: "status", key: "role",
render: (text, record) => ( },
<Tag {
color={record?.isActive === true ? "processing" : "#E3E8EE"} title: "Saldo",
style={{ color: "#4F566B" }} dataIndex: ["coa", "amount"],
> key: ["coa", "amount"],
{record?.isActive === true ? " ACTIVE" : "INACTIVE"} width: "20%",
</Tag> render: (text) =>
), new Intl.NumberFormat("id-ID", {
style: "currency",
currency: "IDR",
}).format(text),
}, },
{ {
title: "Action", title: "Action",
@ -61,21 +202,23 @@ export const Membership = observer(() => {
<Space size="middle"> <Space size="middle">
<Button <Button
onClick={() => { onClick={() => {
setVisibleModal(true); setDestination(record?.id);
setInitialData({ console.log(record?.id);
...record, setIsVisibleTopUpModal(true);
roleId: record.roles.id,
});
}} }}
> >
Edit <DownloadOutlined /> Top Up Saldo
</Button> </Button>
<Button <Button
onClick={async () => { onClick={async () => {
handleDelete(record.id); await store.transaction.getDataHistoryTopUp(record.id);
await store.transaction.getDetailHistoryTransaction(record.id);
await store.authentication.getProfit(record.id);
history.push(LINKS.USER_DETAIL.replace(":id", record.id));
console.log(record.id);
}} }}
> >
Delete Detail
</Button> </Button>
</Space> </Space>
), ),
@ -84,87 +227,130 @@ export const Membership = observer(() => {
const routeData = [ const routeData = [
{ {
route: "/app/home", route: LINKS.HOME,
name: "Home", name: "Beranda",
}, },
{ {
route: "/app/membership", route: LINKS.MEMBERSHIP,
name: <span style={{ fontWeight: "bold" }}>Membership</span>, name: <span style={{ fontWeight: "bold" }}>Keanggotaan</span>,
}, },
]; ];
const onSubmit = async (data) => { const dataRoute = [
{
route: LINKS.MEMBERSHIP,
name: "Keanggotaan",
},
];
const onSubmit = async (data, image, imageStore) => {
data.superior = true;
console.log(imageStore, "Apa imageStore");
if (!imageStore) {
imageStore = [];
}
if (initialData.id) {
data.image_identity = image;
data.image_store = imageStore;
}
if (initialData.id) { if (initialData.id) {
setInitialData({});
setConfirmLoading(true); setConfirmLoading(true);
modalLoader.setLoading(true);
try { try {
await store.membership.update(initialData.id, data); console.log(data, "edit data");
message.success("Success Update Data Member"); const request = {
await store.membership.getData(); ...data,
image_identity: image,
image_store: imageStore,
};
await store.membership.update(initialData.id, request);
console.log(data, "edit data");
message.success(
initialData.isChangePassword
? "Success Change Member Password"
: "Success Update Data Member"
);
await getData();
modalLoader.setLoading(false);
} catch (e) { } catch (e) {
message.error("Failed Update Data Member"); modalLoader.setLoading(true);
message.error(
initialData.isChangePassword
? "Failed Update Member Password"
: "Failed Update Data Member"
);
} }
setConfirmLoading(false); setConfirmLoading(false);
setVisibleModal(false); setVisibleModal(false);
} else { } else {
setInitialData({});
setConfirmLoading(true); setConfirmLoading(true);
modalLoader.setLoading(true);
try { try {
await store.membership.create(data); console.log(data, "data member");
message.success("Success Add New Member"); const request = {
await store.membership.getData(); ...data,
image_identity: image,
image_store: JSON.stringify(imageStore),
};
const response = await store.membership.create(request);
response?.body?.statusCode === 201 || response?.body?.statusCode === 200
? message.success(
response?.body?.message || "Berhasil Tambah Member Baru"
)
: message.error(response?.body?.error || "Gagal");
await getData();
} catch (e) { } catch (e) {
console.log(e, "apa errornya"); console.log(e, "apa errornya");
message.error("Failed Add Member"); message.error(e.response?.body?.message || "Gagal Tambah Member Baru");
} }
modalLoader.setLoading(false);
setConfirmLoading(false); setConfirmLoading(false);
setVisibleModal(false); setVisibleModal(false);
setInitialData({});
form.resetFields();
} }
}; };
const handleDelete = (record) => {
Modal.confirm({
title: "Are you sure reject this record?",
icon: <ExclamationCircleOutlined />,
okText: "Yes",
okType: "primary",
cancelText: "Cancel",
onOk() {
try {
//TODO: minta apinya ke ka ilham ya, jangan di uncomment kalo pake api reconcile, nanti beneran ke apus datanya
// await store.membership.delete(record.id)
message.success("Success Delete Data");
} catch (e) {
message.error("Failed Delete Data");
}
},
onCancel() {
console.log("Cancel");
},
});
};
return ( return (
<div className={["ppob-container"].join(" ")}> <div className={["ppob-container"].join(" ")}>
<BreadcumbComponent data={routeData} /> <BreadcumbComponent
data={
store.authentication.userData.role === "Admin" ? routeData : dataRoute
}
/>
<Card> <Card>
<div> <div>
<Row style={{ marginBottom: 20 }}> <Row style={{ marginBottom: 20 }}>
<Col span={12}> <Col span={12}>
<Button> {store.authentication.userData.role === "Admin" && (
<FilterOutlined/> <Button
Filter onClick={() => {
</Button> store.membership.visibleModalFilterMembership = true;
}}
>
<FilterOutlined />
Filter
</Button>
)}
</Col> </Col>
<Col span={12} style={{textAlign: "right"}}> <Col span={12} style={{ textAlign: "right" }}>
<Search {/* <Search
placeholder="input search text" placeholder="input search text"
style={{width: 200, marginRight: 10}} style={{
/> width: store.ui.mediaQuery.isMobile ? 160 : 200,
<Button onClick={() => { marginRight: store.ui.mediaQuery.isMobile ? 0 : 10,
setInitialData({}); marginBottom: store.ui.mediaQuery.isMobile ? 10 : 0,
setVisibleModal(true); }}
}}> /> */}
<PlusSquareOutlined/> New <Button
onClick={() => {
setInitialData({});
setVisibleModal(true);
}}
>
<PlusSquareOutlined /> New
</Button> </Button>
</Col> </Col>
</Row> </Row>
@ -172,23 +358,32 @@ export const Membership = observer(() => {
<Table <Table
key="1" key="1"
hasEmpty hasEmpty
size={"small"}
columns={columns} columns={columns}
dataSource={store.membership.data} style={{ cursor: "pointer" }}
bordered dataSource={
// pagination={{ store.authentication.userData.role === "Admin"
// total: store.membership.total_data, ? store.membership.dataMember
// current: store.membership.page, : store.membership.data
// pageSize: store.membership.pageSize, }
// simple: true pagination={{
// }} pageSize: store.membership.pageSize,
// onChange={(page) => { total:
// store.membership.pageSize = page.pageSize; store.authentication.userData.role === "Admin"
// store.membership.page = page.current; ? store.membership.dataTotal
// store.membership.getData(); : store.membership.total_data,
// }} current: store.membership.page + 1,
// current={store.membership.page} showSizeChanger: true,
// loading={store.membership.pageSize} simple: false,
}}
onChange={async (page) => {
let pageNumber = page.current;
store.membership.pageSize = page.pageSize;
store.membership.page = pageNumber - 1;
modalLoader.setLoading(true);
await store.membership.getData();
await getData();
modalLoader.setLoading(false);
}}
/> />
)} )}
@ -197,17 +392,26 @@ export const Membership = observer(() => {
itemLayout="horizontal" itemLayout="horizontal"
position={"top"} position={"top"}
pagination={{ pagination={{
onChange: (page) => { onChange: async (page, pageSize) => {
store.membership.pageSize = page.pageSize; store.membership.pageSize = pageSize;
store.membership.page = page.current; store.membership.page = page - 1;
store.membership.getData(); modalLoader.setLoading(true);
await getData();
modalLoader.setLoading(false);
}, },
pageSize: store.membership.pageSize, pageSize: store.membership.pageSize,
total: store.membership.total_data, total:
current: store.membership.page, store.authentication.userData.role === "Admin"
? store.membership.dataTotal
: store.membership.total_data,
current: store.membership.page + 1,
style: { marginBottom: "1rem", marginRight: "1rem" }, style: { marginBottom: "1rem", marginRight: "1rem" },
}} }}
dataSource={store.membership.data} dataSource={
store.authentication.userData.role === "Admin"
? store.membership.dataMember
: store.membership.data
}
style={{ padding: 0 }} style={{ padding: 0 }}
renderItem={(item) => { renderItem={(item) => {
return ( return (
@ -225,12 +429,37 @@ export const Membership = observer(() => {
}} }}
> >
<List.Item.Meta <List.Item.Meta
className={["cariparkir-container"].join(" ")} className={[""].join(" ")}
title={item.username} title={item.user_detail?.name}
description={ description={
<div style={{}}> <div style={{}}>
<p> <p>
<small>Username : {item.username}</small> <br /> <small>Username : {item.username}</small> <br />
<small>Role : {item.roles?.name}</small> <br />
<small>Saldo : {item.coa?.amount}</small> <br />
<Button
style={{ marginRight: 10, marginTop: 7 }}
onClick={() => {
setDestination(item?.id);
console.log(item?.id);
setIsVisibleTopUpModal(true);
}}
>
<DownloadOutlined /> Top Up Saldo
</Button>
<Button
onClick={async () => {
await store.transaction.getDataHistoryTopUp(
item.id
);
history.push(
LINKS.USER_DETAIL.replace(":id", item.id)
);
console.log(item.id);
}}
>
Detail
</Button>
</p> </p>
</div> </div>
} }
@ -242,7 +471,16 @@ export const Membership = observer(() => {
margin: 0, margin: 0,
}} }}
> >
{item.username} {/* <Button
type={
item?.isActive === true ? "danger" : "primary"
}
onClick={() =>
changeStatus(item?.id, item?.isActive)
}
>
{item?.isActive === true ? "Inactive" : "Active"}
</Button> */}
</p> </p>
</div> </div>
</List.Item> </List.Item>
@ -254,18 +492,110 @@ export const Membership = observer(() => {
)} )}
</div> </div>
</Card> </Card>
<Modal
visible={isVisibleTopUpModal}
title="Top Up Saldo"
okText="Top Up"
cancelText="Cancel"
onCancel={() => {
form.resetFields();
handleCancelTransaction();
}}
onOk={() => {
form
.validateFields()
.then((values) => {
console.log(values, "isi form");
handleSubmitTransaction(values);
form.resetFields();
})
.catch((info) => {
console.error("Validate Failed:", info);
});
}}
>
<Form form={form} layout="vertical">
<Form.Item
name="amount"
label="Amount"
rules={[{ required: true, message: "Please input amount!" }]}
>
<InputNumber
style={{ width: "100%" }}
formatter={(value) =>
`Rp. ${value}`.replace(/\B(?=(\d{3})+(?!\d))/g, ",")
}
parser={(value) => value.replace(/\Rp.\s?|(,*)/g, "")}
/>
</Form.Item>
</Form>
</Modal>
<MembershipModal <MembershipModal
visible={visibleModal} visible={visibleModal}
confirmLoading={confirmLoading} confirmLoading={confirmLoading}
initialData={initialData} initialData={initialData}
onCreate={async (data) => { onCreate={async (data, image, imageStore) => {
onSubmit(data); onSubmit(data, image, imageStore);
}} }}
onCancel={() => { onCancel={async () => {
setInitialData({}); setInitialData({});
setVisibleModal(false); setVisibleModal(false);
await store.membership.getData();
}} }}
/> />
<Modal
visible={store.membership.visibleModalFilterMembership}
title={"Filter"}
footer={footerLayoutFilter}
onCancel={async () => {
// setFilterMembership([]);
// setFilterPartner([]);
store.membership.filterMembership = null;
store.membership.filterPartner = null;
store.membership.visibleModalFilterMembership = false;
await store.membership.getData();
}}
>
<Row>
<Col span={24}>
<Title level={5} type={"secondary"} strong>
Atasan/Superior
</Title>
<Select
mode={"multiple"}
placeholder="Choose Superior"
onChange={(val) => {
setFilterMembership(val);
}}
style={{ marginBottom: "20px", width: "100%" }}
value={filterMembership}
>
{store.membership.data.map((item) => (
<Option value={item.id} key={item.id}>
{item.name}
</Option>
))}
</Select>
</Col>
<Col span={24}>
<Title level={5} type={"secondary"} strong>
Type
</Title>
<Select
mode={"multiple"}
placeholder="Choose Type"
onChange={(val) => {
setFilterPartner(val);
}}
style={{ marginBottom: "20px", width: "100%" }}
value={filterPartner}
>
<Option value="partner">Partner</Option>
<Option value="b2c">B2C</Option>
</Select>
</Col>
</Row>
</Modal>
</div> </div>
); );
}); });

View File

@ -1,96 +1,615 @@
import React from 'react'; import React, { useState, useEffect } from "react";
import {Form, Input, Modal, Select,} from 'antd'; import {
import {capitalize} from "lodash"; Form,
import {useStore} from "../../utils/useStore"; Input,
Modal,
Select,
InputNumber,
Row,
Col,
Typography,
Upload,
message,
} from "antd";
import { useStore } from "../../utils/useStore";
import { appConfig } from "../../config/app";
import { LoadingOutlined, PlusOutlined } from "@ant-design/icons";
export const MembershipModal = ({ export const MembershipModal = ({
visible, visible,
onCreate, onCreate,
onCancel, onCancel,
initialData, initialData,
}) => { }) => {
const [form] = Form.useForm(); const [form] = Form.useForm();
const {Option} = Select; const { Option } = Select;
const dataStatus = ["true", "false"] const store = useStore();
const store = useStore(); const [value, setValue] = useState();
const [image, setImage] = useState("");
const [imageStore, setImageStore] = useState("");
const [fileList, setFileList] = useState([]);
const [fileStore, setFileStore] = useState([]);
const [previewImage, setPreviewImage] = useState("");
const [previewImageStore, setPreviewImageStore] = useState("");
const [responseFilename, setResponseFilename] = useState("");
const [responseFilenameStore, setResponseFilenameStore] = useState("");
const [loading, setLoading] = useState(false);
const [loadingStore, setLoadingStore] = useState(false);
const [previewVisible, setPreviewVisible] = useState(false);
const [previewImage1, setPreviewImage1] = useState("");
const [previewTitle, setPreviewTitle] = useState("");
useEffect(() => {
if (initialData.id) {
setFileList([
{
url: `${appConfig.apiUrl}/config/image/${initialData.image_identity}`,
},
]);
setFileStore([
{
url: `${appConfig.apiUrl}/config/image/${initialData.image_store}`,
},
]);
setImage(
`${appConfig.apiUrl}/config/image/${initialData.image_identity}`
);
setImageStore(
`${appConfig.apiUrl}/config/image/${initialData.image_store}`
);
}
return () => {};
}, [initialData]);
return ( const getBase64 = (file) => {
<Modal return new Promise((resolve, reject) => {
visible={visible} const reader = new FileReader();
title={initialData.id ? "Edit Member" : "Create a new Membership"} reader.readAsDataURL(file);
okText={initialData.id ? "Edit" : "Create"} reader.onload = () => resolve(reader.result);
cancelText="Cancel" reader.onerror = (error) => reject(error);
onCancel={() => { });
form.resetFields() };
onCancel()
}} const handlePreviewData = async (file) => {
onOk={() => { if (!file.url && !file.preview) {
form file.preview = await getBase64(file.originFileObj);
.validateFields() }
.then(values => { setPreviewImage1(file.url || file.preview);
onCreate(values); setPreviewVisible(true);
form.resetFields() setPreviewTitle(
}) file.name || file.url.substring(file.url.lastIndexOf("/") + 1)
.catch(info => {
console.log('Validate Failed:', info);
});
}}
>
<Form
form={form}
layout="vertical"
name="form_in_modal"
initialValues={initialData}
>
<Form.Item
name="username"
label="Username"
rules={[{required: true, message: 'Please input Username!'}]}
>
<Input/>
</Form.Item>
<Form.Item
name="password"
label="Password"
rules={[{required: true, message: 'Please input password!'}]}
>
<Input/>
</Form.Item>
<Form.Item
name="roleId"
label="Role"
rules={[{required: true, message: 'Please input role id!'}]}
>
<Select>
{store.role.data.map(item => (
<Option key={item.id} value={item.id}>{item.name}</Option>
))}
</Select>
</Form.Item>
<Form.Item
name="superior"
label="Superior"
rules={[{required: true, message: 'Please select superior status!'}]}
>
<Select
showSearch
placeholder="Select Status"
optionFilterProp="children"
filterOption={(input, option) =>
option.children.toLowerCase().indexOf(input.toLowerCase()) >= 0
}
filterSort={(optionA, optionB) =>
optionA.children.toLowerCase().localeCompare(optionB.children.toLowerCase())
}
>
{dataStatus.map(it => {
return <Option value={it}>{capitalize(it)}</Option>
})}
</Select>
</Form.Item>
</Form>
</Modal>
); );
};
const handleCancel1 = () => {
setPreviewVisible(false);
};
const beforeUpload = (file) => {
let isLt2M;
let allowedFile = ["image/jpeg", "image/png"];
let isValid = allowedFile.includes(file.type);
if (!isValid) {
message.error("You can only upload Image file!");
}
isLt2M = file.size / 1024 / 1024 < 2;
if (!isLt2M) {
message.error("File must smaller than 2MB!");
}
return isValid && isLt2M ? true : Upload.LIST_IGNORE;
};
const beforeUploadStore = (file) => {
let isLt2M;
let allowedFile = ["image/jpeg", "image/png"];
let isValid = allowedFile.includes(file.type);
if (!isValid) {
message.error("You can only upload Image file!");
}
isLt2M = file.size / 1024 / 1024 < 2;
if (!isLt2M) {
message.error("File must smaller than 2MB!");
}
return isValid && isLt2M ? true : Upload.LIST_IGNORE;
};
const uploadHandler = async (args) => {
const file = args.file;
const res = await store.payback.uploadImages(file);
console.log(res, "ini respon 1");
setImage(`${appConfig.apiUrl}/config/image/${res.body.filename}`);
console.log(initialData.image_identity);
initialData.image_identity !== ""
? file === ""
? setResponseFilename(initialData.image_identity)
: setResponseFilename(res.body.filename)
: setResponseFilename(res.body.filename);
setFileList([
{
uid: "-1",
name: res.body.filename,
status: "done",
url: `${appConfig.apiUrl}/config/image/${res.body.filename}`,
},
]);
setLoading(false);
};
const uploadHandlerStore = async (args) => {
const file = args.file;
const res = await store.payback.uploadImages(file);
console.log(res, "ini respon 2");
setImageStore(`${appConfig.apiUrl}/config/image/${res.body.filename}`);
setResponseFilenameStore([...responseFilenameStore, res.body.filename]);
setFileStore([
...fileStore,
{
uid: "-1",
name: res.body.filename,
status: "done",
url: `${appConfig.apiUrl}/config/image/${res.body.filename}`,
},
]);
setLoadingStore(false);
};
const handleChange = (info) => {
if (info.file.status === "uploading") {
setLoading(true);
} else {
setLoading(false);
}
};
const handleChangeStore = (info) => {
if (info.file.status === "uploading") {
setLoadingStore(true);
} else {
setLoadingStore(false);
}
};
const uploadButton = (
<div>
{loading ? <LoadingOutlined /> : <PlusOutlined />}
<div style={{ marginTop: 8 }}>Click to Upload</div>
</div>
);
const uploadButtonStore = (
<div>
{loadingStore ? <LoadingOutlined /> : <PlusOutlined />}
<div style={{ marginTop: 8 }}>Click to Upload</div>
</div>
);
return (
<Modal
visible={visible}
title={
initialData.isChangePassword
? "Change Member Password"
: initialData.id
? "Edit Member"
: "Create a new Membership"
}
okText={initialData.id ? "Edit" : "Create"}
cancelText="Cancel"
onCancel={() => {
form.resetFields();
onCancel();
setImage("");
setFileList([]);
setPreviewImage("");
setResponseFilename("");
setImageStore("");
setFileStore([]);
setPreviewImageStore("");
setResponseFilenameStore("");
}}
onOk={() => {
form
.validateFields()
.then((values) => {
console.log(values, "apa valuesanya");
values.image_identity = responseFilename;
values.image_store = JSON.stringify(responseFilenameStore);
onCreate(values, responseFilename, responseFilenameStore);
form.resetFields();
setFileStore([]);
setImage("");
setFileList([]);
setPreviewImage("");
setResponseFilename("");
setImageStore("");
setFileStore([]);
setPreviewImageStore("");
setResponseFilenameStore("");
})
.catch((info) => {
console.log("Validate Failed:", info);
});
}}
>
<Form
form={form}
layout="vertical"
name="form_in_modal"
initialValues={initialData}
>
{((initialData.id && !initialData.isChangePassword) ||
!initialData.id) && (
<Form.Item
name="name"
label="Name"
rules={[{ required: true, message: "Please input Name!" }]}
>
<Input />
</Form.Item>
)}
{!initialData.id && (
<Form.Item
name="username"
label="Username"
rules={[{ required: true, message: "Please input Username!" }]}
>
<Input />
</Form.Item>
)}
{((initialData.id && initialData.isChangePassword) ||
!initialData.id) && (
<Form.Item
name="password"
label="Password"
rules={[{ required: false, message: "Please input password!" }]}
>
<Input.Password />
</Form.Item>
)}
{((initialData.id && !initialData.isChangePassword) ||
!initialData.id) && (
<Form.Item
name="phone_number"
label="Phone Number"
rules={[
{
required: true,
message: "Please input Phone Number!",
},
{
pattern: /^(?:\d*)$/,
message: "Phone number should contain just number",
},
{
//pattern: /^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!@#\$%\^&\*])(?=.{8,})/,
pattern: /^[\d]{10,12}$/,
message: "Phone number should be 10 - 12 character",
},
]}
>
<Input
onChange={(value) => {
setValue(value);
}}
/>
</Form.Item>
)}
{((initialData.id && !initialData.isChangePassword) ||
!initialData.id) &&
store.authentication.userData.role === "Admin" && (
<div>
<Form.Item
name="identity_number"
label="Identity Number"
rules={[
{
required: true,
message: "Please input identity number!",
},
{
pattern: /^(?:\d*)$/,
message: "Phone number should contain just number",
},
]}
>
<Input
onChange={(value) => {
setValue(value);
}}
/>
</Form.Item>
<Form.Item
label="Upload identity image"
name="image_identity"
rules={[
{ required: true, message: "Please insert image identity" },
]}
>
<div>
<Upload
listType="picture-card"
fileList={fileList}
showUploadList={true}
onPreview={handlePreviewData}
onChange={handleChange}
beforeUpload={(file) => beforeUpload(file)}
customRequest={(args) => uploadHandler(args)}
maxCount={1}
onRemove={(file) => {
setImage("");
setLoading(false);
setFileList([]);
}}
>
{image === "" ? uploadButton : null}
</Upload>
<Modal
visible={previewVisible}
title={previewTitle}
footer={null}
onCancel={handleCancel1}
>
<img
alt="example"
style={{ width: "100%" }}
src={previewImage1}
/>
</Modal>
<h5
style={{
marginTop: 12,
color: "rgba(0, 0, 0, 0.45)",
}}
>
Max size of file 2 MB
</h5>
</div>
</Form.Item>
<Form.Item
name="roleId"
label="Role"
rules={[{ required: true, message: "Please input role id!" }]}
>
<Select>
{store.role.data.map((item) => (
<Option key={item.id} value={item.id}>
{item.name}
</Option>
))}
</Select>
</Form.Item>
</div>
)}
{((initialData.id && !initialData.isChangePassword) ||
!initialData.id) &&
store.authentication.userData.role === "Supervisor" && (
<div>
<Form.Item
name="identity_number"
label="Identity Number"
rules={[
{
required: true,
message: "Please input identity number!",
},
{
pattern: /^(?:\d*)$/,
message: "Phone number should contain just number",
},
]}
>
<Input
onChange={(value) => {
setValue(value);
}}
/>
</Form.Item>
<Form.Item
label="Upload identity image"
name="image_identity"
rules={[
{ required: true, message: "Please insert image identity" },
]}
>
<div>
<Upload
listType="picture-card"
fileList={fileList}
// onPreview={(file) => {
// setPreviewImage(file.url || file.filename);
// }}
showUploadList={true}
onPreview={handlePreviewData}
onChange={handleChange}
beforeUpload={(file) => beforeUpload(file)}
customRequest={(args) => uploadHandler(args)}
onRemove={(file) => {
setImage("");
setLoading(false);
setFileList([]);
}}
>
{image === "" ? uploadButton : null}
</Upload>
<Modal
visible={previewVisible}
title={previewTitle}
footer={null}
onCancel={handleCancel1}
>
<img
alt="example"
style={{ width: "100%" }}
src={previewImage1}
/>
</Modal>
<h5
style={{
marginTop: 12,
color: "rgba(0, 0, 0, 0.45)",
}}
>
Max size of file 2 MB
</h5>
</div>
</Form.Item>
<Form.Item
name="roleId"
label="Role"
rules={[{ required: true, message: "Please input role id!" }]}
>
<Select>
<Option
key="e4dfb6a3-2348-464a-8fb8-5cbc089d4209"
value="e4dfb6a3-2348-464a-8fb8-5cbc089d4209"
>
Sales
</Option>
</Select>
</Form.Item>
</div>
)}
{((initialData.id && !initialData.isChangePassword) ||
!initialData.id) &&
store.authentication.userData.role === "Sales" && (
<div>
<Form.Item
name="identity_number"
label="Identity Number"
rules={[
{
required: true,
message: "Please input identity number!",
},
{
pattern: /^(?:\d*)$/,
message: "Phone number should contain just number",
},
]}
>
<Input
onChange={(value) => {
setValue(value);
}}
/>
</Form.Item>
<Form.Item
label="Upload identity image"
name="image_identity"
rules={[
{ required: true, message: "Please insert image identity" },
]}
>
<div>
<Upload
listType="picture-card"
fileList={fileList}
// onPreview={(file) => {
// setPreviewImage(file.url || file.filename);
// }}
showUploadList={true}
onPreview={handlePreviewData}
onChange={handleChange}
beforeUpload={(file) => beforeUpload(file)}
customRequest={(args) => uploadHandler(args)}
onRemove={(file) => {
setImage("");
setLoading(false);
setFileList([]);
}}
>
{image === "" ? uploadButton : null}
</Upload>
<Modal
visible={previewVisible}
title={previewTitle}
footer={null}
onCancel={handleCancel1}
>
<img
alt="example"
style={{ width: "100%" }}
src={previewImage1}
/>
</Modal>
<h5
style={{
marginTop: 12,
color: "rgba(0, 0, 0, 0.45)",
}}
>
Max size of file 2 MB
</h5>
</div>
</Form.Item>
<Form.Item
label="Upload foto toko tampak samping kanan,kiri dan depan"
name="image_store"
rules={[
{ required: true, message: "Please insert image store" },
]}
>
<div>
<Upload
listType="picture-card"
fileList={fileStore}
// onPreview={(file) => {
// setPreviewImageStore(file.url || file.filename);
// }}
showUploadList={true}
onPreview={handlePreviewData}
onChange={handleChangeStore}
beforeUpload={(file) => beforeUploadStore(file)}
customRequest={(args) => uploadHandlerStore(args)}
maxCount={3}
onRemove={(file) => {
setImageStore("");
setLoadingStore(false);
setFileStore([]);
}}
>
{fileStore.length >= 3 ? null : uploadButtonStore}
</Upload>
<Modal
visible={previewVisible}
title={previewTitle}
footer={null}
onCancel={handleCancel1}
>
<img
alt="example"
style={{ width: "100%" }}
src={previewImage1}
/>
</Modal>
<h5
style={{
marginTop: 12,
color: "rgba(0, 0, 0, 0.45)",
}}
>
Max size of file 2 MB
</h5>
</div>
</Form.Item>
<Form.Item
name="roleId"
label="Role"
rules={[{ required: true, message: "Please input role id!" }]}
>
<Select>
<Option
key="e4dfb6a3-2338-464a-8fb8-5cbc089d4209"
value="e4dfb6a3-2338-464a-8fb8-5cbc089d4209"
>
Retail
</Option>
</Select>
</Form.Item>
</div>
)}
</Form>
</Modal>
);
}; };

View File

@ -0,0 +1,519 @@
import React, { useContext, useEffect, useState } from "react";
import {
Button,
Card,
Col,
Divider,
Image,
Input,
List,
message,
Modal,
Row,
Space,
Table,
Tag,
Select,
Typography,
DatePicker,
Form,
} from "antd";
import { useStore } from "../../utils/useStore";
import { observer } from "mobx-react-lite";
import {
CheckCircleOutlined,
CheckOutlined,
CloseOutlined,
FilterOutlined,
StopOutlined,
} from "@ant-design/icons";
import { BreadcumbComponent } from "../../component/BreadcumbComponent";
import { LINKS } from "../../routes/app";
import { ModalLoaderContext } from "../../utils/modal";
import { appConfig } from "../../config/app";
import { capitalize } from "lodash";
import { PAYBACK_STATUS } from "../../constants/payback";
import moment from "moment";
export const Payback = observer(() => {
const { Option } = Select;
const { Title } = Typography;
const [form] = Form.useForm();
const store = useStore();
const modalLoader = useContext(ModalLoaderContext);
const [filterMembership, setFilterMembership] = useState([]);
const [filterSubCategories, setFilterSubCategories] = useState([]);
useEffect(() => {
const init = async () => {
try {
modalLoader.setLoading(true);
await store.membership.getData();
await store.payback.getDataConfirmation();
modalLoader.setLoading(false);
} catch (e) {
modalLoader.setLoading(false);
if (e.response?.body?.message) {
message.error(e.response.body.message);
return;
}
message.error(e.message);
}
};
init();
}, []);
const columns = [
{
title: "Name",
dataIndex: "userData_name",
key: "userData_name",
},
{
title: "Picture",
dataIndex: "image_prove",
key: "image_prove",
render: (text, record) => (
<Image
src={`${appConfig.apiUrl}/config/image/${text}`}
style={{ width: "5vw" }}
alt={record.id}
/>
),
},
{
title: "Amount",
dataIndex: "amount",
key: "amount",
width: "20%",
render: (text) =>
new Intl.NumberFormat("id-ID", {
style: "currency",
currency: "IDR",
}).format(text),
},
{
title: "Action",
dataIndex: "amount",
key: "action",
width: "10%",
render: (text, record) =>
PAYBACK_STATUS[record.status] === PAYBACK_STATUS[0] ? (
<Space size="middle">
<Button
onClick={async () => {
Modal.confirm({
title: `Are you sure Accept this submission?`,
icon: <CheckOutlined />,
okText: "Accept",
cancelText: "Cancel",
okType: "primary",
onOk() {
handleAction(record.id, "accept");
},
onCancel() {
console.log("Cancel");
},
});
}}
icon={<CheckCircleOutlined />}
style={{
backgroundColor: "#1bb91d",
color: "#fff",
borderColor: "#1bb91d",
}}
>
Accept
</Button>
<Button
onClick={async () => {
Modal.confirm({
title: `Are you sure Reject this submission?`,
icon: <StopOutlined />,
okText: "Reject",
cancelText: "Cancel",
okType: "primary",
onOk() {
handleAction(record.id, "reject");
},
onCancel() {
console.log("Cancel");
},
});
}}
icon={<CloseOutlined />}
style={{
backgroundColor: "#ff1c1c",
color: "#fff",
borderColor: "#ff1c1c",
}}
>
Reject
</Button>
</Space>
) : (
<Tag
color={
PAYBACK_STATUS[record.status] === PAYBACK_STATUS[3]
? "cyan"
: "red"
}
style={{ color: "#4F566B" }}
>
{PAYBACK_STATUS[record.status]}
</Tag>
),
},
];
if (store.authentication.userData.role === "Retail") columns.pop();
const routeData = [
{
route: LINKS.HOME,
name: "Beranda",
},
{
route: LINKS.PAYBACK,
name: <span style={{ fontWeight: "bold" }}>Konfirmasi Pembayaran</span>,
},
];
const dataRoute = [
{
route: LINKS.PAYBACK,
name: "Konfirmasi Pembayaran",
},
];
const handleAction = async (id, type) => {
modalLoader.setLoading(true);
try {
const response = await store.payback.confirmPayback(id, type);
console.log(response);
response.body.statusCode !== 201 && response.body.statusCode !== 200
? message.error(
response?.body?.message || `Failed ${capitalize(type)} Payback`
)
: message.success(
response?.body?.message || `Success ${capitalize(type)} Payback`
);
} catch (e) {
console.error(e, "apa errornya");
message.error(
e.response?.body?.message || "Failed Handler Action Payback"
);
}
modalLoader.setLoading(false);
};
const handleRemoveFilter = async () => {
store.payback.filterMembership = null;
store.payback.filterStart = null;
store.payback.filterEnd = null;
setFilterMembership([]);
form.resetFields();
await store.payback.getDataConfirmation();
store.payback.visibleModalFilterPayback = false;
};
const handleCancelFilter = async () => {
//setFilterMembership([]);
//form.resetFields();
store.payback.filterMembership = null;
store.payback.filterStart = null;
store.payback.filterEnd = null;
store.payback.visibleModalFilterPayback = false;
await store.payback.getDataConfirmation();
};
const handleSubmitFilter = async () => {
const data = form.getFieldsValue();
//console.log(data);
store.payback.filterMembership = filterMembership;
store.payback.filterStart = data.start_date
? moment(data.start_date).format("YYYY-MM-DD") + " 00:00:00"
: null;
store.payback.filterEnd = data.end_date
? moment(data.end_date).format("YYYY-MM-DD") + " 23:59:59"
: null;
modalLoader.setLoading(true);
await store.payback.getDataConfirmation();
modalLoader.setLoading(false);
store.payback.visibleModalFilterPayback = false;
};
const footerLayoutFilter = [
<Button
key={"remove"}
onClick={handleRemoveFilter}
style={{
backgroundColor: "#e74e5e",
color: "#fff",
}}
>
Remove Filter
</Button>,
<Button key={"cancel"} onClick={handleCancelFilter}>
Cancel
</Button>,
<Button
key={"submit"}
onClick={handleSubmitFilter}
style={{
backgroundColor: "#4e79e7",
color: "#fff",
}}
>
Apply
</Button>,
];
return (
<div className={["ppob-container"].join(" ")}>
<BreadcumbComponent
data={
store.authentication.userData.role === "Admin" ? routeData : dataRoute
}
/>
<Card>
<div>
<Row style={{ marginBottom: 20 }}>
<Col span={12}>
<Button
onClick={() => {
store.payback.visibleModalFilterPayback = true;
}}
>
<FilterOutlined />
Filter
</Button>
</Col>
<Col span={12} style={{ textAlign: "right" }}>
{/* <Search
placeholder="input search text"
style={{
width: store.ui.mediaQuery.isMobile ? 160 : 200,
marginRight: store.ui.mediaQuery.isMobile ? 0 : 10,
marginBottom: store.ui.mediaQuery.isMobile ? 10 : 0,
}}
/> */}
</Col>
</Row>
{store.ui.mediaQuery.isDesktop && (
<Table
key="1"
hasEmpty
columns={columns}
dataSource={store.payback.dataConfirmation}
bordered
pagination={{
pageSize: store.payback.pageSizeConfirmation,
total: store.payback.totalDataConfirmation,
current: store.payback.pageSizeConfirmation + 1,
showSizeChanger: true,
simple: false,
}}
onChange={async (page) => {
let pageNumber = page.current;
store.payback.pageSizeConfirmation = page.pageSize;
store.payback.pageConfirmation = pageNumber - 1;
modalLoader.setLoading(true);
await store.payback.getDataConfirmation();
modalLoader.setLoading(false);
}}
/>
)}
{store.ui.mediaQuery.isMobile && (
<List
itemLayout="horizontal"
position={"top"}
pagination={{
onChange: async (page) => {
store.payback.pageSizeConfirmation = page.pageSize;
store.payback.pageConfirmation = page.current - 1;
modalLoader.setLoading(true);
await store.payback.getDataConfirmation();
modalLoader.setLoading(false);
},
pageSize: store.payback.pageSizeConfirmation,
total: store.payback.totalDataConfirmation,
current: store.payback.pageConfirmation + 1,
style: { marginBottom: "1rem", marginRight: "1rem" },
}}
dataSource={store.payback.dataConfirmation}
style={{ padding: 0 }}
renderItem={(item) => {
return (
<div>
<List.Item
key={item.id}
style={{
backgroundColor: "#ffffff",
paddingTop: 0,
paddingBottom: 0,
display: "flex",
flexDirection: "row",
alignItems: "center",
justifyContent: "center",
}}
>
<List.Item.Meta
className={[""].join(" ")}
title={item.userData_name}
description={
<div style={{}}>
<p>
<small>Amount: {item.amount}</small>
<br />
{PAYBACK_STATUS[item.status] ===
PAYBACK_STATUS[0] ? (
<Space size="middle">
<Button
onClick={async () => {
Modal.confirm({
title: `Are you sure Accept this submission?`,
icon: <CheckOutlined />,
okText: "Accept",
cancelText: "Cancel",
okType: "primary",
onOk() {
handleAction(item.id, "accept");
},
onCancel() {
console.log("Cancel");
},
});
}}
icon={<CheckCircleOutlined />}
style={{
backgroundColor: "#1bb91d",
color: "#fff",
borderColor: "#1bb91d",
}}
>
Accept
</Button>
<Button
onClick={async () => {
Modal.confirm({
title: `Are you sure Reject this submission?`,
icon: <StopOutlined />,
okText: "Reject",
cancelText: "Cancel",
okType: "primary",
onOk() {
handleAction(item.id, "reject");
},
onCancel() {
console.log("Cancel");
},
});
}}
icon={<CloseOutlined />}
style={{
backgroundColor: "#ff1c1c",
color: "#fff",
borderColor: "#ff1c1c",
}}
>
Reject
</Button>
</Space>
) : (
<Tag
color={
PAYBACK_STATUS[item.status] ===
PAYBACK_STATUS[3]
? "cyan"
: "red"
}
style={{ color: "#4F566B" }}
>
{PAYBACK_STATUS[item.status]}
</Tag>
)}
</p>
</div>
}
/>
<div style={{ marginRight: 16 }}>
<p
style={{
fontSize: 9,
margin: 0,
}}
>
<Image
src={`${appConfig.apiUrl}/config/image/${item.image_prove}`}
style={{ width: "10vw" }}
/>
</p>
</div>
</List.Item>
<Divider plain style={{ margin: 0 }} />
</div>
);
}}
/>
)}
</div>
</Card>
<Modal
visible={store.payback.visibleModalFilterPayback}
title={"Filter"}
footer={footerLayoutFilter}
onCancel={async () => {
//form.resetFields();
//setFilterMembership([]);
store.payback.filterMembership = null;
store.payback.filterStart = null;
store.payback.filterEnd = null;
store.payback.visibleModalFilterPayback = false;
await store.payback.getDataConfirmation();
}}
>
<Row>
<Col span={24}>
<Title level={5} type={"secondary"} strong>
Sender
</Title>
<Form layout="vertical" name="filter" form={form}>
<Select
mode={"multiple"}
placeholder="Pilih Pengirim"
onChange={(val) => {
setFilterMembership(val);
}}
style={{ marginBottom: "20px", width: "100%" }}
value={filterMembership}
>
{store.payback.dataConfirmation.map((item) => (
<Option value={item.id} key={item.id}>
{item.userData_name}
</Option>
))}
</Select>
<Form.Item
name="start_date"
label="Dari"
rules={[{ required: true, message: "Please input Date!" }]}
>
<DatePicker style={{ width: "100%" }}/>
</Form.Item>
<Form.Item
name="end_date"
label="Sampai"
rules={[{ required: true, message: "Please input Date!" }]}
>
<DatePicker style={{ width: "100%" }} />
</Form.Item>
</Form>
</Col>
</Row>
</Modal>
</div>
);
});

View File

@ -0,0 +1,383 @@
import React, { useContext, useEffect, useState } from "react";
import {
Button,
Card,
Col,
Divider,
Image,
List,
message,
DatePicker,
Row,
Table,
Tag,
Form,
Modal,
} from "antd";
import { useStore } from "../../utils/useStore";
import { observer } from "mobx-react-lite";
import { FilterOutlined, PlusSquareOutlined } from "@ant-design/icons";
import { PaybackModal } from "./PaybackModal";
import { BreadcumbComponent } from "../../component/BreadcumbComponent";
import { LINKS } from "../../routes/app";
import { ModalLoaderContext } from "../../utils/modal";
import { appConfig } from "../../config/app";
import { PAYBACK_STATUS } from "../../constants/payback";
import moment from "moment";
export const PaybackCreated = observer(() => {
const [form] = Form.useForm();
const store = useStore();
const [filterMembership, setFilterMembership] = useState([]);
const [initialData, setInitialData] = useState({});
const modalLoader = useContext(ModalLoaderContext);
useEffect(() => {
const init = async () => {
try {
modalLoader.setLoading(true);
await Promise.allSettled([
store.payback.getDataCreated(),
store.authentication.getProfile(),
]);
modalLoader.setLoading(false);
} catch (e) {
modalLoader.setLoading(false);
if (e.response?.body?.message) {
message.error(e.response.body.message);
return;
}
message.error(e.message);
}
};
init();
}, []);
const columns = [
{
title: "Picture",
dataIndex: "image_prove",
key: "image_prove",
render: (text, record) => (
<Image
src={`${appConfig.apiUrl}/config/image/${text}`}
style={{ width: "5vw" }}
alt={record.id}
/>
),
},
{
title: "Amount",
dataIndex: "amount",
key: "amount",
render: (text) =>
new Intl.NumberFormat("id-ID", {
style: "currency",
currency: "IDR",
}).format(text),
},
{
title: "Status",
dataIndex: "status",
key: "status",
width: "10%",
render: (text, record) => (
<Tag
color={
record.status === 0
? "purple"
: record.status === 1
? "blue"
: record.status === 2
? "warning"
: record.status === 3
? "success"
: "red"
}
>
{record.status === 0
? "Pending"
: record.status === 1
? "Success"
: record.status === 2
? "Failed"
: record.status === 3
? "Approved"
: "Rejected"}
</Tag>
),
},
];
const routeData = [
{
route: LINKS.HOME,
name: "Beranda",
},
{
route: LINKS.PAYBACK_CREATED,
name: <span style={{ fontWeight: "bold" }}>Buat Pembayaran</span>,
},
];
const dataRoute = [
{
route: LINKS.PAYBACK_CREATED,
name: "Buat Pembayaran",
},
];
const handleRemoveFilter = async () => {
store.payback.filterStart = null;
store.payback.filterEnd = null;
form.resetFields();
await store.payback.getDataCreated();
store.payback.visibleModalFilterCreate = false;
};
const handleCancelFilter = async () => {
store.payback.filterStart = null;
store.payback.filterEnd = null;
store.payback.visibleModalFilterCreate = false;
await store.payback.getDataCreated();
};
const handleSubmitFilter = async () => {
const data = form.getFieldsValue();
console.log(data);
store.payback.filterStart = moment(data.start_date).format(
"YYYY-MM-DD 00:00:00"
);
store.payback.filterEnd = moment(data.end_date).format(
"YYYY-MM-DD HH:mm:ss"
);
modalLoader.setLoading(true);
await store.payback.getDataCreated();
modalLoader.setLoading(false);
store.payback.visibleModalFilterCreate = false;
};
const footerLayoutFilter = [
<Button
key={"remove"}
onClick={handleRemoveFilter}
style={{
backgroundColor: "#e74e5e",
color: "#fff",
}}
>
Remove Filter
</Button>,
<Button key={"cancel"} onClick={handleCancelFilter}>
Cancel
</Button>,
<Button
key={"submit"}
onClick={handleSubmitFilter}
style={{
backgroundColor: "#4e79e7",
color: "#fff",
}}
>
Apply
</Button>,
];
return (
<div className={["ppob-container"].join(" ")}>
<BreadcumbComponent
data={
store.authentication.userData.role === "Admin" ? routeData : dataRoute
}
/>
<Card>
<div>
<Row style={{ marginBottom: 20 }}>
<Col span={12}>
<Button
onClick={() => {
store.payback.visibleModalFilterCreate = true;
}}
>
<FilterOutlined />
Filter
</Button>
</Col>
<Col span={12} style={{ textAlign: "right" }}>
{/* <Search
placeholder="input search text"
style={{
width: store.ui.mediaQuery.isMobile ? 160 : 200,
marginRight: store.ui.mediaQuery.isMobile ? 0 : 10,
marginBottom: store.ui.mediaQuery.isMobile ? 10 : 0,
}}
/> */}
{store.authentication.userData.role !== "Admin" && (
<Button
onClick={() => {
setInitialData({});
store.payback.visibleModalPayback = true;
}}
>
<PlusSquareOutlined /> New
</Button>
)}
</Col>
</Row>
{store.ui.mediaQuery.isDesktop && (
<Table
key="1"
hasEmpty
columns={columns}
dataSource={store.payback.dataCreated}
bordered
pagination={{
pageSize: store.payback.pageSizeCreated,
total: store.payback.totalDataCreated,
current: store.payback.pageCreated + 1,
showSizeChanger: true,
simple: false,
}}
onChange={async (page) => {
let pageNumber = page.current;
store.payback.pageSizeCreated = page.pageSize;
store.payback.pageCreated = pageNumber - 1;
modalLoader.setLoading(true);
await store.payback.getDataCreated();
modalLoader.setLoading(false);
}}
/>
)}
{store.ui.mediaQuery.isMobile && (
<List
itemLayout="horizontal"
position={"top"}
pagination={{
onChange: async (page, pageSize) => {
store.payback.pageSizeCreated = pageSize;
store.payback.pageCreated = page - 1;
modalLoader.setLoading(true);
await store.payback.getDataCreated();
modalLoader.setLoading(false);
},
pageSize: store.payback.pageSizeCreated,
total: store.payback.totalDataCreated,
current: store.payback.pageCreated + 1,
style: { marginBottom: "1rem", marginRight: "1rem" },
}}
dataSource={store.payback.dataCreated}
style={{ padding: 0 }}
renderItem={(item) => {
return (
<div>
<List.Item
key={item.id}
style={{
backgroundColor: "#ffffff",
paddingTop: 0,
paddingBottom: 0,
display: "flex",
flexDirection: "row",
alignItems: "center",
justifyContent: "center",
}}
>
<List.Item.Meta
className={[""].join(" ")}
title={item.name}
description={
<div style={{ marginBottom: 10 }}>
<small>Amount: {item.amount}</small>
<br />
<div style={{ marginTop: 5 }}>
<small>Status : </small>
<Tag
color={
item.status === 0
? "purple"
: item.status === 1
? "blue"
: item.status === 2
? "warning"
: item.status === 3
? "success"
: "red"
}
>
{item.status === 0
? "Pending"
: item.status === 1
? "Success"
: item.status === 2
? "Failed"
: item.status === 3
? "Approved"
: "Rejected"}
</Tag>
</div>
</div>
}
/>
<div style={{ marginRight: 16 }}>
<p
style={{
fontSize: 9,
margin: 0,
}}
>
{" "}
<Image
src={`${appConfig.apiUrl}/config/image/${item.image_prove}`}
style={{ width: "20vw" }}
preview={true}
/>
</p>
</div>
</List.Item>
<Divider plain style={{ margin: 0 }} />
</div>
);
}}
/>
)}
</div>
</Card>
<Modal
visible={store.payback.visibleModalFilterCreate}
title={"Filter"}
footer={footerLayoutFilter}
onCancel={async () => {
//form.resetFields();
store.payback.filterStart = null;
store.payback.filterEnd = null;
store.payback.visibleModalFilterCreate = false;
await store.payback.getDataCreated();
}}
>
<Row>
<Col span={24}>
<Form layout="vertical" name="filter" form={form}>
<Form.Item
name="start_date"
label="Dari"
rules={[{ required: true, message: "Please input Date!" }]}
>
<DatePicker style={{ width: "100%" }} />
</Form.Item>
<Form.Item
name="end_date"
label="Sampai"
rules={[{ required: true, message: "Please input Date!" }]}
>
<DatePicker style={{ width: "100%" }} />
</Form.Item>
</Form>
</Col>
</Row>
</Modal>
<PaybackModal initialData={initialData} />
</div>
);
});

View File

@ -0,0 +1,169 @@
import React, { useContext, useState } from "react";
import { Form, InputNumber, message, Modal, Upload } from "antd";
import { useStore } from "../../utils/useStore";
import { LoadingOutlined, PlusOutlined } from "@ant-design/icons";
import { ModalLoaderContext } from "../../utils/modal";
import { appConfig } from "../../config/app";
export const PaybackModal = ({ initialData }) => {
const [form] = Form.useForm();
const store = useStore();
const [image, setImage] = useState("");
const [fileList, setFileList] = useState([]);
const [previewImage, setPreviewImage] = useState("");
const [responseFilename, setResponseFilename] = useState("");
const [loading, setLoading] = useState(false);
const modalLoader = useContext(ModalLoaderContext);
const beforeUpload = (file) => {
let isLt2M;
let allowedFile = ["image/jpeg", "image/png"];
let isValid = allowedFile.includes(file.type);
if (!isValid) {
message.error("You can only upload Image file!");
}
isLt2M = file.size / 1024 / 1024 < 2;
if (!isLt2M) {
message.error("File must smaller than 2MB!");
}
return isValid && isLt2M ? true : Upload.LIST_IGNORE;
};
const uploadHandler = async (args) => {
const file = args.file;
const res = await store.payback.uploadImages(file);
setImage(`${appConfig.apiUrl}/config/image/${res.body.filename}`);
setResponseFilename(res.body.filename);
setFileList([
{
uid: "-1",
name: res.body.filename,
status: "done",
url: `${appConfig.apiUrl}/config/image/${res.body.filename}`,
},
]);
setLoading(false);
};
const handleChange = (info) => {
if (info.file.status === "uploading") {
setLoading(true);
} else {
setLoading(false);
}
};
const uploadButton = (
<div>
{loading ? <LoadingOutlined /> : <PlusOutlined />}
<div style={{ marginTop: 8 }}>Click to Upload</div>
</div>
);
const handleSubmit = async (data) => {
modalLoader.setLoading(true);
try {
const request = {
...data,
destination: store.authentication.profileData.superior?.id,
image_prove: responseFilename,
};
const response = await store.payback.create(request);
message.success(response?.body?.message || "Success Add Created");
} catch (e) {
if (e.response?.body?.message) {
message.error(e.response.body.message);
return;
}
message.error(e.message);
}
modalLoader.setLoading(false);
store.payback.visibleModalPayback = false;
form.resetFields();
setImage("");
setFileList([]);
setPreviewImage("");
setResponseFilename("");
};
const handleCancel = () => {
form.resetFields();
setImage("");
setFileList([]);
setPreviewImage("");
setResponseFilename("");
store.payback.visibleModalPayback = false;
};
return (
<Modal
visible={store.payback.visibleModalPayback}
title={"Create a new Created"}
okText={"Create"}
cancelText="Cancel"
onCancel={handleCancel}
onOk={() => {
form
.validateFields()
.then((values) => {
handleSubmit(values);
form.resetFields();
})
.catch((info) => {
console.log("Validate Failed:", info);
});
}}
>
<Form
form={form}
layout="vertical"
name="form_in_modal"
initialValues={initialData}
>
<Form.Item label="Upload Picture" name="image_prove">
<div>
<Upload
listType="picture-card"
fileList={fileList}
onPreview={(file) => {
setPreviewImage(file.url || file.filename);
}}
showUploadList={true}
onChange={handleChange}
beforeUpload={(file) => beforeUpload(file)}
customRequest={(args) => uploadHandler(args)}
onRemove={(file) => {
setImage("");
setLoading(false);
setFileList([]);
}}
>
{image === "" ? uploadButton : null}
</Upload>
<h5
style={{
marginTop: 12,
color: "rgba(0, 0, 0, 0.45)",
}}
>
Max size of file 2 MB
</h5>
</div>
</Form.Item>
<Form.Item
name="amount"
label="amount"
rules={[{ required: true, message: "Please input Amount!" }]}
>
<InputNumber
style={{ width: "100%" }}
formatter={(value) =>
`Rp. ${value}`.replace(/\B(?=(\d{3})+(?!\d))/g, ",")
}
parser={(value) => value.replace(/\Rp.\s?|(,*)/g, "")}
/>
</Form.Item>
</Form>
</Modal>
);
};

View File

@ -0,0 +1,64 @@
import React, {useContext, useEffect, useState} from "react";
import {Button, Card, Col, Input, Row, Tabs,message} from "antd";
import {FilterOutlined, PlusSquareOutlined} from "@ant-design/icons";
import {BreadcumbComponent} from "../../component/BreadcumbComponent";
import {useStore} from "../../utils/useStore";
import {observer} from "mobx-react-lite";
import {LINKS} from "../../routes/app";
import {CategoryComponent} from "../../component/CategoryComponent";
import {ModalLoaderContext} from "../../utils/modal";
export const Category = observer(() => {
const store = useStore();
const modalLoader = useContext(ModalLoaderContext);
useEffect(() => {
const init = async () => {
try {
modalLoader.setLoading(true);
await store.category.getData();
modalLoader.setLoading(false);
} catch (e) {
modalLoader.setLoading(false);
if (e.response?.body?.message) {
message.error(e.response.body.message);
return;
}
message.error(e.message);
}
};
init();
}, []);
const routeData = [
{
route: LINKS.HOME,
name: "Beranda",
},
{
route: LINKS.CATEGORY,
name: <span style={{ fontWeight: "bold" }}>Kategori</span>,
},
];
return (
<div className={["ppob-container"].join(" ")}>
<BreadcumbComponent data={routeData} />
<Card>
<Row style={{ marginBottom: 20 }}>
<Col span={24} style={{ textAlign: "right" }}>
<Button
onClick={() => (store.category.visibleModalCategory = true)}
>
<PlusSquareOutlined /> New
</Button>
</Col>
</Row>
<CategoryComponent />
</Card>
</div>
);
});

View File

@ -1,120 +1,324 @@
import React,{useState,useEffect} from "react"; import React, { useContext, useEffect, useState } from "react";
import {Button, Card, Col, Input, Row, Tabs,message} from "antd"; import {
import {FilterOutlined, PlusSquareOutlined,} from "@ant-design/icons"; Button,
import {BreadcumbComponent} from "../../component/BreadcumbComponent"; Card,
import {Pulsa} from "./Pulsa"; Col,
import {PulsaModal} from "./PulsaModal"; Form,
import {useStore} from "../../utils/useStore"; Input,
import {observer} from "mobx-react-lite"; message,
Modal,
Row,
Select,
Upload,
} from "antd";
import {
FilterOutlined,
PlusOutlined,
UploadOutlined,
} from "@ant-design/icons";
import { BreadcumbComponent } from "../../component/BreadcumbComponent";
import { useStore } from "../../utils/useStore";
import { observer } from "mobx-react-lite";
import { ProductComponent } from "../../component/ProductComponent";
import { LINKS } from "../../routes/app";
import { ModalLoaderContext } from "../../utils/modal";
const { Search } = Input;
const { Option } = Select;
export const Product = observer(() => {
const {TabPane} = Tabs;
const {Search} = Input;
export const Product =observer(() => {
const [visibleModal, setVisibleModal] = useState(false)
const [initialData, setInitialData] = useState({})
const [confirmLoading, setConfirmLoading] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const store = useStore(); const store = useStore();
const modalLoader = useContext(ModalLoaderContext);
const [loading, setLoading] = useState(false);
const [visibleModalUpload, setVisibleModalUpload] = useState(false);
const [excel, setExcel] = useState("");
const [fileList, setFileList] = useState([]);
const [form] = Form.useForm();
useEffect(() => {
const init = async () => {
try {
store.supplier.page = 0;
store.supplier.pageSize = 1000;
modalLoader.setLoading(true);
await Promise.allSettled([
store.supplier.getData(),
store.category.getData(),
store.product.getDataSubCategories(),
store.product.getProductPartner(),
]);
await store.product.getData();
modalLoader.setLoading(false);
} catch (e) {
modalLoader.setLoading(false);
if (e.response?.body?.message) {
message.error(e.response.body.message);
return;
}
message.error(e.message);
}
};
init();
return () => {
store.supplier.pageSize = 10;
};
}, []);
const callback = (key) => {
console.log(key);
};
const routeData = [ const routeData = [
{ {
route: "/app/home", route: LINKS.HOME,
name: "Home", name: "Beranda",
}, },
{ {
route: "/app/product", route: LINKS.PRODUCT,
name: <span style={{fontWeight: 'bold'}}>Product</span>, name: <span style={{ fontWeight: "bold" }}>Produk</span>,
}, },
]; ];
const onSubmit = async (data) => {
if (initialData.id) { const dataRoute = [
setInitialData({}) {
setConfirmLoading(true); route: LINKS.PRODUCT,
try { name: "Produk",
await store.product.update(initialData.id, data) },
message.success("Success Update Data Member") ];
} catch (e) {
message.error("Failed Update Data Member") const beforeUpload = (file) => {
} let isLt2M;
setConfirmLoading(false); let allowedFile = [
setVisibleModal(false); "text/csv",
} else { "application/csv",
setInitialData({}) "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
setConfirmLoading(true); "application/vnd.ms-excel.sheet.binary.macroEnabled.12",
try { "application/vnd.ms-excel",
await store.product.create(data) "application/vnd.ms-excel.sheet.macroEnabled.12",
message.success("Success Add New Member") ];
} catch (e) { let isValid = allowedFile.includes(file.type);
console.log(e, "apa errornya") if (!isValid) {
message.error("Failed Add Member") message.error("You can only upload Excel file!");
}
setConfirmLoading(false);
setVisibleModal(false);
} }
} isLt2M = file.size / 1024 / 1024 < 10;
if (!isLt2M) {
message.error("File must smaller than 10MB!");
}
return isValid && isLt2M ? true : Upload.LIST_IGNORE;
};
const uploadHandler = async (args) => {
const file = args.file;
try {
const response = await store.product.uploadExcel(file);
if (response.status === 201) {
message.success("Success upload excel!");
} else {
message.error("Failed upload excel!");
}
setFileList([
{
uid: "-1",
name: response.body.filename,
status: "done",
url: "",
},
]);
setExcel(response.body.filename);
} catch (e) {
setLoading(false);
message.error("Failed upload excel!");
}
};
const handleChange = (info) => {
if (info.file.status === "uploading") {
setLoading(true);
} else {
setLoading(false);
}
};
const handleUploadProduct = async (data) => {
try {
const response = await store.product.uploadProduct(data);
if (response.status === 201) {
message.success("Success Create Product by Excel!");
} else {
message.error("Failed Create Product by Excel!");
}
return response;
} catch (e) {
setLoading(false);
message.error("Failed Create Product by Excel!");
}
};
const handleCancel = () => {
form.resetFields();
setFileList([]);
setExcel("");
setVisibleModalUpload(false);
};
const handleSubmit = async (data) => {
const request = {
fileName: excel,
supplierCode: data.supplierCode,
};
const responseUploadProduct = await handleUploadProduct(request);
await store.product.getData();
setLoading(false);
setFileList([]);
setExcel("");
setVisibleModalUpload(false);
};
return ( return (
<div> <div className={["ppob-container"].join(" ")}>
<BreadcumbComponent data={routeData}/> <BreadcumbComponent
<Card> data={
<Row style={{marginBottom: 20}}> store.authentication.userData.role === "Admin" ||
store.authentication.userData.role === "Admin Partner"
? routeData
: dataRoute
}
/>
<Card>
<div>
<Row style={{ marginBottom: 20 }}>
<Col span={12}> <Col span={12}>
<Button> <Button
<FilterOutlined/> onClick={() => {
store.product.visibleModalFilterProduct = true;
}}
>
<FilterOutlined />
Filter Filter
</Button> </Button>
</Col> </Col>
<Col span={12} style={{textAlign: "right"}}> <Col span={12}>
<Search <div
placeholder="input search text" style={{
style={{width: 200, marginRight: 10}} display: store.ui.mediaQuery.isMobile ? "" : "flex",
/> justifyContent: "flex-end",
<Button onClick={() => setVisibleModal(true)}> textAlign: "right",
<PlusSquareOutlined/> New }}
</Button> >
{/* <Search
placeholder="input search text"
style={{
width: store.ui.mediaQuery.isMobile ? 160 : 200,
marginRight: store.ui.mediaQuery.isMobile ? 0 : 10,
marginBottom: store.ui.mediaQuery.isMobile ? 10 : 0,
}}
/> */}
{store.authentication.userData.role == "Admin" && (
<div
style={{
display: store.ui.mediaQuery.isMobile ? "" : "flex",
justifyContent: "flex-end",
textAlign: "right",
}}
>
<Button
disabled={visibleModalUpload}
style={{
marginRight: store.ui.mediaQuery.isMobile ? 0 : 10,
marginBottom: store.ui.mediaQuery.isMobile ? 10 : 10,
}}
icon={<PlusOutlined />}
onClick={() => setVisibleModalUpload(true)}
>
Tambah Produk
</Button>
</div>
)}
</div>
</Col> </Col>
</Row> </Row>
<Tabs </div>
defaultActiveKey="1" <ProductComponent />
onChange={callback} </Card>
size="default"
tabBarGutter="50" <Modal
visible={visibleModalUpload}
title={"Upload Excel Product"}
okText={"Create"}
cancelText="Cancel"
onCancel={() => {
form.resetFields();
handleCancel();
}}
onOk={() => {
form
.validateFields()
.then((values) => {
console.log(values, "isi form");
handleSubmit(values);
form.resetFields();
})
.catch((info) => {
console.error("Validate Failed:", info);
});
}}
>
<Form form={form} layout="vertical">
<Form.Item
name="fileName"
label="Upload Excel Product"
rules={[
{ required: true, message: "Please Upload Excel Product!" },
]}
> >
<TabPane tab="Pulsa" key="1"> <Upload
<Pulsa/> fileList={fileList}
</TabPane> onChange={handleChange}
<TabPane tab="Game Voucher" key="2"> beforeUpload={(file) => beforeUpload(file)}
Game Voucher customRequest={(args) => uploadHandler(args)}
</TabPane> onRemove={(file) => {
<TabPane tab="Product" key="3"> setLoading(false);
Product setFileList([]);
</TabPane> setExcel("");
<TabPane tab="Prduct" key="4"> }}
Prduct >
</TabPane> <div
<TabPane tab="Prdct" key="5"> style={{
Prdct display: "flex",
</TabPane> flexDirection: "row",
</Tabs> justifyContent: "center",
</Card> }}
<PulsaModal visible={visibleModal} >
confirmLoading={confirmLoading} <Button
initialData={initialData} disabled={loading}
onCreate={async (data) => { style={{
onSubmit(data) marginRight: store.ui.mediaQuery.isMobile ? 0 : 10,
}} }}
onCancel={() => { icon={<UploadOutlined />}
setInitialData({}) >
setVisibleModal(false); Upload Product
}}/> </Button>
</div> </div>
</Upload>
</Form.Item>
<Form.Item
name="supplierCode"
label="Supplier Code"
rules={[{ required: true, message: "Please input Supplier Code!" }]}
>
<Select>
{store.supplier.data.map((data) => (
<Option key={data.id} value={data.code}>
{data.name}
</Option>
))}
</Select>
</Form.Item>
</Form>
</Modal>
</div>
); );
}); });

View File

@ -0,0 +1,171 @@
import React, { useContext, useEffect } from "react";
import { Card, Col, Row, Table, Typography, Tag } from "antd";
import { BreadcumbComponent } from "../../component/BreadcumbComponent";
import { LINKS } from "../../routes/app";
import { useStore } from "../../utils/useStore";
import { observer } from "mobx-react-lite";
import { format, parseISO } from "date-fns";
import { ModalLoaderContext } from "../../utils/modal";
import { useParams } from "react-router-dom";
const { Title, Text } = Typography;
export const ProductDetail = observer(() => {
const store = useStore();
const { id } = useParams();
const modalLoader = useContext(ModalLoaderContext);
const routeData = [
{
route: LINKS.HOME,
name: "Beranda",
},
{
route: LINKS.PRODUCT,
name: <span style={{ fontWeight: "bold" }}>Produk</span>,
},
{
route: LINKS.PRODUCT_DETAIL.replace(":id", `${id}`),
name: <span style={{ fontWeight: "bold" }}>Detail Produk</span>,
},
];
useEffect(() => {
(async () => {
modalLoader.setLoading(true);
await Promise.allSettled([
store.product.getPriceHistoryByProduct(id),
store.product.getDetailProduct(id),
]);
modalLoader.setLoading(false);
})();
}, []);
const columns = [
{
title: "Markup Price",
dataIndex: "mark_up_price",
key: "mark_up_price",
render: (text) =>
new Intl.NumberFormat("id-ID", {
style: "currency",
currency: "IDR",
}).format(text),
},
{
title: "Price",
dataIndex: "price",
key: "price",
render: (text) =>
new Intl.NumberFormat("id-ID", {
style: "currency",
currency: "IDR",
}).format(text),
},
{
title: "Tanggal Berlaku",
dataIndex: "startDate",
key: "startDate",
render: (text) => {
return (
<Text>{text ? format(parseISO(text), "dd MMMM yyyy") : "-"}</Text>
);
},
},
{
title: "Tanggal Berakhir",
dataIndex: "endDate",
key: "endDate",
render: (text) => {
return (
<Text>
{text ? format(parseISO(text), "dd MMMM yyyy") : "Sampai Sekarang"}
</Text>
);
},
},
];
const styleSaldoTitle = store.ui.mediaQuery.isDesktop
? {
display: "flex",
justifyContent: "center",
}
: { fontSize: "0.75rem" };
const styleSaldoContent = store.ui.mediaQuery.isDesktop
? {
fontSize: "1.25rem",
display: "flex",
justifyContent: "center",
}
: null;
return (
<div className={[""].join(" ")}>
<BreadcumbComponent data={routeData} />
<Card>
<Title strong>Product Detail</Title>
<Row style={{ marginBottom: 20 }}>
<Col lg={12} xs={24}>
<Row>
<Col span={12}>
<Text strong>Kode</Text>
</Col>
<Col span={12}>
<Text>{store.product?.dataDetailProduct?.code}</Text>
</Col>
<Col span={12}>
<Text strong>Nama Produk</Text>
</Col>
<Col span={10}>
<Text>{store.product?.dataDetailProduct?.name}</Text>
</Col>
<Col span={12}>
<Text strong>Supplier</Text>
</Col>
<Col span={12}>
<Text>{store.product?.dataDetailProduct?.supplier?.name}</Text>
</Col>
<Col span={12}>
<Text strong>Status</Text>
</Col>
<Col span={12}>
<Text>{store.product?.dataDetailProduct?.status}</Text>
</Col>
</Row>
</Col>
</Row>
<Row>
<Col span={24}>
<div>
<Title strong level={3}>
Product Price History
</Title>
<Table
columns={columns}
dataSource={store.product.dataPriceHistory}
bordered
pagination={{
pageSize: store.product.pageSizePriceHistory,
total: store.product.totalDataPriceHistory,
current: store.product.pagePriceHistory + 1,
showSizeChanger: true,
simple: false,
}}
onChange={async (page) => {
let pageNumber = page.current;
store.product.pageSizePriceHistory = page.pageSize;
store.product.pagePriceHistory = pageNumber - 1;
modalLoader.setLoading(true);
await store.product.getPriceHistoryByProduct(id);
modalLoader.setLoading(false);
}}
/>
</div>
</Col>
</Row>
<div />
</Card>
</div>
);
});

View File

@ -0,0 +1,69 @@
import React, { useContext, useEffect } from "react";
import { Button, Card, Col, Input, message, Row } from "antd";
import { PlusSquareOutlined } from "@ant-design/icons";
import { BreadcumbComponent } from "../../component/BreadcumbComponent";
import { useStore } from "../../utils/useStore";
import { observer } from "mobx-react-lite";
import { LINKS } from "../../routes/app";
import { SubcategoryComponent } from "../../component/SubcategoryComponent";
import { ModalLoaderContext } from "../../utils/modal";
const { Search } = Input;
export const Subcategory = observer(() => {
const store = useStore();
const modalLoader = useContext(ModalLoaderContext);
useEffect(() => {
const init = async () => {
try {
modalLoader.setLoading(true);
await getData();
modalLoader.setLoading(false);
} catch (e) {
modalLoader.setLoading(false);
if (e.response?.body?.message) {
message.error(e.response.body.message);
return;
}
message.error(e.message);
}
};
init();
}, []);
const getData = async () => {
await store.category.getData();
await store.subcategory.getData();
};
const routeData = [
{
route: LINKS.HOME,
name: "Beranda",
},
{
route: LINKS.SUBCATEGORY,
name: <span style={{ fontWeight: "bold" }}>Sub Kategori</span>,
},
];
return (
<div className={["ppob-container"].join(" ")}>
<BreadcumbComponent data={routeData} />
<Card>
<Row style={{ marginBottom: 20 }}>
<Col span={24} style={{ textAlign: "right" }}>
<Button
onClick={() => (store.subcategory.visibleModalSubcategory = true)}
>
<PlusSquareOutlined /> New
</Button>
</Col>
</Row>
<SubcategoryComponent />
</Card>
</div>
);
});

View File

@ -0,0 +1,542 @@
import React, { useContext, useEffect, useState } from "react";
import {
Button,
Card,
Col,
Row,
Table,
Typography,
DatePicker,
Modal,
Form,
Input,
Divider,
List,
Image,
Tag,
} from "antd";
import moment from "moment";
import { BreadcumbComponent } from "../../component/BreadcumbComponent";
import { LINKS } from "../../routes/app";
import { useStore } from "../../utils/useStore";
import { observer } from "mobx-react-lite";
import { FilterOutlined } from "@ant-design/icons";
import { format, parseISO } from "date-fns";
import { appConfig } from "../../config/app";
import { ModalLoaderContext } from "../../utils/modal";
const { Title, Text } = Typography;
const { RangePicker } = DatePicker;
export const Profile = observer(() => {
const store = useStore();
const [form] = Form.useForm();
const modalLoader = useContext(ModalLoaderContext);
const [filterStart, setFilterStart] = useState([]);
const [filterEnd, setFilterEnd] = useState([]);
const routeData = [
{
route: LINKS.HOME,
name: "Beranda",
},
{
route: LINKS.PROFILE,
name: <span style={{ fontWeight: "bold" }}>Profil</span>,
},
];
const dataRoute = [
{
route: LINKS.PROFILE,
name: "Profil",
},
];
useEffect(() => {
(async () => {
modalLoader.setLoading(true);
await Promise.allSettled([
store.authentication.getProfile(),
store.transaction.getDataHistoryTransaction(),
]);
modalLoader.setLoading(false);
})();
}, []);
const handleRemoveFilter = async () => {
store.transaction.filterStart = null;
store.transaction.filterEnd = null;
form.resetFields();
setFilterStart([]);
setFilterEnd([]);
await store.transaction.getDataHistoryTransaction();
store.transaction.visibleModalFilterTransaction = false;
};
const handleCancelFilter = async () => {
store.transaction.filterStart = null;
//form.resetFields();
store.transaction.filterEnd = null;
store.transaction.visibleModalFilterTransaction = false;
await store.transaction.getDataHistoryTransaction();
};
const handleSubmitFilter = async () => {
const data = form.getFieldsValue();
const awal = (store.transaction.filterStart = moment(
data.start_date
).format("YYYY-MM-DD HH:mm:ss"));
const akhir = (store.transaction.filterEnd = moment(data.end_date).format(
"YYYY-MM-DD HH:mm:ss"
));
modalLoader.setLoading(true);
await store.transaction.getDataHistoryTransaction();
modalLoader.setLoading(false);
store.transaction.filterStart = null;
store.transaction.filterEnd = null;
//form.resetFields();
store.transaction.visibleModalFilterTransaction = false;
};
const footerLayoutFilter = [
<Button
key={"remove"}
onClick={handleRemoveFilter}
style={{
backgroundColor: "#e74e5e",
color: "#fff",
}}
>
Remove Filter
</Button>,
<Button key={"cancel"} onClick={handleCancelFilter}>
Cancel
</Button>,
<Button
key={"submit"}
onClick={handleSubmitFilter}
style={{
backgroundColor: "#4e79e7",
color: "#fff",
}}
>
Apply
</Button>,
];
const columns = [
{
title: "Name Produk",
dataIndex: "name",
key: "name",
},
{
title: "Price",
dataIndex: "price",
key: "price",
render: (text) =>
new Intl.NumberFormat("id-ID", {
style: "currency",
currency: "IDR",
}).format(text),
},
{
title: "Pembeli",
dataIndex: "buyer",
key: "buyer",
width: "10%",
},
{
title: "Tujuan",
dataIndex: "transaction_destination",
key: "transaction_destination",
},
{
title: "Kode Transaksi",
dataIndex: "transaction_code",
key: "transaction_code",
},
{
title: "Status",
dataIndex: "status",
key: "status",
render: (text, record) => {
return (
<Tag
color={
record.status === 1
? "success"
: record.status === 0
? "warning"
: "processing"
}
>
{record.status === 1
? "Success"
: record.status === 0
? "Pending"
: "Failed"}
</Tag>
);
},
},
{
title: "No Seri",
dataIndex: "seri_number",
key: "seri_number",
},
{
title: "IDTrx Mitra",
dataIndex: "partner_transaction_code",
key: "partner_transaction_code",
},
{
title: "Tanggal Transaksi",
dataIndex: "created_at",
key: "created_at",
width: "15%",
render: (text, record) => {
return (
<Text>
{format(parseISO(record.created_at), "dd-MM-yyyy HH:mm:ss")}
</Text>
);
},
},
{
title: "Alasan Gagal",
dataIndex: "failed_reason",
key: "failed_reason",
},
];
const styleSaldoTitle = store.ui.mediaQuery.isDesktop
? {
display: "flex",
justifyContent: "center",
}
: { fontSize: "0.75rem" };
const styleSaldoContent = store.ui.mediaQuery.isDesktop
? {
fontSize: "1.25rem",
display: "flex",
justifyContent: "center",
}
: null;
return (
<div className={["ppob-container"].join(" ")}>
<BreadcumbComponent
data={
store.authentication.userData.role === "Admin" ||
store.authentication.userData.role === "Admin Partner"
? routeData
: dataRoute
}
/>
<Card>
<Title strong>Profile</Title>
<Row style={{ marginBottom: 20 }}>
<Col lg={12} xs={24}>
<Row>
<Col span={12}>
<Text strong>Name</Text>
</Col>
<Col span={12}>
<Text>
{store.authentication.profileData?.userDetail?.name}
</Text>
</Col>
<Col span={12}>
<Text strong>Phone Number</Text>
</Col>
<Col span={12}>
<Text>
{store.authentication.profileData?.userDetail?.phone_number}
</Text>
</Col>
<Col span={12}>
<Text strong>Username</Text>
</Col>
<Col span={12}>
<Text>{store.authentication.profileData?.username}</Text>
</Col>
<Col span={12}>
<Text strong>Role</Text>
</Col>
<Col span={12}>
<Text>{store.authentication.profileData.roles?.name}</Text>
</Col>
<Col span={12}>
<Text strong>Superior</Text>
</Col>
<Col span={12}>
<Text>
{store.authentication.profileData.superior?.username}
</Text>
</Col>
{store.authentication.userData.role === "Retail" && (
<Row>
<Col span={12}>
<Text strong>Foto Identitas</Text>
</Col>
<Col span={12}></Col>
<Col span={12}>
<Text>
<Image
src={
store.authentication.profileData.userDetail
?.image_identity
? `${appConfig.apiUrl}/config/image/${store.authentication.profileData.userDetail?.image_identity}`
: "https://st4.depositphotos.com/14953852/24787/v/600/depositphotos_247872612-stock-illustration-no-image-available-icon-vector.jpg"
}
style={{ width: "10vw" }}
/>
</Text>
</Col>
<Col span={24}>
<Text strong>Foto Toko</Text>
<Text>
<Row>
{store.authentication.imageProfil.map((item) => (
<Image
src={
item
? `${appConfig.apiUrl}/config/image/${item}`
: "https://st4.depositphotos.com/14953852/24787/v/600/depositphotos_247872612-stock-illustration-no-image-available-icon-vector.jpg"
}
style={{ width: "10vw", marginRight: 15 }}
/>
))}
</Row>
</Text>
</Col>
</Row>
)}
</Row>
</Col>
<Col span={store.ui.mediaQuery.isMobile ? 24 : 5}>
<Row justify={"center"}>
<Col lg={12} xs={12}>
<Title strong level={3} style={styleSaldoTitle}>
Saldo
</Title>
</Col>
<Col lg={24} xs={12}>
<Text style={styleSaldoContent}>
{new Intl.NumberFormat("id-ID", {
style: "currency",
currency: "IDR",
}).format(store.authentication.profileData?.wallet || 0)}
</Text>
</Col>
<Col></Col>
</Row>
</Col>
{store.authentication.userData.role != "Admin Partner" &&
store.authentication.userData.role != "Retail" && (
<Col span={store.ui.mediaQuery.isMobile ? 24 : 5}>
<Row justify={"center"}>
<Col lg={12} xs={12}>
<Title strong level={3} style={styleSaldoTitle}>
Profit
</Title>
</Col>
<Col lg={24} xs={12}>
<Text style={styleSaldoContent}>
{new Intl.NumberFormat("id-ID", {
style: "currency",
currency: "IDR",
}).format(store.authentication.profileData?.profit || 0)}
</Text>
</Col>
<Col></Col>
</Row>
</Col>
)}
</Row>
<Row>
<Col span={24}>
<div>
<Title strong level={3}>
History Transaction
</Title>
<Button
style={{ marginBottom: "1rem" }}
onClick={() => {
store.transaction.visibleModalFilterTransaction = true;
}}
>
<FilterOutlined />
Filter
</Button>
{store.ui.mediaQuery.isDesktop && (
<Table
columns={columns}
bordered
scroll={{ x: 1300 }}
//scroll={{ x: 1500, y: 300 }}
dataSource={store.transaction.dataHistoryTransaction}
pagination={{
pageSize: store.transaction.pageSizeHistoryTransaction,
total: store.transaction.total_dataHistoryTransaction,
current: store.transaction.pageHistoryTransaction + 1,
showSizeChanger: true,
simple: false,
}}
onChange={async (page) => {
let pageNumber = page.current;
store.transaction.pageSizeHistoryTransaction =
page.pageSize;
store.transaction.pageHistoryTransaction = pageNumber - 1;
modalLoader.setLoading(true);
await store.transaction.getDataHistoryTransaction();
modalLoader.setLoading(false);
}}
/>
)}
{store.ui.mediaQuery.isMobile && (
<List
itemLayout="horizontal"
position={"top"}
dataSource={store.transaction.dataHistoryTransaction}
pagination={{
onChange: async (page, pageSize) => {
console.log(page, "Page");
console.log(pageSize, "Page size");
store.transaction.pageSizeHistoryTransaction = pageSize;
store.transaction.pageHistoryTransaction = page - 1;
modalLoader.setLoading(true);
await store.transaction.getDataHistoryTransaction();
modalLoader.setLoading(false);
},
pageSize: store.transaction.pageSizeHistoryTransaction,
total: store.transaction.total_dataHistoryTransaction,
current: store.transaction.pageHistoryTransaction + 1,
style: { marginBottom: "1rem", marginRight: "1rem" },
}}
style={{ padding: 0 }}
renderItem={(item) => {
return (
<div>
<List.Item
key={item.id}
style={{
backgroundColor: "#ffffff",
paddingTop: 0,
paddingBottom: 0,
display: "flex",
flexDirection: "row",
alignItems: "center",
justifyContent: "center",
}}
>
<List.Item.Meta
className={["cariparkir-container"].join(" ")}
title={item.buyer}
description={
<div style={{}}>
<p>
<small>Price : {item.price}</small> <br />
<small>
Tujuan : {item.transaction_destination}
</small>{" "}
<br />
<small>
Kode Transaksi : {item.transaction_code}
</small>{" "}
<br />
<small>
Status :{" "}
{
<Tag
color={
item.status === 1
? "success"
: item.status === 0
? "warning"
: "processing"
}
>
{item.status === 1
? "Success"
: item.status === 0
? "Pending"
: "Failed"}
</Tag>
}
</small>{" "}
<br />
<small>
No.Seri : {item.seri_number}
</small>{" "}
<br />
<small>
Alasan Gagal : {item.failed_reason}
</small>{" "}
<br />
<small>
IDTrx Mitra :{" "}
{item.partner_transaction_code}
</small>{" "}
<br />
<small>
Transaction Date :{" "}
{format(
parseISO(item.created_at),
"dd-MM-yyyy"
)}
</small>{" "}
<br />
</p>
</div>
}
/>
</List.Item>
<Divider plain style={{ margin: 0 }} />
</div>
);
}}
/>
)}
</div>
</Col>
</Row>
<div />
</Card>
<Modal
visible={store.transaction.visibleModalFilterTransaction}
title={"Filter"}
footer={footerLayoutFilter}
onCancel={async () => {
//form.resetFields();
store.transaction.filterStart = null;
store.transaction.filterEnd = null;
store.transaction.visibleModalFilterTransaction = false;
await store.transaction.getDataHistoryTransaction();
}}
>
<Row>
<Col span={24}>
<Form layout="vertical" name="filter" form={form}>
<Form.Item
name="start_date"
label="Dari"
rules={[{ required: true, message: "Please input Date!" }]}
>
<DatePicker style={{ width: "100%" }} />
</Form.Item>
<Form.Item
name="end_date"
label="Sampai"
rules={[{ required: true, message: "Please input Date!" }]}
>
<DatePicker style={{ width: "100%" }} />
</Form.Item>
</Form>
</Col>
</Row>
</Modal>
</div>
);
});

View File

@ -0,0 +1,279 @@
import React, { useContext, useEffect, useState } from "react";
import { useStore } from "../../utils/useStore";
import {
Card,
Col,
Form,
Input,
message,
Modal,
Row,
Select,
Button,
} from "antd";
import { observer } from "mobx-react-lite";
import { ModalLoaderContext } from "../../utils/modal";
const { Search } = Input;
const { Option } = Select;
export const Product = observer(() => {
const store = useStore();
const modalLoader = useContext(ModalLoaderContext);
const [form] = Form.useForm();
const [visibleModalBuy, setVisibleModalBuy] = useState(false);
const [barang, setBarang] = useState({});
useEffect(() => {
const init = async () => {
try {
modalLoader.setLoading(true);
await Promise.allSettled([
store.transaction.getDataSubCategories(),
store.transaction.getDataCategories(),
]);
modalLoader.setLoading(false);
} catch (e) {
modalLoader.setLoading(false);
if (e.response?.body?.message) {
message.error(e.response.body.message);
return;
}
message.error(e.message);
}
};
init();
}, []);
const handleChangeSubcategory = async (item) => {
store.transaction.filterSubCategory = item;
modalLoader.setLoading(true);
await store.transaction.getData();
modalLoader.setLoading(false);
};
const handleBuyProduct = async (data, productCode) => {
modalLoader.setLoading(true);
try {
const response = await store.transaction.buyProd({
...data,
productCode: productCode,
});
if (response.status === 201) {
message.success(response?.body?.message || "Berhasil Beli Produk");
} else {
message.error(response?.body?.error || "Gagal Beli Produk", 3);
}
} catch (e) {
console.log("testingan");
console.log(e.response, "testingan");
console.log(e.result, "testingan1");
if (e.response?.body?.error) {
message.error(e.response.body.error);
setVisibleModalBuy(false);
modalLoader.setLoading(false);
return;
}
console.log(e, "apa errornya");
message.error("Gagal Beli Product");
}
setVisibleModalBuy(false);
modalLoader.setLoading(false);
};
const handleBuyStag = async (data, productCode) => {
modalLoader.setLoading(true);
try {
const response = await store.transaction.buyProduct({
...data,
productCode: productCode,
});
if (response.status === 201) {
message.success(response?.body?.message || "Berhasil Beli Produk");
} else {
message.error(response?.body?.error || "Gagal Beli Produk", 3);
}
} catch (e) {
console.log("testingan");
console.log(e.response, "testingan");
console.log(e.result, "testingan1");
if (e.response?.body?.error) {
message.error(e.response.body.error);
setVisibleModalBuy(false);
modalLoader.setLoading(false);
return;
}
console.log(e, "apa errornya");
message.error("Gagal Beli Product");
}
// setDataProd(false);
// setDataStag(false);
setVisibleModalBuy(false);
modalLoader.setLoading(false);
};
const handleCancel = () => {
form.resetFields();
setVisibleModalBuy(false);
};
return (
<div>
<Row>
<span style={{ fontWeight: "bold", marginBottom: "10px" }}>
Sub Category
</span>
</Row>
<Row>
<Col span={24}>
<Select
placeholder={"Select Sub Category"}
allowClear={true}
onChange={(val) => handleChangeSubcategory(val)}
style={{ marginBottom: "10px", width: "100%" }}
value={store.transaction.filterSubCategory}
>
{store.transaction.dataSubCategories.map((item, index) => (
<Option key={item.id} value={item.id}>
{item.name}
</Option>
))}
</Select>
</Col>
</Row>
<Row justify={"center"} align={"center"} style={{ marginBottom: "1rem" }}>
<Col
span={12}
style={{ fontWeight: "bold", display: "flex", alignItems: "center" }}
>
Produk & Nominal
</Col>
<Col span={12} style={{ textAlign: "right" }}>
{/* <Search
placeholder="input search text"
style={{ width: 200, marginRight: 10 }}
/> */}
</Col>
</Row>
{store.transaction.data.length != 0 && (
<Row>
{store.transaction.data.map((item, index) => (
<Col key={index} xs={24} md={16} lg={8}>
<Card
onClick={() => {
setVisibleModalBuy(true);
setBarang(item);
}}
hoverable
style={{
cursor: "pointer",
marginLeft: 10,
borderColor: "salmon",
height: 100,
marginBottom: 10,
}}
>
<span style={{ color: "black" }}>{item?.product_name}</span>
<br />
<span style={{ color: "grey", fontSize: 10 }}>
{new Intl.NumberFormat("id-ID", {
style: "currency",
currency: "IDR",
}).format(item?.price)}
</span>
</Card>
</Col>
))}
</Row>
)}
<Modal
visible={visibleModalBuy}
title={`Are you sure buy ${barang?.product_name}?`}
okText={"Confirm"}
onCancel={() => {
form.resetFields();
setVisibleModalBuy(false);
}}
// footer={footerLayoutFilter}
footer={[
<Button
key="back"
style={{
backgroundColor: "#e74e5e",
color: "#fff",
}}
onClick={() => {
form.resetFields();
handleCancel();
}}
>
Cancel
</Button>,
<Button
key="Buy Prod"
style={{
backgroundColor: "#4e79e7",
color: "#fff",
}}
onClick={() => {
form
.validateFields()
.then((values) => {
console.log(values, "isi form");
handleBuyProduct(values, barang.product_code);
form.resetFields();
})
.catch((info) => {
console.error("Validate Failed:", info);
});
}}
>
Buy Prod
</Button>,
<Button
key="Buy Stag"
type="primary"
onClick={() => {
form
.validateFields()
.then((values) => {
console.log(values, "isi form");
handleBuyStag(values, barang.product_code);
form.resetFields();
})
.catch((info) => {
console.error("Validate Failed:", info);
});
}}
>
Buy Staging
</Button>,
]}
>
<Form form={form} layout="vertical">
<Form.Item
name="destination"
label="Destination"
rules={[
{
required: true,
message: "Please input Destination Number!",
},
// {
// pattern: /^(?:\d*)$/,
// message: "Value should contain just number",
// },
// {
// pattern: /^[\d]{1,12}$/,
// message: "Value should be 1 - 12 character",
// },
]}
>
<Input />
</Form.Item>
</Form>
</Modal>
</div>
);
});

View File

@ -1,121 +0,0 @@
import React from "react";
import {Button, Card, Col, Dropdown, Menu, message, Modal, Row, Space,} from "antd";
import {DownOutlined, TabletOutlined, UserOutlined} from "@ant-design/icons";
export const Pulsa = () => {
function handleMenuClick(e) {
message.info("Click on menu item.");
console.log("click", e);
}
const menu = (
<Menu onClick={handleMenuClick}>
<Menu.Item key="1" icon={<UserOutlined/>}>
1st menu item
</Menu.Item>
<Menu.Item key="2" icon={<UserOutlined/>}>
2nd menu item
</Menu.Item>
<Menu.Item key="3" icon={<UserOutlined/>}>
3rd menu item
</Menu.Item>
</Menu>
);
function success() {
Modal.success({
content: 'some messages...some messages...',
});
}
const dataCard = [
{
title: "DATA AXIS BRONET 2GB-60HR",
price: "Harga : Rp.6.000",
},
{
title: "DATA AXIS BRONET 2GB-60HR",
price: "Harga : Rp.6.000",
},
{
title: "DATA AXIS BRONET 2GB-60HR",
price: "Harga : Rp.6.000",
},
{
title: "DATA AXIS BRONET 2GB-60HR",
price: "Harga : Rp.6.000",
},
{
title: "DATA AXIS BRONET 2GB-60HR",
price: "Harga : Rp.6.000",
},
{
title: "DATA AXIS BRONET 2GB-60HR",
price: "Harga : Rp.6.000",
},
{
title: "DATA AXIS BRONET 2GB-60HR",
price: "Harga : Rp.6.000",
},
{
title: "DATA AXIS BRONET 2GB-60HR",
price: "Harga : Rp.6.000",
},
{
title: "DATA AXIS BRONET 2GB-60HR",
price: "Harga : Rp.6.000",
},
{
title: "DATA AXIS BRONET 2GB-60HR",
price: "Harga : Rp.6.000",
},
]
return (
<div>
<Row>
<span style={{fontWeight: "bold", marginBottom: "10px"}}>
Sub-Category
</span>
</Row>
<Row>
<Space wrap>
<Dropdown overlay={menu}>
<Button
style={{
marginBottom: "20px",
color: "grey",
}}
>
<TabletOutlined/>
Select sub-Category
<DownOutlined/>
</Button>
</Dropdown>
</Space>
</Row>
<Row>
<span style={{fontWeight: "bold", marginBottom: "10px"}}>
Produk & Nominal
</span>
</Row>
<Row>
{dataCard.map((item, index) => (
<Col key={index} xs={24} md={16} lg={8}>
<Card onClick={success}>
<span style={{color: "black"}}>{item.title}</span>
<br/>
<span style={{color: "grey", fontSize: 10}}>{item.price}</span>
</Card>
</Col>
))}
</Row>
<Col style={{textAlign: "right"}}>
<Button style={{backgroundColor: "#2D9CDB", color: "white"}}>
Beli Sekarang
</Button>
</Col>
</div>
);
};

View File

@ -1,67 +1,88 @@
import React from "react"; import React, { message, useContext, useEffect, useState } from "react";
import {Button, Card, Col, Input, Row, Tabs} from "antd"; import { useStore } from "../../utils/useStore";
import {FilterOutlined,} from "@ant-design/icons"; import {
import {BreadcumbComponent} from "../../component/BreadcumbComponent"; Card,
import {Pulsa} from "./Pulsa"; Tabs,
Col,
Button,
Typography,
Select,
Modal,
Row,
DatePicker,
} from "antd";
import { BreadcumbComponent } from "../../component/BreadcumbComponent";
import { Product } from "./Product";
import { LINKS } from "../../routes/app";
import { observer } from "mobx-react-lite";
import { ModalLoaderContext } from "../../utils/modal";
import { FilterOutlined } from "@ant-design/icons";
const {TabPane} = Tabs; const { TabPane } = Tabs;
const {Search} = Input; export const Transaction = observer(() => {
const store = useStore();
const { Title } = Typography;
const { Option } = Select;
const modalLoader = useContext(ModalLoaderContext);
export const Transaction = () => { useEffect(() => {
const callback = (key) => { const init = async () => {
console.log(key); try {
modalLoader.setLoading(true);
await store.transaction.getDataCategories();
modalLoader.setLoading(false);
} catch (e) {
modalLoader.setLoading(false);
if (e.response?.body?.message) {
message.error(e.response.body.message);
return;
}
message.error(e.message);
}
};
init();
}, []);
const handleChangeTabs = async (key) => {
modalLoader.setLoading(true);
store.transaction.dataSubCategories = [];
store.transaction.data = [];
store.transaction.filterSubCategory = null;
store.transaction.filterCategory = key;
await store.transaction.getDataSubCategories();
modalLoader.setLoading(false);
}; };
const routeData = [ const routeData = [
{ {
route: "/app/home", route: LINKS.TRANSACTION,
name: "Home", name: "Transaksi"
},
{
route: "/app/transaction",
name: <span style={{fontWeight: 'bold'}}>Transaction</span>,
}, },
]; ];
return ( return (
<div className={["ppob-container"].join(" ")}> <div className={["ppob-container"].join(" ")}>
<BreadcumbComponent data={routeData} text=""/> <BreadcumbComponent data={routeData} text="" />
<Card> <Card>
<Row style={{marginBottom: 20}}> <Col span={12} style={{ marginBottom: 30 }}>
<Col span={12}> {/* <Button
<Button> onClick={() => {
<FilterOutlined/> store.transaction.visibleModalFilterTransaction = true;
Filter }}
</Button>
</Col>
<Col span={12} style={{textAlign: "right"}}>
<Search
placeholder="input search text"
style={{width: 200, marginRight: 10}}
/>
</Col>
</Row>
<Tabs
defaultActiveKey="1"
onChange={callback}
size="default"
tabBarGutter="50"
> >
<TabPane tab="Pulsa" key="1"> <FilterOutlined />
<Pulsa/> Filter
</Button> */}
</Col>
<Tabs onChange={handleChangeTabs} size="default" tabBarGutter="50">
{store.transaction.dataCategories.map((item, index) => (
<TabPane tab={item.name} key={item.id}>
<Product />
</TabPane> </TabPane>
<TabPane tab="Game Voucher" key="2"> ))}
Game Voucher </Tabs>
</TabPane> </Card>
<TabPane tab="Product" key="3"> </div>
Product
</TabPane>
<TabPane tab="Prduct" key="4">
Product
</TabPane>
<TabPane tab="Prdct" key="5">
Product
</TabPane>
</Tabs>
</Card>
</div>
); );
}; });

View File

@ -2,28 +2,92 @@ import {Redirect, Route, Switch} from "react-router-dom";
import {Home} from "../pages/Home/Home"; import {Home} from "../pages/Home/Home";
import {About} from "../pages/About/About"; import {About} from "../pages/About/About";
import {Membership} from "../pages/Membership/Membership"; import {Membership} from "../pages/Membership/Membership";
import {DetailUser} from "../pages/Membership/DetailUser";
import {Product} from "../pages/Product/Product"; import {Product} from "../pages/Product/Product";
import {ProductDetail} from "../pages/Product/ProductDetail";
import {Transaction} from "../pages/Transaction/Transaction"; import {Transaction} from "../pages/Transaction/Transaction";
import {Profile} from "../pages/Profile/Profile";
import {Commission} from "../pages/Config/Commission";
import {Partner} from "../pages/Config/Partner";
import {Supplier} from "../pages/Config/Supplier";
import {Category} from "../pages/Product/Category";
import {Payback} from "../pages/Payback/Payback";
import {PaybackCreated} from "../pages/Payback/PaybackCreated";
import {Subcategory} from "../pages/Product/Subcategory";
import {Konfirmasi} from "../pages/Membership/Konfirmasi";
export const LINKS = {
HOME: "/app/home",
ABOUT: "/app/about",
MEMBERSHIP: "/app/membership",
PRODUCT: "/app/product",
TRANSACTION: "/app/transaction",
PROFILE: "/app/profile",
PARTNER: "/app/partner",
COMMISSION: "/app/commission",
SUPPLIER: "/app/supplier",
CATEGORY: "/app/category",
PAYBACK: "/app/payback",
KONFIRMASI: "/app/konfirmasi",
PAYBACK_CREATED: "/app/payback-created",
SUBCATEGORY: "/app/subcategory",
USER_DETAIL: "/app/user-detail/:id",
PRODUCT_DETAIL: "/app/product-detail/:id",
};
export const AppRoute = () => { export const AppRoute = () => {
return <Switch> return <Switch>
<Route path={"/app/home"}> <Route path={LINKS.HOME}>
<Home/> <Home/>
</Route> </Route>
<Route path={"/app/membership"}> <Route path={LINKS.USER_DETAIL}>
<DetailUser/>
</Route>
<Route path={LINKS.KONFIRMASI}>
<Konfirmasi/>
</Route>
<Route path={LINKS.PRODUCT_DETAIL}>
<ProductDetail/>
</Route>
<Route path={LINKS.COMMISSION}>
<Commission/>
</Route>
<Route path={LINKS.PAYBACK_CREATED}>
<PaybackCreated/>
</Route>
<Route path={LINKS.CATEGORY}>
<Category/>
</Route>
<Route path={LINKS.SUBCATEGORY}>
<Subcategory/>
</Route>
<Route path={LINKS.SUPPLIER}>
<Supplier/>
</Route>
<Route path={LINKS.MEMBERSHIP}>
<Membership/> <Membership/>
</Route> </Route>
<Route path={"/app/product"}> <Route path={LINKS.PRODUCT}>
<Product/> <Product/>
</Route> </Route>
<Route path={"/app/transaction"}> <Route path={LINKS.PARTNER}>
<Partner/>
</Route>
<Route path={LINKS.TRANSACTION}>
<Transaction/> <Transaction/>
</Route> </Route>
<Route path={"/app/about"}> <Route path={LINKS.PAYBACK}>
<Payback/>
</Route>
<Route path={LINKS.ABOUT}>
<About/> <About/>
</Route> </Route>
<Route path={LINKS.PROFILE}>
<Profile/>
</Route>
<Route path="/app" exact> <Route path="/app" exact>
<Redirect to={'/app/home'}/> <Redirect to={LINKS.HOME}/>
</Route> </Route>
</Switch> </Switch>
} }

67
src/store/approval.js Normal file
View File

@ -0,0 +1,67 @@
import {makeAutoObservable} from "mobx";
import {http} from "../utils/http";
export class Approval {
page = 0;
pageSize = 10
data = [];
listImage=[];
total_data = 0;
constructor(ctx) {
this.ctx = ctx;
makeAutoObservable(this);
}
async getData() {
try {
const response = await http.get(`/users/find-by-approval?page=${this.page}&pageSize=${this.pageSize}`);
console.log(response,"data dari store")
this.data = response.body.data ?? []
this.listImage= this.data.user_detail?.image_store ? JSON.parse(this.data.user_detail?.image_store) : []
this.total_data = response.body.count ?? 0
} catch (e) {
console.error(e);
}
}
async approveUser(id) {
try {
const response = await http.put(`/users/approve-user/${id}`);
await this.getData();
return response;
} catch (e) {
console.error(e);
}
}
async rejectUser(id) {
try {
const response = await http.put(`/users/reject-user/${id}`);
await this.getData();
return response;
} catch (e) {
console.error(e);
}
}
async resendUser(id) {
try {
const response = await http.put(`/users/resend-user/${id}`);
await this.getData();
return response;
} catch (e) {
console.error(e);
}
}
// async delete(id) {
// try {
// const response = await http.del(`/product/${id}`);
// await this.getData();
// return response;
// } catch (e) {
// console.error(e);
// }
// }
}

View File

@ -2,10 +2,15 @@ import {makeAutoObservable, runInAction} from "mobx";
import {TokenUtil} from "../utils/token"; import {TokenUtil} from "../utils/token";
import {http} from "../utils/http"; import {http} from "../utils/http";
export class Authentication { export class Authentication {
isLoggedIn = false; isLoggedIn = false;
isLoginLoading = false; isLoginLoading = false;
ctx; ctx;
profileData = {};
dataProfit=[];
listImage=[];
imageProfil=[];
constructor(ctx) { constructor(ctx) {
this.ctx = ctx; this.ctx = ctx;
@ -33,7 +38,7 @@ export class Authentication {
try { try {
const result = await http.post('/auth/login').send({username, password}); const result = await http.post('/auth/login').send({username, password});
TokenUtil.setAccessToken(result.body.access_token); TokenUtil.setAccessToken(result.body.access_token);
TokenUtil.persistToken(); TokenUtil.persistToken();
runInAction(() => { runInAction(() => {
@ -49,9 +54,35 @@ export class Authentication {
} }
} }
async getProfit(id) {
try {
const response = await http.get(`/auth/profile/${id}`);
console.log(response,"Data Gambar Store")
this.dataProfit = response.body ?? [];
this.listImage = this.dataProfit.userDetail?.image_store ? JSON.parse(this.dataProfit.userDetail?.image_store) : [];
this.total_data = response?.body?.count ?? 0;
} catch (e) {
console.error(e);
}
}
async getProfile() {
try {
const response = await http.get('/auth/profile');
console.log(response,"Data Profile")
this.profileData = response.body;
this.imageProfil = this.profileData.userDetail?.image_store ? JSON.parse(this.profileData.userDetail?.image_store) : [];
//this.imageProfil = this.profileData.userDetail?.image_store ? JSON.parse(this.profileData.userDetail?.image_store) : [];
} catch (e) {
console.error(e);
}
}
logout() { logout() {
TokenUtil.clearAccessToken(); TokenUtil.clearAccessToken();
TokenUtil.persistToken(); TokenUtil.persistToken();
this.isLoggedIn = false; this.isLoggedIn = false;
window.location.reload();
} }
} }

99
src/store/category.js Normal file
View File

@ -0,0 +1,99 @@
import {makeAutoObservable} from "mobx";
import {http} from "../utils/http";
export class Category {
page = 0;
pageSize = 10
data = [];
total_data = 0;
filterCategory = null;
visibleModalCategory = false;
pageSubCategories = 0;
pageSizeSubCategories = 10
dataSubCategories = [];
total_dataSubCategories = 0;
constructor(ctx) {
this.ctx = ctx;
makeAutoObservable(this);
}
async getData() {
try {
const response = await http.get(`/product/categories?page=${this.page}&pageSize=${this.pageSize}`);
//console.log(response)
this.data = response.body.data.map((item, idx) => {
item.key = idx;
return item
}) ?? []
this.total_data = response?.body?.count ?? 0
} catch (e) {
console.log(e);
}
}
async getDataSubCategories() {
try {
const response = await http.get(`/product/sub-categories?page=${this.pageSubCategories}&pageSize=${this.pageSizeSubCategories}`);
this.dataSubCategories = response.body.data.map((item, idx) => {
item.key = idx;
return item
}) ?? []
this.total_dataSubCategories = response.body.count ?? 0
} catch (e) {
console.log(e);
}
}
async getDataCategories() {
try {
const response = await http.get(`/product/categories?page=${this.pageCategories}&pageSize=${this.pageSizeCategories}`);
this.dataCategories = response.body.data.map((item, idx) => {
item.key = idx;
return item
}) ?? []
this.total_dataCategories = response?.body?.count ?? 0
if (this.dataCategories.length > 0) {
this.filterCategory = this.dataCategories[0].id
}
} catch (e) {
console.error(e);
}
}
async create(data) {
try {
const response = await http.post('/product/categories').send(data);
await this.getData();
return response;
} catch (e) {
console.log(e);
}
}
async update(id, data) {
try {
const response = await http.put(`/product/categories/${id}`).send(data);
await this.getData();
return response;
} catch (e) {
console.error(e);
}
}
async delete(id) {
try {
const response = await http.del(`/product/${id}`);
await this.getData();
return response;
} catch (e) {
console.error(e);
}
}
}

62
src/store/commission.js Normal file
View File

@ -0,0 +1,62 @@
import {makeAutoObservable} from "mobx";
import {http} from "../utils/http";
export class Commission {
page = 0;
pageSize = 10
data = [];
total_data = 0;
filterCategory = null;
visibleModalCommission = false;
pageCategories = 0;
pageSizeCategories = 10
dataCategories = [];
total_dataCategories = 0;
pageSubCategories = 0;
pageSizeSubCategories = 10
dataSubCategories = [];
total_dataSubCategories = 0;
constructor(ctx) {
this.ctx = ctx;
makeAutoObservable(this);
}
async getData() {
try {
const response = await http.get(`/config/commission?page=${this.page}&pageSize=${this.pageSize}`);
this.data = response.body.data.map((item, idx) => {
item.key = idx;
return item
}) ?? []
this.total_data = response?.body?.count ?? 0
} catch (e) {
console.error(e);
}
}
async update(id, data) {
try {
const response = await http.put(`/config/commission/${id}`).send(data);
await this.getData();
return response;
} catch (e) {
console.error(e);
}
}
async delete(id) {
try {
const response = await http.del(`/product/${id}`);
await this.getData();
return response;
} catch (e) {
console.error(e);
}
}
}

View File

@ -1,10 +1,19 @@
import {UI} from "./ui"; import { UI } from "./ui";
import {Authentication} from "./authentication"; import { Authentication } from "./authentication";
import {User} from "./user"; import { User } from "./user";
import {Membership} from "./membership"; import { Membership } from "./membership";
import {Product} from "./product"; import { Product } from "./product";
import {TokenUtil} from "../utils/token"; import { Partner } from "./partner";
import {Role} from "./role"; import { Supplier } from "./supplier";
import { Commission } from "./commission";
import { Transaction } from "./transaction";
import { TokenUtil } from "../utils/token";
import { Category } from "./category";
import { Subcategory } from "./subcategory";
import { Payback } from "./payback";
import { Role } from "./role";
import { Approval } from "./approval";
export class Store { export class Store {
ui = new UI(this); ui = new UI(this);
@ -12,7 +21,15 @@ export class Store {
user = new User(this); user = new User(this);
membership = new Membership(this); membership = new Membership(this);
product = new Product(this); product = new Product(this);
partner = new Partner(this);
supplier = new Supplier(this);
commission = new Commission(this);
category = new Category(this);
payback = new Payback(this);
transaction = new Transaction(this);
subcategory = new Subcategory(this);
role = new Role(this); role = new Role(this);
approval = new Approval(this);
constructor() { constructor() {
TokenUtil.loadToken(); TokenUtil.loadToken();

View File

@ -1,36 +1,135 @@
import {action, makeAutoObservable} from "mobx"; import {makeAutoObservable} from "mobx";
import {http} from "../utils/http"; import {http} from "../utils/http";
export class Membership { export class Membership {
page = 0; page = 0;
pageSize = 10 pageSize = 10
data = []; data = [];
total_data = 0 total_data = 0;
dataTotal=0;
dataDetail = {};
dataMember=[];
//filter
visibleModalFilterMembership = false;
filterMembership = null;
filterPartner = null;
constructor(ctx) { constructor(ctx) {
this.ctx = ctx; this.ctx = ctx;
makeAutoObservable(this); makeAutoObservable(this);
} }
@action
async getData() { async getData() {
const response = await http.get(`/users/find-by-supperior?page=${this.page}&pageSize=${this.pageSize}`); try {
this.data = response.body.data ?? [] const response = await http.get(`/users?page=${this.page}&pageSize=${this.pageSize}&superior=${this.filterMembership}&type=${this.filterPartner}`);
this.total_data = response.body.total_data ?? 0 console.log(this.filterMembership)
// console.log(this.filterPartner)
console.log(response)
this.dataMember = response.body.data ?? []
// this.dataMember = response.body.data.map((item, idx) => {
// item.key = idx;
// item.name = item?.user_detail?.name;
// item.phone_number = item?.user_detail?.phone_number;
// item.roleId = item?.roles.id;
// item.roleName = item?.roles.name;
// return item
// }) ?? []
this.dataTotal = response?.body?.count ?? 0
console.log(this.dataTotal)
} catch (e) {
console.error(e);
}
}
async getDetail(id) {
try {
const response = await http.get(`/users/`+id);
console.log(response,'Data Detail')
this.dataDetail = response.body.data
} catch (e) {
console.error(e);
}
}
async getDataBySuperior() {
try {
const response = await http.get(`/users/find-by-supperior?page=${this.page}&pageSize=${this.pageSize}`);
console.log(response)
this.data = response.body.data.map((item, idx) => {
item.key = idx;
item.name = item?.user_detail?.name;
item.phone_number = item?.user_detail?.phone_number;
item.roleId = item?.roles.id;
item.roleName = item?.roles?.name;
return item
}) ?? []
this.total_data = response?.body?.count ?? 0
console.log(this.total_data)
} catch (e) {
console.error(e);
}
} }
@action
async create(data) { async create(data) {
return await http.post('/users').send(data) try {
const response = await http.post('/users').send(data);
return response;
} catch (e) {
console.error(e);
}
} }
@action
async update(id, data) { async update(id, data) {
return await http.put('/users/' + id).send(data); try {
const response = await http.put('/users/' + id).send(data);
console.log(data,"data dari store")
return response;
} catch (e) {
console.error(e);
}
}
async changePassword(id, data) {
try {
const response = await http.put('/users/change-password/' + id).send(data);
return response;
} catch (e) {
console.error(e);
}
} }
async delete(id) { async delete(id) {
return await http.del('/users/' + id); try {
const response = await http.del('/users/' + id);
return response;
} catch (e) {
console.error(e);
}
}
async changeStatus(id, status) {
try {
const response = await http.get(`/users/${id}/${status}`);
console.log(status,"change status")
await this.getData();
return response;
} catch (e) {
console.error(e);
}
}
async withdrawProfit(id) {
try {
const response = await http.put(`/transaction/withdraw/${id}`);
await this.getData();
return response;
} catch (e) {
console.error(e);
}
} }
} }

91
src/store/partner.js Normal file
View File

@ -0,0 +1,91 @@
import {makeAutoObservable} from "mobx";
import {http} from "../utils/http";
export class Partner {
page = 0;
pageSize = 10
data = [];
total_data = 0;
filterCategory = null;
visibleModalPartner = false;
pageCategories = 0;
pageSizeCategories = 10
dataCategories = [];
total_dataCategories = 0;
pageSubCategories = 0;
pageSizeSubCategories = 10
dataSubCategories = [];
total_dataSubCategories = 0;
constructor(ctx) {
this.ctx = ctx;
makeAutoObservable(this);
}
async getData() {
try {
const response = await http.get(`/users/partner?page=${this.page}&pageSize=${this.pageSize}`);
//console.log(response)
this.data = response.body.data.map((item, idx) => {
item.key = idx;
return item
}) ?? []
this.total_data = response.body.count ?? 0
} catch (e) {
console.error(e);
}
}
async create(data) {
try {
const response = await http.post('/users/partner').send(data);
await this.getData();
return response;
} catch (e) {
console.error(e);
}
}
async update(id, data) {
try {
const response = await http.put(`/users/partner/${id}`).send(data);
await this.getData();
return response;
} catch (e) {
console.error(e);
}
}
async updatePassword(id, data) {
const response = await http.put(`/users/change-password-partner/${id}`).send(data);
console.log(response)
await this.getData();
return response;
}
async delete(id) {
try {
const response = await http.del(`/product/${id}`);
await this.getData();
return response;
} catch (e) {
console.error(e);
}
}
async changeStatus(id, status) {
try {
const response = await http.get(`/users/partner/${id}/${status}`);
await this.getData();
return response;
} catch (e) {
console.error(e);
}
}
}

125
src/store/payback.js Normal file
View File

@ -0,0 +1,125 @@
import { makeAutoObservable } from "mobx";
import { http } from "../utils/http";
export class Payback {
pageCreated = 0;
pageSizeCreated = 10;
dataCreated = [];
totalDataCreated = 0;
filterCategory = null;
visibleModalPayback = false;
pageConfirmation = 0;
pageSizeConfirmation = 10;
dataConfirmation = [];
totalDataConfirmation = 0;
//data=[]
//filter
visibleModalFilterPayback = false;
filterMembership = null;
filterStart = null;
filterEnd = null;
filterStartConfirmation = null;
filterEndConfirmation = null;
//filter created
visibleModalFilterCreate = false;
constructor(ctx) {
this.ctx = ctx;
makeAutoObservable(this);
}
async getDataCreated() {
try {
const response = await http.get(
`/transaction/deposit-return?page=${this.pageCreated}&pageSize=${this.pageSizeCreated}&start=${this.filterStart}&end=${this.filterEnd}`
);
console.log(response)
this.dataCreated =
response.body.data.map((item, idx) => {
item.key = idx;
return item;
}) ?? [];
this.totalDataCreated = response.body.count ?? 0;
} catch (e) {
console.error(e);
}
}
async getDataConfirmation() {
try {
const response = await http.get(
`/transaction/deposit-return/confirmation?page=${this.pageConfirmation}&pageSize=${this.pageSizeConfirmation}&start=${this.filterStart}&end=${this.filterEnd}&sender=${this.filterMembership}`
);
console.log(response);
this.dataConfirmation =
response.body.data.map((item, idx) => {
item.key = idx;
return item;
}) ?? [];
this.totalDataConfirmation = response.body.count ?? 0;
} catch (e) {
console.error(e);
}
}
async update(id, data) {
try {
const response = await http.put(`/config/commission/${id}`).send(data);
await this.getDataCreated();
return response;
} catch (e) {
console.error(e);
}
}
async delete(id) {
try {
const response = await http.del(`/product/${id}`);
await this.getDataCreated();
return response;
} catch (e) {
console.error(e);
}
}
async create(data) {
try {
const response = await http
.post("/transaction/deposit-return")
.send(data);
await this.getDataCreated();
return response;
} catch (e) {
console.error(e);
}
}
async confirmPayback(id, data) {
try {
const response = await http.put(
`/transaction/deposit-return/confirmation/${id}/${data}`
);
// console.log(response)
// this.data=response.body.data
await Promise.all([this.getDataConfirmation(), this.getDataCreated()]);
return response;
} catch (e) {
console.error(e);
}
}
async uploadImages(data) {
try {
const response = await http.upload(data);
return response;
} catch (e) {
console.error(e);
}
}
}

View File

@ -1,39 +1,170 @@
import {action, makeAutoObservable} from "mobx"; import {makeAutoObservable} from "mobx";
import {http} from "../utils/http"; import {http} from "../utils/http";
export class Product { export class Product {
page = 0; page = 0;
pageSize = 10 pageSize = 10
data = []; data = [];
total_data = 0 total_data = 0;
total_data_partner=0;
filterSupplier = null;
filterSubCategory = null;
visibleModalProduct = false;
visibleModalFilterProduct = false;
uploadBtnProduct = false;
pageCategories = 0;
pageSizeCategories = 100;
dataCategories = [];
total_dataCategories = 0;
pageSubCategories = 0;
pageSizeSubCategories = 10
dataSubCategories = [];
total_dataSubCategories = 0;
filterCategory = null;
dataPriceHistory = [];
totalDataPriceHistory = 0;
pagePriceHistory = 0;
pageProductPartner = 0;
pageSizePriceHistory = 10
dataDetailProduct = {};
dataProductPartner=[]
constructor(ctx) { constructor(ctx) {
this.ctx = ctx; this.ctx = ctx;
makeAutoObservable(this); makeAutoObservable(this);
} }
@action
async getData() { async getData() {
const response = await http.get(`/product?page=${this.page}&pageSize=${this.pageSize}`); try {
console.log(response,'Data') const response = await http.get(`/product/all?supplier=${this.filterSupplier}&sub-category=${this.filterSubCategory}&page=${this.page}&pageSize=${this.pageSize}`);
console.log(JSON.stringify(response.body.data),'Data') console.log(response)
this.data = response.body.data.map((item, idx) => {
this.data = response.body.data ?? [] item.key = idx;
this.total_data = response.body.total_data ?? 0 return item
}) ?? []
this.total_data = response?.body?.count ?? 0
//console.log(this.total_data)
} catch (e) {
console.error(e);
}
}
async getDataSubCategories() {
try {
const response = await http.get(`/product/sub-categories?category=${this.filterCategory}&page=${this.pageSubCategories}&pageSize=${this.pageSizeSubCategories}`);
this.dataSubCategories = response.body.data.map((item, idx) => {
item.key = idx;
return item
}) ?? []
this.total_dataSubCategories = response.body.count ?? 0
} catch (e) {
console.error(e);
}
}
async getDataCategories() {
try {
const response = await http.get(`/product/categories?page=${this.pageCategories}&pageSize=${this.pageSizeCategories}`);
this.dataCategories = response.body.data.map((item, idx) => {
item.key = idx;
return item
}) ?? []
} catch (e) {
console.error(e);
}
}
async getPriceHistoryByProduct(id) {
try {
const response = await http.get(`/product/price-history/${id}?page=${this.pagePriceHistory}&pageSize${this.pageSizePriceHistory}`);
this.dataPriceHistory = response.body.data
this.totalDataPriceHistory = response?.body?.count ?? 0
} catch (e) {
console.error(e);
}
}
async getDetailProduct(id) {
try {
const response = await http.get(`/product/${id}`);
this.dataDetailProduct = response.body.data
} catch (e) {
console.error(e);
}
}
async getProductPartner(id) {
try {
const response = await http.get(`/product/by-categories?page=${this.pageProductPartner}&pageSize=10&sub-category=${id}`);
console.log(response)
this.dataProductPartner = response.body.data
this.total_data_partner= response?.body?.count ?? 0
} catch (e) {
console.error(e);
}
} }
@action
async create(data) { async create(data) {
return await http.post('/product').send(data) try {
const response = await http.post('/product').send(data);
await this.getData();
return response;
} catch (e) {
console.error(e)
}
}
async buyProduct(data) {
try {
const response = await http.post('/transaction/order').send(data);
await this.getData();
return response;
} catch (e) {
console.error(e);
}
} }
@action
async update(id, data) { async update(id, data) {
return await http.put('/user/' + id).send(data); try {
const response = await http.put(`/product/${id}`).send(data);
await this.getData();
return response;
} catch (e) {
console.error(e);
}
} }
async delete(id) { async delete(id) {
return await http.del('/product/' + id); try {
const response = await http.del(`/product/${id}`);
await this.getData();
return response;
} catch (e) {
console.error(e);
}
}
async uploadExcel(data) {
try {
const response = await http.upload(data);
return response;
} catch (e) {
console.error(e);
}
}
async uploadProduct(data) {
try {
const response = await http.post('/product/upload-product').send(data);
return response;
} catch (e) {
console.error(e);
}
} }
} }

View File

@ -2,33 +2,51 @@ import {makeAutoObservable} from "mobx";
import {http} from "../utils/http"; import {http} from "../utils/http";
export class Role { export class Role {
page = null; page = 0;
pageSize = null; pageSize = 10;
data = []; data = [];
total_data = 0 total_data = 0;
constructor(ctx) { constructor(ctx) {
this.ctx = ctx; this.ctx = ctx;
makeAutoObservable(this); makeAutoObservable(this);
} }
async getData() { async getData(isForMembership = false) {
const response = await http.get(`/config/roles?page=${this.page}&pageSize=${this.pageSize}`); try {
this.data = response.body.data ?? [] const response = await http.get(
this.total_data = response.body.total_data ?? 0 `/config/roles${isForMembership ? "/for-membership" : ""}?page=${
this.page
}&pageSize=${this.pageSize}`
);
this.data = response.body.data ?? [];
this.total_data = response?.body?.count ?? 0;
} catch (e) {
console.error(e);
} }
}
async create(data) { async create(data) {
return await http.post('/users').send(data) try {
return await http.post("/users").send(data);
} catch (e) {
console.error(e);
} }
}
async update(id, data) { async update(id, data) {
return await http.put('/users/' + id).send(data); try {
return await http.put("/users/" + id).send(data);
} catch (e) {
console.error(e);
} }
}
async delete(id) { async delete(id) {
return await http.del('/users/' + id); try {
return await http.del("/users/" + id);
} catch (e) {
console.error(e);
} }
}
} }

88
src/store/subcategory.js Normal file
View File

@ -0,0 +1,88 @@
import {makeAutoObservable} from "mobx";
import {http} from "../utils/http";
export class Subcategory {
page = 0;
pageSize = 10
data = [];
total_data = 0;
filterCategory = null;
visibleModalSubcategory = false;
pageCategories = 0;
pageSizeCategories = 10
dataCategories = [];
total_dataCategories = 0;
pageSubCategories = 0;
pageSizeSubCategories = 10
dataSubCategories = [];
total_dataSubCategories = 0;
constructor(ctx) {
this.ctx = ctx;
makeAutoObservable(this);
}
async getData() {
try {
const response = await http.get(`/product/sub-categories?page=${this.page}&pageSize=${this.pageSize}`);
console.log(response)
this.data = response.body.data.map((item, idx) => {
item.key = idx;
item.categoryName = item.category.name;
return item
}) ?? []
this.total_data = response?.body?.count ?? 0
} catch (e) {
console.error(e);
}
}
async getDataSubCategories() {
try {
const response = await http.get(`/product/sub-categories?page=${this.pageSubCategories}&pageSize=${this.pageSizeSubCategories}`);
this.dataSubCategories = response.body.data.map((item, idx) => {
item.key = idx;
return item
}) ?? []
this.total_dataSubCategories = response.body.count ?? 0
} catch (e) {
console.error(e);
}
}
async create(data) {
try {
const response = await http.post('/product/sub-categories').send(data);
//await this.getData();
return response;
} catch (e) {
console.error(e);
}
}
async update(id, data) {
try {
const response = await http.put(`/product/sub-categories/${id}`).send(data);
//await this.getData();
return response;
} catch (e) {
console.error(e);
}
}
async delete(id) {
try {
const response = await http.del(`/product/${id}`);
await this.getData();
return response;
} catch (e) {
console.error(e);
}
}
}

87
src/store/supplier.js Normal file
View File

@ -0,0 +1,87 @@
import { makeAutoObservable } from "mobx";
import { http } from "../utils/http";
export class Supplier {
page = 0;
pageSize = 10;
data = [];
total_data = 0;
filterCategory = null;
visibleModalSupplier = false;
visibleModalTransaction = false;
code = "";
pageCategories = 0;
pageSizeCategories = 10;
dataCategories = [];
total_dataCategories = 0;
pageSubCategories = 0;
pageSizeSubCategories = 10;
dataSubCategories = [];
total_dataSubCategories = 0;
constructor(ctx) {
this.ctx = ctx;
makeAutoObservable(this);
}
async getData() {
try {
const response = await http.get(
`/users/supplier?page=${this.page}&pageSize=${this.pageSize}`
);
//console.log(response)
this.data = response.body.data ?? [];
this.total_data = response.body.count ?? 0;
} catch (e) {
console.error(e);
}
}
async create(data) {
const response = await http.post("/users/supplier").send(data);
return response;
}
async createTransaction(data) {
try {
const response = await http
.post("/transaction/add-saldo-supplier")
.send(data);
await this.getData();
return response;
} catch (e) {
console.error(e);
}
}
async update(id, data) {
try {
const response = await http.put(`/users/supplier/${id}`).send(data);
await this.getData();
return response;
} catch (e) {
console.error(e);
}
}
async delete(id) {
try {
const response = await http.del(`/product/${id}`);
await this.getData();
return response;
} catch (e) {
console.error(e);
}
}
async changeStatus(id, status) {
try {
const response = await http.get(`/users/supplier/${id}/${status}`);
await this.getData();
return response;
} catch (e) {
console.error(e);
}
}
}

229
src/store/transaction.js Normal file
View File

@ -0,0 +1,229 @@
import { makeAutoObservable } from "mobx";
import { http } from "../utils/http";
export class Transaction {
page = 0;
pageSize = 10;
data = [];
total_data = 0;
filterSubCategory = null;
visibleModalProduct = false;
visibleModalTransaction = false;
pageSizeDetail=10;
pageDetail=0
pageCategories = 0;
pageSizeCategories = 10;
dataCategories = [];
total_dataCategories = 0;
pageSubCategories = 0;
pageSizeSubCategories = 10;
dataSubCategories = [];
total_dataSubCategories = 0;
filterSubCategory = null;
pageHistoryTransaction = 0;
pageSizeHistoryTransaction = 10;
dataHistoryTransaction = [];
total_dataHistoryTransaction = 0;
pageHistoryTransactionDetailUser = 0;
pageSizeHistoryTransactionDetailUser = 10;
dataHistoryTransactionDetailUser = [];
total_dataHistoryTransactionDetailUser = 0;
pageHistoryTopUp = 0;
pageSizeHistoryTopUp = 10;
dataHistoryTopUp = [];
total_dataHistoryTopUp = 0;
dataTransaction = [];
dataTransactionB2B = [];
dataTransactionPartner = [];
total_dataDetailHistoryTransactionDetailUser=0;
//filter
visibleModalFilterTransaction = false;
filterStart = null;
filterEnd = null;
filterStartDetailUser = null;
filterEndDetailUser = null;
constructor(ctx) {
this.ctx = ctx;
makeAutoObservable(this);
}
async getData() {
try {
const response = await http.get(
`/product/by-categories-all?sub-category=${this.filterSubCategory}&page=${this.page}&pageSize=${this.pageSize}`
);
this.data = response.body.data ?? [];
this.total_data = response?.body?.count ?? 0;
} catch (e) {
console.error(e);
}
}
async getDataTransaction() {
try {
const response = await http.get(`/transaction/total-order`);
//console.log(response)
this.dataTransaction = response.body.data;
} catch (e) {
console.error(e);
}
}
async getDataTransactionB2B() {
try {
const response = await http.get(`/transaction/total-order-b2b`);
//console.log(response)
this.dataTransactionB2B = response.body.data;
} catch (e) {
console.error(e);
}
}
async getDataTransactionPartner() {
try {
const response = await http.get(`/transaction/total-order-partner`);
//console.log(response)
this.dataTransactionPartner = response.body.data;
} catch (e) {
console.error(e);
}
}
async getDataSubCategories() {
try {
const response = await http.get(
`/product/sub-categories?category=${this.filterCategory}&page=${this.pageSubCategories}&pageSize=${this.pageSizeSubCategories}`
);
this.dataSubCategories = response.body.data ?? [];
this.total_dataSubCategories = response.body.count ?? 0;
} catch (e) {
console.error(e);
}
}
async getDataCategories() {
try {
const response = await http.get(
`/product/categories?page=${this.pageCategories}&pageSize=${this.pageSizeCategories}`
);
this.dataCategories = response.body.data ?? [];
this.total_dataCategories = response?.body?.count ?? 0;
if (this.dataCategories.length > 0) {
this.filterCategory = this.dataCategories[0].id;
}
} catch (e) {
console.error(e);
}
}
async getDataHistoryTransaction() {
try {
const response = await http.get(
`/transaction/history?page=${this.pageHistoryTransaction}&pageSize=${this.pageSizeHistoryTransaction}&start=${this.filterStart}&end=${this.filterEnd}`
);
console.log(response);
this.dataHistoryTransaction = response.body.data ?? [];
this.total_dataHistoryTransaction = response?.body?.count ?? 0;
console.log(this.total_dataHistoryTransaction)
} catch (e) {
console.error(e);
}
}
async getDetailHistoryTransaction(id) {
try {
const response = await http.get(`/transaction/history-user/${id}?page=${this.page}&pageSize=${this.pageSize}&start=${this.filterStart}&end=${this.filterEnd}`);
console.log(response,'Data Trans');
this.dataDetailHistoryTransactionDetailUser = response.body.data ?? [];
this.total_data = response?.body?.count ?? 0;
} catch (e) {
console.error(e);
}
}
async getDataHistoryTopUp(id) {
try {
const response = await http.get(
`/transaction/history-deposit?page=${this.pageHistoryTopUp}&pageSize=${this.pageSizeHistoryTopUp}&user-destination=${id}`
);
console.log(response,'get data history')
this.dataHistoryTopUp = response.body.data ?? [];
this.total_dataHistoryTopUp = response?.body?.count ?? 0;
} catch (e) {
console.error(e);
}
}
async create(data) {
try {
const response = await http.post("/product").send(data);
await this.getData();
return response;
} catch (e) {
console.error(e);
}
}
async buyProduct(data) {
const response = await http.post("/transaction/order").send(data);
console.log(response,'dari store')
return response;
}
async buyProd(data) {
const response = await http.post("/transaction/order-prod").send(data);
console.log(response)
return response;
}
async update(id, data) {
try {
const response = await http.put(`/product/${id}`).send(data);
await this.getData();
return response;
} catch (e) {
console.error(e);
}
}
async delete(id) {
try {
const response = await http.del(`/product/${id}`);
await this.getData();
return response;
} catch (e) {
console.error(e);
}
}
async distribute(data) {
try {
const response = await http.post("/transaction/distribute").send(data);
return response;
} catch (e) {
console.error(e);
}
}
async distributeAdmin(data) {
try {
const response = await http
.post("/transaction/distribute-admin")
.send(data);
return response;
} catch (e) {
console.error(e);
}
}
}

View File

@ -13,7 +13,7 @@ export class UI {
} }
setTestValue() { setTestValue() {
this.testValue = "yo dayo"; this.testValue = "yoshahhh #!";
} }
setMediaQuery(data) { setMediaQuery(data) {
@ -23,7 +23,7 @@ export class UI {
}; };
toggleLeftDrawerIsShown() { toggleLeftDrawerIsShown() {
console.log('what') //console.log('what')
this.leftDrawerIsShown = !this.leftDrawerIsShown; this.leftDrawerIsShown = !this.leftDrawerIsShown;
} }

View File

@ -1,12 +1,20 @@
import {action, observable} from "mobx"; import {makeAutoObservable} from "mobx";
import {http} from "../utils/http"; import {http} from "../utils/http";
export class User { export class User {
@observable data = []; data = [];
constructor(ctx) {
this.ctx = ctx;
makeAutoObservable(this);
}
@action
async getData() { async getData() {
this.data = (await http.get('/product')).body.data; try {
this.data = (await http.get('/user')).body.data;
} catch (e) {
console.error(e);
}
} }
} }

View File

@ -1,46 +1,71 @@
import superagent from "superagent"; import superagent from "superagent";
import superagentIntercept from 'superagent-intercept';
import {appConfig} from "../config/app"; import {appConfig} from "../config/app";
import {TokenUtil} from "./token"; import {TokenUtil} from "./token";
import {attachSuperagentLogger} from "./http_logger";
let authIntercept = superagentIntercept((err, res) => {
if (res && res.status === 401) {
TokenUtil.clearAccessToken();
TokenUtil.persistToken();
window.location.href = "/login";
}
});
export const http = { export const http = {
get: (url, opts = {}) => { get: (url, opts = {}) => {
let req = superagent.get(appConfig.apiUrl + url); let req = superagent.get(appConfig.apiUrl + url)
.use(authIntercept)
.use(attachSuperagentLogger);
if (TokenUtil.accessToken) { if (TokenUtil.accessToken) {
req = req.set('Authorization', 'Bearer ' + TokenUtil.accessToken); req = req.set('Authorization', 'Bearer ' + TokenUtil.accessToken);
} }
return req; return req;
}, },
post: (url, opts) => { post: (url, opts) => {
let req = superagent.post(appConfig.apiUrl + url); let req = superagent.post(appConfig.apiUrl + url)
.use(authIntercept)
.use(attachSuperagentLogger);
if (TokenUtil.accessToken) { if (TokenUtil.accessToken) {
req = req.set('Authorization', 'Bearer ' + TokenUtil.accessToken); req = req.set('Authorization', 'Bearer ' + TokenUtil.accessToken);
} }
return req; return req;
}, },
put: (url, opts) => { put: (url, opts) => {
let req = superagent.put(appConfig.apiUrl + url); let req = superagent.put(appConfig.apiUrl + url)
.use(authIntercept)
.use(attachSuperagentLogger);
if (TokenUtil.accessToken) { if (TokenUtil.accessToken) {
req = req.set('Authorization', 'Bearer ' + TokenUtil.accessToken); req = req.set('Authorization', 'Bearer ' + TokenUtil.accessToken);
} }
return req; return req;
}, },
del: (url, opts) => { del: (url, opts) => {
let req = superagent.del(appConfig.apiUrl + url); let req = superagent.del(appConfig.apiUrl + url)
.use(authIntercept)
.use(attachSuperagentLogger);
if (TokenUtil.accessToken) { if (TokenUtil.accessToken) {
req = req.set('Authorization', 'Bearer ' + TokenUtil.accessToken); req = req.set('Authorization', 'Bearer ' + TokenUtil.accessToken);
} }
return req; return req;
}, },
upload: (file) => { upload: (file) => {
const request = superagent let req = superagent
.post(appConfig.apiUrl + '/files') .post(appConfig.apiUrl + '/config/upload-files')
.attach('file', file); .attach('file', file)
.use(authIntercept)
.use(attachSuperagentLogger);
if (TokenUtil.accessToken) {
req = req.set('Authorization', 'Bearer ' + TokenUtil.accessToken);
}
return request; return req;
}, },
uploadAntd: (args) => { uploadAntd: (args) => {
const file = args.file; const file = args.file;
const request = http.upload(file); const request = http.upload(file)
.use(authIntercept)
.use(attachSuperagentLogger);
request request
.on('progress', event => { .on('progress', event => {
args.onProgress(event); args.onProgress(event);

17
src/utils/http_logger.js Normal file
View File

@ -0,0 +1,17 @@
export function attachSuperagentLogger(req) {
const callback = req.callback;
console.log('%s %s %s',
req.method.padEnd('delete'.length, " "),
req.url,
'(pending)'
);
req.callback = function (err, res) {
console.log('%s %s %s',
req.method.padEnd('delete'.length, " "),
req.url,
res ? res.status : '-'
);
callback.call(req, err, res);
};
}

3
src/utils/modal.js Normal file
View File

@ -0,0 +1,3 @@
import React from "react";
export const ModalLoaderContext = React.createContext(null);

7671
yarn.lock

File diff suppressed because it is too large Load Diff