Pages Membership
:exit :q
This commit is contained in:
commit
d5251b0967
1
.dockerignore
Normal file
1
.dockerignore
Normal file
|
@ -0,0 +1 @@
|
|||
node_modules
|
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -24,4 +24,4 @@ yarn-error.log*
|
|||
|
||||
/.idea
|
||||
/package-lock.json
|
||||
/yarn.lock
|
||||
|
||||
|
|
68
.gitlab-ci.yml
Normal file
68
.gitlab-ci.yml
Normal 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 $NWP_REGISTRY_USER -p $NWP_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="${NWP_CLUSTER_HOST}"
|
||||
- kubectl config set clusters.k8s.certificate-authority-data ${NWP_CLUSTER_CA}
|
||||
- kubectl config set-credentials gitlab --token="${NWP_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
12
Dockerfile
Normal 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;"]
|
22
k8s/staging/deployment.yaml
Normal file
22
k8s/staging/deployment.yaml
Normal 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
25
k8s/staging/ingress.yaml
Normal 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
|
9
k8s/staging/middleware.yaml
Normal file
9
k8s/staging/middleware.yaml
Normal 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
|
4
k8s/staging/namespace.yaml
Normal file
4
k8s/staging/namespace.yaml
Normal file
|
@ -0,0 +1,4 @@
|
|||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: empatnusabangsa-staging
|
13
k8s/staging/service.yaml
Normal file
13
k8s/staging/service.yaml
Normal 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
|
17
nginx/nginx.conf
Normal file
17
nginx/nginx.conf
Normal 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;
|
||||
}
|
||||
|
||||
}
|
19
src/component/Modal/ModalLoader.js
Normal file
19
src/component/Modal/ModalLoader.js
Normal 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>
|
||||
});
|
|
@ -1,12 +1,11 @@
|
|||
import React, {useEffect} from "react";
|
||||
import React, {useEffect, useState} from "react";
|
||||
import {DesktopLayout} from "./DesktopLayout";
|
||||
import {useMediaQuery} from 'react-responsive';
|
||||
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 = () => {
|
||||
// TODO: add mobile layout
|
||||
const store = useStore();
|
||||
const mediaQuery = {
|
||||
isDesktop: useMediaQuery({minWidth: 1024}),
|
||||
|
@ -15,11 +14,23 @@ export const App = () => {
|
|||
isNotMobile: useMediaQuery({minWidth: 768}),
|
||||
};
|
||||
|
||||
const [modalLoading, setModalLoading] = useState(false);
|
||||
const [modalText, setModalText] = useState(undefined);
|
||||
const modalContextValue = {
|
||||
setLoading: setModalLoading,
|
||||
setText: setModalText,
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
store.ui.setMediaQuery(mediaQuery);
|
||||
});
|
||||
// const isMobileDevice = useMediaQuery({
|
||||
// query: "(min-device-width: 480px)",
|
||||
// });
|
||||
return <DesktopLayout/>;
|
||||
|
||||
return (
|
||||
<>
|
||||
<ModalLoader isOpen={modalLoading} text={modalText}/>
|
||||
<ModalLoaderContext.Provider value={modalContextValue}>
|
||||
<DesktopLayout/>
|
||||
</ModalLoaderContext.Provider>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -117,16 +117,16 @@ export const Membership = observer(() => {
|
|||
key: "name",
|
||||
render: (text, record) => record?.name ?? record?.username,
|
||||
},
|
||||
{
|
||||
title: "Username",
|
||||
dataIndex: "username",
|
||||
key: "username",
|
||||
},
|
||||
{
|
||||
title: "Role",
|
||||
dataIndex: "roleName",
|
||||
key: "role",
|
||||
},
|
||||
{
|
||||
title: "Saldo",
|
||||
dataIndex: ["coa","amount"],
|
||||
key: ["coa","amount"],
|
||||
},
|
||||
{
|
||||
title: "Status",
|
||||
dataIndex: "isActive",
|
||||
|
|
|
@ -1,33 +1,32 @@
|
|||
import React, {useEffect, useState} from "react";
|
||||
import {Button, Card, Col, Input, Row, Select, Tabs, Upload} from "antd";
|
||||
import React, {useContext, useEffect} from "react";
|
||||
import {Button, Card, Col, Input, Row, Upload} from "antd";
|
||||
import {FilterOutlined, PlusSquareOutlined, 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 {TabPane} = Tabs;
|
||||
const {Search} = Input;
|
||||
const {Option} = Select;
|
||||
|
||||
export const Product = observer(() => {
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const store = useStore();
|
||||
const modalLoader = useContext(ModalLoaderContext);
|
||||
|
||||
useEffect(() => {
|
||||
const init = async () => {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
modalLoader.setLoading(true);
|
||||
await Promise.allSettled([
|
||||
store.supplier.getData(),
|
||||
store.product.getDataCategories(),
|
||||
store.category.getData(),
|
||||
]);
|
||||
await store.product.getData();
|
||||
setIsLoading(false);
|
||||
modalLoader.setLoading(false);
|
||||
} catch (e) {
|
||||
setIsLoading(false);
|
||||
modalLoader.setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -23,10 +23,10 @@ export class Category {
|
|||
try {
|
||||
const response = await http.get(`/product/categories?page=${this.page}&pageSize=${this.pageSize}`);
|
||||
this.data = response.body.data.map((item, idx) => {
|
||||
item.key = idx;
|
||||
return item
|
||||
}) ?? []
|
||||
this.total_data = response.body.total_data ?? 0
|
||||
item.key = idx;
|
||||
return item
|
||||
}) ?? []
|
||||
this.total_data = response?.body?.count ?? 0
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
|
@ -55,7 +55,7 @@ export class Category {
|
|||
return item
|
||||
}) ?? []
|
||||
|
||||
this.total_dataCategories = response.body.total_data ?? 0
|
||||
this.total_dataCategories = response?.body?.count ?? 0
|
||||
if (this.dataCategories.length > 0) {
|
||||
this.filterCategory = this.dataCategories[0].id
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ export class Commission {
|
|||
return item
|
||||
}) ?? []
|
||||
|
||||
this.total_data = response.body.total_data ?? 0
|
||||
this.total_data = response?.body?.count ?? 0
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ export class Membership {
|
|||
return item
|
||||
}) ?? []
|
||||
|
||||
this.total_data = response.body.total_data ?? 0
|
||||
this.total_data = response?.body?.count ?? 0
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
|
@ -40,7 +40,7 @@ export class Membership {
|
|||
return item
|
||||
}) ?? []
|
||||
|
||||
this.total_data = response.body.total_data ?? 0
|
||||
this.total_data = response?.body?.count ?? 0
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { makeAutoObservable } from "mobx";
|
||||
import { http } from "../utils/http";
|
||||
import {makeAutoObservable} from "mobx";
|
||||
import {http} from "../utils/http";
|
||||
|
||||
export class Payback {
|
||||
page = 0;
|
||||
|
|
|
@ -36,7 +36,7 @@ export class Product {
|
|||
return item
|
||||
}) ?? []
|
||||
|
||||
this.total_data = response.body.total_data ?? 0
|
||||
this.total_data = response?.body?.count ?? 0
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { makeAutoObservable } from "mobx";
|
||||
import { http } from "../utils/http";
|
||||
import {makeAutoObservable} from "mobx";
|
||||
import {http} from "../utils/http";
|
||||
|
||||
export class Role {
|
||||
page = null;
|
||||
|
@ -20,7 +20,7 @@ export class Role {
|
|||
}&pageSize=${this.pageSize}`
|
||||
);
|
||||
this.data = response.body.data ?? [];
|
||||
this.total_data = response.body.total_data ?? 0;
|
||||
this.total_data = response?.body?.count ?? 0;
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ export class Subcategory {
|
|||
return item
|
||||
}) ?? []
|
||||
|
||||
this.total_data = response.body.total_data ?? 0
|
||||
this.total_data = response?.body?.count ?? 0
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
|
|
|
@ -35,7 +35,7 @@ export class Transaction {
|
|||
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.total_data ?? 0
|
||||
this.total_data = response?.body?.count ?? 0
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
|
@ -56,7 +56,7 @@ export class Transaction {
|
|||
const response = await http.get(`/product/categories?page=${this.pageCategories}&pageSize=${this.pageSizeCategories}`);
|
||||
|
||||
this.dataCategories = response.body.data ?? []
|
||||
this.total_dataCategories = response.body.total_data ?? 0
|
||||
this.total_dataCategories = response?.body?.count ?? 0
|
||||
if (this.dataCategories.length > 0) {
|
||||
this.filterCategory = this.dataCategories[0].id
|
||||
}
|
||||
|
@ -70,7 +70,7 @@ export class Transaction {
|
|||
const response = await http.get(`/transaction/history?page=${this.pageHistoryTransaction}`);
|
||||
|
||||
this.dataHistoryTransaction = response.body.data ?? []
|
||||
this.total_dataHistoryTransaction = response.body.total_data ?? 0
|
||||
this.total_dataHistoryTransaction = response?.body?.count ?? 0
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
|
|
3
src/utils/modal.js
Normal file
3
src/utils/modal.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
import React from "react";
|
||||
|
||||
export const ModalLoaderContext = React.createContext(null);
|
Loading…
Reference in New Issue
Block a user