Commit 4cff47ae authored by ardiansyah's avatar ardiansyah

Merge branch 'ENV-DEV' into 'ENV-DEPLOYMENT'

Env dev

See merge request !2386
parents b2c9629a 25e3c70f
......@@ -87,6 +87,16 @@ const create = (type = "") => {
// console.tron.log(url)
})
api.addResponseTransform(response => {
const msg = response.data?.message || ""
if (msg.includes("Someone Logged In") || msg.includes("Token Expired")) {
localStorage.removeItem(Constant.TOKEN)
window.location.reload()
return
}
})
// ------
// STEP 2
// ------
......@@ -155,6 +165,7 @@ const create = (type = "") => {
const getApprovedByAM = () => api.get('approval_matrix/get_all_approver')
const getTypeAM = () => api.get('approval_type/get_all_approval_type')
const getOperatorAM = () => api.get('operator_type/get_all_operator_type')
const getMasterReportType = () => api.get('masterreporttype/get_all_master_report_type')
const getDetailAM = (id) => api.get(`approval_matrix/get_approval_matrix_by_id/${id}`)
const searchAM = (body) => api.post('/approval_matrix/search_approval_matrix', body)
const createAM = (body) => api.post('/approval_matrix/create_approval_matrix', body)
......@@ -623,6 +634,7 @@ const create = (type = "") => {
getApprovedByAM,
getTypeAM,
getOperatorAM,
getMasterReportType,
getDetailAM,
searchAM,
createAM,
......
......@@ -93,7 +93,7 @@ class ReportHistorical extends Component {
}))
}
showAlert = (message, severity = 'success') => {
showAlert = (message, severity = Constant.ALERT_SEVIRITY.SUCCESS) => {
this.setState({
showAlert: true,
alertMessage: message,
......@@ -107,8 +107,12 @@ class ReportHistorical extends Component {
handleDownload = async () => {
try {
this.setLoading(true)
const { data } = this.state
if (!data?.company_id) {
this.showAlert('Data is not complete !', Constant.ALERT_SEVIRITY.WARNING);
return
}
this.setLoading(true)
const payload = {
report_id: data.report_id?.id,
company_id: data.company_id?.map(c => c.id).join(','),
......@@ -125,12 +129,12 @@ class ReportHistorical extends Component {
downloadFileBlob(fileName, blob)
}
this.showAlert('Download Berhasil', 'success');
this.showAlert('Download Berhasil');
})
} catch (error) {
// Show error alert
this.showAlert(`Gagal menyimpan: ${error.message}`, 'error');
this.showAlert(`Gagal menyimpan: ${error.message}`, Constant.ALERT_SEVIRITY.ERROR);
}
};
......
......@@ -27,21 +27,29 @@ const AutocompleteField = ({
multiple = false,
showCheckbox = false,
isLoading = false,
minSizeBox = false,
...props
}) => {
const defaultRenderInput = (params) => (
<TextField
{...params}
label={label}
margin={margin}
style={{ marginTop: 7 }}
margin={minSizeBox ? 'none' : margin}
style={minSizeBox ? {} : { marginTop: 7 }}
disabled={disabled}
required={required}
error={error}
helperText={helperText}
fullWidth
InputLabelProps={minSizeBox ? {
style: {
fontSize: 11,
color: '#7e8085'
}
} : {}}
InputProps={{
...params.InputProps,
style: minSizeBox ? { fontSize: 11 } : {},
endAdornment: (
<>
{isLoading ? (
......@@ -90,7 +98,7 @@ const AutocompleteField = ({
onChange={onChange}
value={multiple ? (value || []) : (value || null)}
id={id}
disableClearable={!multiple && disableClearable}
disableClearable={disableClearable}
disableCloseOnSelect={multiple}
style={style}
disabled={disabled}
......
......@@ -29,9 +29,7 @@ class ContentContainer extends Component {
{isLoading && (
<OverlayLoader isLoading={isLoading} {...loaderProps} />
)}
<Header
title={title}
/>
{title && <Header title={title} />}
{children}
</div>
);
......
......@@ -4,96 +4,88 @@ import AutocompleteField from '../AutocompleteField';
import api from '../../api';
class DDLCompany extends Component {
constructor(props) {
super(props);
this.state = {
selectedValue: props.multiple
? (props.value || [])
: (props.value || null),
state = {
companies: [],
selectedValue: this.props.multiple ? [] : null,
isLoading: false,
};
}
componentDidMount() {
this.getCompanyActive();
}
componentDidUpdate(prevProps, prevState) {
// Value dikontrol parent
if (prevProps.value !== this.props.value) {
this.syncValueWithCompanies(this.props.value);
}
// Companies berubah (hasil API)
if (prevState.companies !== this.state.companies) {
// 🔁 value dari parent berubah ATAU companies selesai load
if (
prevProps.value !== this.props.value ||
prevState.companies !== this.state.companies
) {
this.syncValueWithCompanies(this.props.value);
}
}
setLoading = (isLoading) => {
this.setState({ isLoading });
}
};
getCompanyActive = async () => {
try {
this.setLoading(true);
const response = await api.create().getPerusahaanActive();
const data = response?.data?.data || [];
const res = await api.create().getPerusahaanActive();
const data = res?.data?.data || [];
const companies = data.map(item => ({
id: String(item.company_id),
name: item.company_name,
}));
this.setState({ companies });
} catch (err) {
console.error('Failed to load companies', err);
this.setState({ companies }, () => {
// ⭐ optional auto select index 0
if (
this.props.autoSelectFirst &&
!this.props.value &&
companies.length > 0
) {
this.handleChange(null, this.props.multiple ? [companies[0]] : companies[0]);
}
});
} catch (e) {
console.error(e);
this.setState({ companies: [] });
} finally {
this.setLoading(false);
}
};
// Sinkron value → referensi object dari companies
// 🔑 SINKRON VALUE ↔ OPTIONS (INI KUNCI UTAMA)
syncValueWithCompanies = (value) => {
const { companies } = this.state;
const { multiple } = this.props;
if (!value || companies.length === 0) {
this.setState({
selectedValue: multiple ? [] : null,
});
return;
}
if (!value || companies.length === 0) return;
if (multiple) {
const synced = value
.map(v =>
companies.find(c => String(c.id) === String(v.id))
)
.map(v => companies.find(c => c.id === String(v.id)))
.filter(Boolean);
this.setState({ selectedValue: synced });
} else {
const matched = companies.find(
c => String(c.id) === String(value.id)
);
const matched = companies.find(c => c.id === String(value.id));
this.setState({ selectedValue: matched || null });
}
};
// 🔁 SELALU KIRIM BALIK KE PARENT
handleChange = (event, newValue) => {
const { onChange, onCompanyChange, name, multiple } = this.props;
this.setState({ selectedValue: newValue });
// Standard handler
if (onChange) {
onChange(event, newValue, name);
}
// Backward compatibility
if (onCompanyChange) {
if (multiple) {
onCompanyChange(newValue.map(v => v.id));
......@@ -103,29 +95,20 @@ class DDLCompany extends Component {
}
};
getSelectedCompanyValue = () => {
const { selectedValue } = this.state;
const { multiple } = this.props;
return multiple
? selectedValue.map(v => v.id)
: selectedValue?.id || null;
};
render() {
const {
label = 'Company',
placeholder = 'Select Company',
disabled = false,
required = false,
error = false,
label,
placeholder,
disabled,
required,
error,
helperText,
style = { width: 250 },
margin = 'normal',
style,
margin,
multiple,
} = this.props;
const { selectedValue, companies, isLoading } = this.state;
const { companies, selectedValue, isLoading } = this.state;
return (
<AutocompleteField
......@@ -142,17 +125,14 @@ class DDLCompany extends Component {
margin={margin}
multiple={multiple}
showCheckbox={multiple}
isLoading={isLoading}
loading={isLoading}
/>
);
}
}
DDLCompany.propTypes = {
value: PropTypes.oneOfType([
PropTypes.object,
PropTypes.array,
]),
value: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
onChange: PropTypes.func,
onCompanyChange: PropTypes.func,
name: PropTypes.string,
......@@ -168,13 +148,16 @@ DDLCompany.propTypes = {
helperText: PropTypes.string,
multiple: PropTypes.bool,
autoSelectFirst: PropTypes.bool,
};
DDLCompany.defaultProps = {
label: 'Company',
placeholder: 'Select Company',
style: { width: 250 },
margin: 'normal',
multiple: false,
autoSelectFirst: false,
};
export default DDLCompany;
......@@ -27,3 +27,57 @@ export function downloadFileBlob(fileName, blobData) {
a.remove()
window.URL.revokeObjectURL(url)
}
/**
* @param {Object} location - Objek location dari props (this.props.location)
* @param {string} paramName - Nama key yang ingin diambil (misal: 'month')
* @param {any} defaultValue - Nilai kembalian jika data null/undefined
*/
export const getStateParam = (location, paramName = null, defaultValue = null) => {
if (!paramName) {
return location?.state ?? defaultValue;
}
return location?.state?.[paramName] ?? defaultValue;
};
export const formatters = {
// Untuk format Infinity
infinity: {
month: (val) => String(val === undefined || val === 'Infinity' || val === '-Infinity'
? "0.0"
: Number(val).toFixed(2)),
totalCY: (val) => String(val === undefined || val === 'Infinity' || val === '-Infinity'
? "0.0"
: Number(val).toFixed(2)),
totalOther: (val) => String(val !== '' && val !== 'Infinity' && val !== '-Infinity'
? Number(val).toFixed(2)
: val)
},
// Untuk format .value property
value: {
month: (val) => String(val?.value === undefined ? val : Number(val.value).toFixed(1)),
total: (val) => String(val !== '' ? Number(val).toFixed(1) : val)
}
};
// ===== HELPER FUNCTIONS =====
export const createMonthData = (item, startIdx, formatFn) => {
const monthNames = ['january', 'february', 'march', 'april', 'may', 'june',
'july', 'august', 'september', 'october', 'november', 'december'];
const months = {};
monthNames.forEach((month, idx) => {
months[month] = formatFn(item[startIdx + idx]);
});
return months;
};
export const convertSelect = (id, name) => {
const obj = id ? {
id,
name,
} : null
return obj
};
\ No newline at end of file
import api from "../api";
import Constant from "../library/Constant";
const handleResponse = (response) => {
// 1. Handling Sukses
if (response.ok && response.data?.status === "success") {
return response.data.data;
}
// 2. Ambil pesan asli
const serverMessage = response.data?.message || response.data?.error || response.problem || "Gagal";
// 3. Masking bahasa teknis backend
let finalMessage = serverMessage;
if (typeof serverMessage === 'string' && (
serverMessage.includes("java.lang") ||
serverMessage.includes("FormatException") ||
serverMessage.includes("sql")
)) {
finalMessage = "Terjadi kesalahan format data pada sistem.";
}
const error = new Error(finalMessage);
error.tipe = (response.status >= 400 && response.status < 500) ? 'warning' : 'error';
error.isApiError = true;
error.originalMessage = serverMessage;
throw error;
};
/**
* Wrapper agar komponen bisa memilih mau pakai data saja atau errornya juga.
* Menghilangkan kebutuhan try-catch di komponen.
*/
const wrapService = (promise) => {
return promise
.then(res => {
return { data: handleResponse(res), error: null };
})
.catch((err) => {
console.error("API_LOG:", err.originalMessage || err.message);
return {
data: null,
error: { message: err.message, tipe: err.tipe }
};
});
};
// --- EXPORTED SERVICES ---
export const fetchMenuPermission = (menuName) => wrapService(api.create().getPermission({ menu: menuName }));
export const fetchDetailRole = (roleId) => wrapService(api.create().getDetailRole(roleId));
export const fetchDetailUser = () => {
const userId = localStorage.getItem(Constant.USER);
return wrapService(api.create().getDetailUser(userId));
};
export const fetchApprover = () => wrapService(api.create().checkApprover());
export const fetchLastPeriod = (companyId) => wrapService(api.create().getLastPeriod(companyId));
export const fetchRevision = (payload) => wrapService(api.create().getRevision(payload));
export const fetchSubmission = (payload) => wrapService(api.create().getSubmission(payload));
export const fetchListApprover = (report, monthlyReportId) => {
return wrapService(api.create().getListApprover(report, monthlyReportId));
}
export const fetchDetailReportCF = (payload) => wrapService(api.create().getDetailReportCF(payload));
export const fetchPLID = (payload) => wrapService(api.create().getPLID(payload));
export const fetchHierarkiCreateReportPLMB = (payload) => wrapService(api.create().getHierarkiCreateReportPLMB(payload));
export const fetchFRID = (payload) => wrapService(api.create().getFRID(payload));
export const fetchDownloadFile = (payload) => wrapService(api.create().createDownloadFile(payload));
export const fetchZipReport = (downloadedFileReportId) => wrapService(api.create().createZipReport(downloadedFileReportId));
export const fetchMasterReportType = () => wrapService(api.create().getMasterReportType());
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment