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 { formatApiResponse } from '../utils/response';
|
||||||
import { oslogApi, apiGo } from './axiosClient';
|
import { oslogApi, apiGo } from './axiosClient';
|
||||||
|
|
||||||
@ -10,31 +12,26 @@ const request = async ({
|
|||||||
endpoint = '',
|
endpoint = '',
|
||||||
suffix = '',
|
suffix = '',
|
||||||
data = null,
|
data = null,
|
||||||
params = {},
|
|
||||||
headers = {},
|
|
||||||
customUrl = '',
|
customUrl = '',
|
||||||
axiosConfig = {},
|
axiosConfig = {}
|
||||||
}) => {
|
}) => {
|
||||||
const url = customUrl || `${endpoint}${suffix}`;
|
const url = customUrl || `${endpoint}${suffix}`;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const client = isApiGo ? apiGo : oslogApi;
|
const client = isApiGo ? apiGo : oslogApi;
|
||||||
|
|
||||||
const response = await client({
|
const response = await client({
|
||||||
method,
|
method,
|
||||||
url,
|
url,
|
||||||
data,
|
data,
|
||||||
params,
|
|
||||||
headers,
|
// highest priority
|
||||||
...axiosConfig,
|
...axiosConfig
|
||||||
});
|
});
|
||||||
|
|
||||||
return formatApiResponse(response);
|
return formatApiResponse(response);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const responseError = formatApiResponse(error);
|
return formatApiResponse(error);
|
||||||
|
|
||||||
//logError(`at endpoint: ${url} error: `, responseError);
|
|
||||||
|
|
||||||
return responseError;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -42,65 +39,86 @@ const request = async ({
|
|||||||
* CREATE NEW
|
* CREATE NEW
|
||||||
* POST /endpoint/new
|
* POST /endpoint/new
|
||||||
*/
|
*/
|
||||||
export const newRequest = (endpoint, data = null, config = {}) =>
|
export const newRequest = (
|
||||||
|
endpoint,
|
||||||
|
data = null,
|
||||||
|
axiosConfig = {}
|
||||||
|
) =>
|
||||||
request({
|
request({
|
||||||
method: 'post',
|
method: 'post',
|
||||||
endpoint,
|
endpoint,
|
||||||
suffix: '/new',
|
suffix: '/new',
|
||||||
data,
|
data,
|
||||||
...config,
|
axiosConfig
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ADD
|
* ADD
|
||||||
* POST /endpoint/add
|
* POST /endpoint/add
|
||||||
*/
|
*/
|
||||||
export const addRequest = (endpoint, data = null, config = {}) =>
|
export const addRequest = (
|
||||||
|
endpoint,
|
||||||
|
data = null,
|
||||||
|
axiosConfig = {}
|
||||||
|
) =>
|
||||||
request({
|
request({
|
||||||
method: 'post',
|
method: 'post',
|
||||||
endpoint,
|
endpoint,
|
||||||
suffix: '/add',
|
suffix: '/add',
|
||||||
data,
|
data,
|
||||||
...config,
|
axiosConfig
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GET BY ID
|
* GET BY ID
|
||||||
* GET /endpoint/:id
|
* GET /endpoint/:id
|
||||||
*/
|
*/
|
||||||
export const getByIdRequest = (id, endpoint, params = {}, config = {}) =>
|
export const getByIdRequest = (
|
||||||
|
id,
|
||||||
|
endpoint,
|
||||||
|
axiosConfig = {}
|
||||||
|
) =>
|
||||||
request({
|
request({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
endpoint,
|
endpoint,
|
||||||
suffix: `/${id}`,
|
suffix: `/${id}`,
|
||||||
params,
|
axiosConfig
|
||||||
...config,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* EDIT
|
* EDIT
|
||||||
* PUT /endpoint/edit/:id
|
* PUT /endpoint/edit/:id
|
||||||
*/
|
*/
|
||||||
export const editRequest = (id, endpoint, data = null, config = {}) =>
|
export const editRequest = (
|
||||||
|
id,
|
||||||
|
endpoint,
|
||||||
|
data = null,
|
||||||
|
axiosConfig = {}
|
||||||
|
) =>
|
||||||
request({
|
request({
|
||||||
method: 'put',
|
method: 'put',
|
||||||
endpoint,
|
endpoint,
|
||||||
suffix: `/edit/${id}`,
|
suffix: `/edit/${id}`,
|
||||||
data,
|
data,
|
||||||
...config,
|
axiosConfig
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DELETE
|
* DELETE
|
||||||
* DELETE /endpoint/delete/:id
|
* DELETE /endpoint/delete/:id
|
||||||
*/
|
*/
|
||||||
export const deleteRequest = (id, endpoint, data = null, config = {}) =>
|
export const deleteRequest = (
|
||||||
|
id,
|
||||||
|
endpoint,
|
||||||
|
data = null,
|
||||||
|
axiosConfig = {}
|
||||||
|
) =>
|
||||||
request({
|
request({
|
||||||
method: 'delete',
|
method: 'delete',
|
||||||
endpoint,
|
endpoint,
|
||||||
suffix: `/delete/${id}`,
|
suffix: `/delete/${id}`,
|
||||||
data,
|
data,
|
||||||
...config,
|
axiosConfig
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -110,8 +128,7 @@ export const deleteRequest = (id, endpoint, data = null, config = {}) =>
|
|||||||
export const searchRequest = (
|
export const searchRequest = (
|
||||||
endpoint,
|
endpoint,
|
||||||
data = null,
|
data = null,
|
||||||
params = {},
|
axiosConfig = {}
|
||||||
config = {},
|
|
||||||
) => {
|
) => {
|
||||||
const timezone = -new Date().getTimezoneOffset() / 60;
|
const timezone = -new Date().getTimezoneOffset() / 60;
|
||||||
|
|
||||||
@ -120,37 +137,38 @@ export const searchRequest = (
|
|||||||
endpoint,
|
endpoint,
|
||||||
suffix: '/search',
|
suffix: '/search',
|
||||||
data,
|
data,
|
||||||
params: {
|
|
||||||
tz: timezone,
|
axiosConfig: {
|
||||||
...params,
|
...axiosConfig,
|
||||||
},
|
|
||||||
...config,
|
params: {
|
||||||
|
tz: timezone,
|
||||||
|
...(axiosConfig.params || {})
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CUSTOM REQUEST
|
* CUSTOM REQUEST
|
||||||
* untuk endpoint bebas / beda sendiri
|
|
||||||
*/
|
*/
|
||||||
export const customRequest = ({
|
export const customRequest = ({
|
||||||
method = 'get',
|
method = 'get',
|
||||||
url = '',
|
url = '',
|
||||||
data = null,
|
data = null,
|
||||||
params = {},
|
isApiGo = false,
|
||||||
headers = {},
|
axiosConfig = {}
|
||||||
axiosConfig = {},
|
|
||||||
}) =>
|
}) =>
|
||||||
request({
|
request({
|
||||||
method,
|
method,
|
||||||
customUrl: url,
|
customUrl: url,
|
||||||
data,
|
data,
|
||||||
params,
|
isApiGo,
|
||||||
headers,
|
axiosConfig
|
||||||
axiosConfig,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Optional Export Object
|
* EXPORT OBJECT
|
||||||
*/
|
*/
|
||||||
export const apiRequest = {
|
export const apiRequest = {
|
||||||
new: newRequest,
|
new: newRequest,
|
||||||
@ -159,5 +177,5 @@ export const apiRequest = {
|
|||||||
edit: editRequest,
|
edit: editRequest,
|
||||||
delete: deleteRequest,
|
delete: deleteRequest,
|
||||||
search: searchRequest,
|
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 ClearIcon from "@mui/icons-material/Clear";
|
||||||
import FilterTableBuilder from "../utils/filterTableBuilder";
|
import FilterTableBuilder from "../utils/filterTableBuilder";
|
||||||
import { apiRequest } from "../api/request";
|
import { apiRequest } from "../api/request";
|
||||||
|
import { useApiHeaders } from "./ApiContext";
|
||||||
|
|
||||||
export default function ListVirtual({
|
export default function ListVirtual({
|
||||||
id,
|
id,
|
||||||
@ -33,8 +34,8 @@ export default function ListVirtual({
|
|||||||
helperText,
|
helperText,
|
||||||
filter,
|
filter,
|
||||||
isApiGo = false,
|
isApiGo = false,
|
||||||
headersRequest,
|
|
||||||
}) {
|
}) {
|
||||||
|
const headersRequest = useApiHeaders();
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const [search, setSearch] = useState("");
|
const [search, setSearch] = useState("");
|
||||||
const [rows, setRows] = useState([]);
|
const [rows, setRows] = useState([]);
|
||||||
@ -65,18 +66,17 @@ export default function ListVirtual({
|
|||||||
if (keyword) payload.like(name, keyword);
|
if (keyword) payload.like(name, keyword);
|
||||||
|
|
||||||
if (filter && filter.length > 0) {
|
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 {
|
try {
|
||||||
const res = await apiRequest.search(
|
const res = await apiRequest.search(
|
||||||
url,
|
url,
|
||||||
payload.build(),
|
payload.build(),
|
||||||
{},
|
|
||||||
{
|
{
|
||||||
isApiGo,
|
|
||||||
headers: headersRequest
|
headers: headersRequest
|
||||||
}
|
},
|
||||||
|
isApiGo,
|
||||||
);
|
);
|
||||||
const incomingData = res.data || [];
|
const incomingData = res.data || [];
|
||||||
|
|
||||||
@ -125,14 +125,13 @@ export default function ListVirtual({
|
|||||||
.equal("id", id);
|
.equal("id", id);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await apiRequest.search(
|
const res = await apiRequest.search(
|
||||||
url,
|
url,
|
||||||
payload.build(),
|
payload.build(),
|
||||||
{},
|
|
||||||
{
|
{
|
||||||
isApiGo,
|
|
||||||
headers: headersRequest
|
headers: headersRequest
|
||||||
}
|
},
|
||||||
|
isApiGo,
|
||||||
);
|
);
|
||||||
|
|
||||||
const data = res.data?.[0];
|
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