feat: ApiContext
This commit is contained in:
parent
25c9bc21b3
commit
1797af7ba9
8444
dist/index.js
vendored
8444
dist/index.js
vendored
File diff suppressed because it is too large
Load Diff
27
src/App.jsx
Normal file
27
src/App.jsx
Normal file
@ -0,0 +1,27 @@
|
||||
import ListVirtual from "./components/ListVirtual";
|
||||
import { ApiProvider } from "./components/ApiContext";
|
||||
import { API_TOKEN } from "./config/constants";
|
||||
|
||||
const Testing = () => {
|
||||
const token = API_TOKEN;
|
||||
return (
|
||||
<div style={{ padding: 8, marginTop: 12 }}>
|
||||
<ApiProvider
|
||||
headers={{
|
||||
Authorization: `Bearer ${token}`,
|
||||
}}
|
||||
>
|
||||
<ListVirtual
|
||||
id="company-select"
|
||||
label="Company"
|
||||
name="name"
|
||||
url="/company"
|
||||
defaultValue={"442"}
|
||||
size="small"
|
||||
/>
|
||||
</ApiProvider>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Testing;
|
||||
@ -1,3 +1,5 @@
|
||||
// src/api/request.js
|
||||
|
||||
import { formatApiResponse } from '../utils/response';
|
||||
import { oslogApi, apiGo } from './axiosClient';
|
||||
|
||||
@ -10,31 +12,26 @@ const request = async ({
|
||||
endpoint = '',
|
||||
suffix = '',
|
||||
data = null,
|
||||
params = {},
|
||||
headers = {},
|
||||
customUrl = '',
|
||||
axiosConfig = {},
|
||||
axiosConfig = {}
|
||||
}) => {
|
||||
const url = customUrl || `${endpoint}${suffix}`;
|
||||
|
||||
try {
|
||||
const client = isApiGo ? apiGo : oslogApi;
|
||||
|
||||
const response = await client({
|
||||
method,
|
||||
url,
|
||||
data,
|
||||
params,
|
||||
headers,
|
||||
...axiosConfig,
|
||||
|
||||
// highest priority
|
||||
...axiosConfig
|
||||
});
|
||||
|
||||
return formatApiResponse(response);
|
||||
} catch (error) {
|
||||
const responseError = formatApiResponse(error);
|
||||
|
||||
//logError(`at endpoint: ${url} error: `, responseError);
|
||||
|
||||
return responseError;
|
||||
return formatApiResponse(error);
|
||||
}
|
||||
};
|
||||
|
||||
@ -42,65 +39,86 @@ const request = async ({
|
||||
* CREATE NEW
|
||||
* POST /endpoint/new
|
||||
*/
|
||||
export const newRequest = (endpoint, data = null, config = {}) =>
|
||||
export const newRequest = (
|
||||
endpoint,
|
||||
data = null,
|
||||
axiosConfig = {}
|
||||
) =>
|
||||
request({
|
||||
method: 'post',
|
||||
endpoint,
|
||||
suffix: '/new',
|
||||
data,
|
||||
...config,
|
||||
axiosConfig
|
||||
});
|
||||
|
||||
/**
|
||||
* ADD
|
||||
* POST /endpoint/add
|
||||
*/
|
||||
export const addRequest = (endpoint, data = null, config = {}) =>
|
||||
export const addRequest = (
|
||||
endpoint,
|
||||
data = null,
|
||||
axiosConfig = {}
|
||||
) =>
|
||||
request({
|
||||
method: 'post',
|
||||
endpoint,
|
||||
suffix: '/add',
|
||||
data,
|
||||
...config,
|
||||
axiosConfig
|
||||
});
|
||||
|
||||
/**
|
||||
* GET BY ID
|
||||
* GET /endpoint/:id
|
||||
*/
|
||||
export const getByIdRequest = (id, endpoint, params = {}, config = {}) =>
|
||||
export const getByIdRequest = (
|
||||
id,
|
||||
endpoint,
|
||||
axiosConfig = {}
|
||||
) =>
|
||||
request({
|
||||
method: 'get',
|
||||
endpoint,
|
||||
suffix: `/${id}`,
|
||||
params,
|
||||
...config,
|
||||
axiosConfig
|
||||
});
|
||||
|
||||
/**
|
||||
* EDIT
|
||||
* PUT /endpoint/edit/:id
|
||||
*/
|
||||
export const editRequest = (id, endpoint, data = null, config = {}) =>
|
||||
export const editRequest = (
|
||||
id,
|
||||
endpoint,
|
||||
data = null,
|
||||
axiosConfig = {}
|
||||
) =>
|
||||
request({
|
||||
method: 'put',
|
||||
endpoint,
|
||||
suffix: `/edit/${id}`,
|
||||
data,
|
||||
...config,
|
||||
axiosConfig
|
||||
});
|
||||
|
||||
/**
|
||||
* DELETE
|
||||
* DELETE /endpoint/delete/:id
|
||||
*/
|
||||
export const deleteRequest = (id, endpoint, data = null, config = {}) =>
|
||||
export const deleteRequest = (
|
||||
id,
|
||||
endpoint,
|
||||
data = null,
|
||||
axiosConfig = {}
|
||||
) =>
|
||||
request({
|
||||
method: 'delete',
|
||||
endpoint,
|
||||
suffix: `/delete/${id}`,
|
||||
data,
|
||||
...config,
|
||||
axiosConfig
|
||||
});
|
||||
|
||||
/**
|
||||
@ -110,8 +128,7 @@ export const deleteRequest = (id, endpoint, data = null, config = {}) =>
|
||||
export const searchRequest = (
|
||||
endpoint,
|
||||
data = null,
|
||||
params = {},
|
||||
config = {},
|
||||
axiosConfig = {}
|
||||
) => {
|
||||
const timezone = -new Date().getTimezoneOffset() / 60;
|
||||
|
||||
@ -120,37 +137,38 @@ export const searchRequest = (
|
||||
endpoint,
|
||||
suffix: '/search',
|
||||
data,
|
||||
params: {
|
||||
tz: timezone,
|
||||
...params,
|
||||
},
|
||||
...config,
|
||||
|
||||
axiosConfig: {
|
||||
...axiosConfig,
|
||||
|
||||
params: {
|
||||
tz: timezone,
|
||||
...(axiosConfig.params || {})
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* CUSTOM REQUEST
|
||||
* untuk endpoint bebas / beda sendiri
|
||||
*/
|
||||
export const customRequest = ({
|
||||
method = 'get',
|
||||
url = '',
|
||||
data = null,
|
||||
params = {},
|
||||
headers = {},
|
||||
axiosConfig = {},
|
||||
isApiGo = false,
|
||||
axiosConfig = {}
|
||||
}) =>
|
||||
request({
|
||||
method,
|
||||
customUrl: url,
|
||||
data,
|
||||
params,
|
||||
headers,
|
||||
axiosConfig,
|
||||
isApiGo,
|
||||
axiosConfig
|
||||
});
|
||||
|
||||
/**
|
||||
* Optional Export Object
|
||||
* EXPORT OBJECT
|
||||
*/
|
||||
export const apiRequest = {
|
||||
new: newRequest,
|
||||
@ -159,5 +177,5 @@ export const apiRequest = {
|
||||
edit: editRequest,
|
||||
delete: deleteRequest,
|
||||
search: searchRequest,
|
||||
custom: customRequest,
|
||||
custom: customRequest
|
||||
};
|
||||
39
src/components/ApiContext.js
Normal file
39
src/components/ApiContext.js
Normal file
@ -0,0 +1,39 @@
|
||||
import React, { createContext, useContext } from "react";
|
||||
|
||||
// Sentinel value to reliably detect if the hook is called outside the provider
|
||||
const sentinel = {};
|
||||
|
||||
/**
|
||||
* ApiContext created using the React Context API.
|
||||
* Defaults to a sentinel value to detect out-of-provider usage.
|
||||
*/
|
||||
export const ApiContext = createContext(sentinel);
|
||||
|
||||
/**
|
||||
* ApiProvider component that accepts a `headers` prop and provides it through context.
|
||||
*
|
||||
* @param {Object} props
|
||||
* @param {Object} props.headers - The headers to provide to API requests
|
||||
* @param {React.ReactNode} props.children - Child components
|
||||
*/
|
||||
export function ApiProvider({ headers, children }) {
|
||||
return React.createElement(
|
||||
ApiContext.Provider,
|
||||
{ value: headers },
|
||||
children
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom hook `useApiHeaders()` that returns the current headers from context.
|
||||
* Throws a clear error if called outside of `ApiProvider`.
|
||||
*
|
||||
* @returns {Object} The current headers from context
|
||||
*/
|
||||
export function useApiHeaders() {
|
||||
const context = useContext(ApiContext);
|
||||
if (context === sentinel) {
|
||||
throw new Error("useApiHeaders must be used within an ApiProvider");
|
||||
}
|
||||
return context;
|
||||
}
|
||||
@ -16,6 +16,7 @@ import {
|
||||
import ClearIcon from "@mui/icons-material/Clear";
|
||||
import FilterTableBuilder from "../utils/filterTableBuilder";
|
||||
import { apiRequest } from "../api/request";
|
||||
import { useApiHeaders } from "./ApiContext";
|
||||
|
||||
export default function ListVirtual({
|
||||
id,
|
||||
@ -33,8 +34,8 @@ export default function ListVirtual({
|
||||
helperText,
|
||||
filter,
|
||||
isApiGo = false,
|
||||
headersRequest,
|
||||
}) {
|
||||
const headersRequest = useApiHeaders();
|
||||
const [open, setOpen] = useState(false);
|
||||
const [search, setSearch] = useState("");
|
||||
const [rows, setRows] = useState([]);
|
||||
@ -65,18 +66,17 @@ export default function ListVirtual({
|
||||
if (keyword) payload.like(name, keyword);
|
||||
|
||||
if (filter && filter.length > 0) {
|
||||
filter.forEach(f => payload.where(f.logic_operator || "=", f.name, f.value, f.operator || "AND", f.value1 || null, f.table_name || null));
|
||||
filter.forEach(f => payload.where(f.logic_operator || "=", f.name, f.value, f.operator || "AND", f.value1 || null, f.table_name || null));
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await apiRequest.search(
|
||||
url,
|
||||
payload.build(),
|
||||
{},
|
||||
{
|
||||
isApiGo,
|
||||
headers: headersRequest
|
||||
}
|
||||
},
|
||||
isApiGo,
|
||||
);
|
||||
const incomingData = res.data || [];
|
||||
|
||||
@ -125,14 +125,13 @@ export default function ListVirtual({
|
||||
.equal("id", id);
|
||||
|
||||
try {
|
||||
const res = await apiRequest.search(
|
||||
url,
|
||||
const res = await apiRequest.search(
|
||||
url,
|
||||
payload.build(),
|
||||
{},
|
||||
{
|
||||
isApiGo,
|
||||
headers: headersRequest
|
||||
}
|
||||
},
|
||||
isApiGo,
|
||||
);
|
||||
|
||||
const data = res.data?.[0];
|
||||
|
||||
2
src/components/index.js
Normal file
2
src/components/index.js
Normal file
@ -0,0 +1,2 @@
|
||||
export { default as ListVirtual } from "./ListVirtual";
|
||||
export * from "./ApiContext";
|
||||
@ -1 +1,3 @@
|
||||
export { default as ListVirtual } from './components/ListVirtual.jsx';
|
||||
export * from './api/request';
|
||||
export * from './components';
|
||||
//export * from './utils';
|
||||
|
||||
77
src/main.jsx
Normal file
77
src/main.jsx
Normal file
@ -0,0 +1,77 @@
|
||||
// src/main.jsx
|
||||
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom/client';
|
||||
|
||||
import { ThemeProvider, createTheme } from '@mui/material/styles';
|
||||
import { CssBaseline } from '@mui/material';
|
||||
|
||||
import App from './App'
|
||||
|
||||
const theme = createTheme({
|
||||
palette: {
|
||||
mode: 'light',
|
||||
|
||||
primary: {
|
||||
main: '#2563eb'
|
||||
},
|
||||
|
||||
background: {
|
||||
default: '#f8fafc'
|
||||
}
|
||||
},
|
||||
|
||||
shape: {
|
||||
borderRadius: 12
|
||||
},
|
||||
|
||||
typography: {
|
||||
fontFamily:
|
||||
'"Inter", "Roboto", "Helvetica", "Arial", sans-serif',
|
||||
|
||||
h5: {
|
||||
fontWeight: 700
|
||||
},
|
||||
|
||||
button: {
|
||||
textTransform: 'none',
|
||||
fontWeight: 600
|
||||
}
|
||||
},
|
||||
|
||||
components: {
|
||||
MuiPaper: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
borderRadius: 16
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
MuiButton: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
borderRadius: 10,
|
||||
paddingInline: 16
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
MuiOutlinedInput: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
borderRadius: 12
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root')).render(
|
||||
<React.StrictMode>
|
||||
<ThemeProvider theme={theme}>
|
||||
<CssBaseline />
|
||||
<App />
|
||||
</ThemeProvider>
|
||||
</React.StrictMode>
|
||||
);
|
||||
Loading…
x
Reference in New Issue
Block a user