632 lines
14 KiB
Markdown
632 lines
14 KiB
Markdown
|
||
# uikit
|
||
|
||
کتابخانهی مشترک React برای استفاده بین پروژههای BI.
|
||
|
||
این پکیج به چند entry جدا تقسیم شده تا پروژهها فقط همان بخشی را import کنند که لازم دارند و مجبور نباشند برای یک helper ساده، dependencyهای سنگین مثل Chakra، Keycloak یا React Query نصب کنند.
|
||
|
||
---
|
||
|
||
## نصب
|
||
|
||
```bash
|
||
yarn add git+https://git.d.aiengines.ir/bi/uikit.git
|
||
```
|
||
|
||
یا اگر branch/tag خاصی لازم داری:
|
||
|
||
```bash
|
||
yarn add git+https://git.d.aiengines.ir/bi/uikit.git#main
|
||
```
|
||
|
||
---
|
||
|
||
## آپدیت
|
||
|
||
```bash
|
||
yarn upgrade uikit
|
||
```
|
||
|
||
اگر از git dependency استفاده میکنی و نسخه جدید نگرفتی، lockfile را هم بررسی کن:
|
||
|
||
```bash
|
||
yarn remove uikit
|
||
yarn add git+https://git.d.aiengines.ir/bi/uikit.git
|
||
```
|
||
|
||
---
|
||
|
||
# ساختار importها
|
||
|
||
| مسیر | کاربرد | dependency اضافی |
|
||
| ------------------ | ------------------------------------- | --------------------------------------- |
|
||
| `uikit` | خروجی سبک، فعلاً فقط utils | ندارد |
|
||
| `uikit/utils` | helperهای ساده مثل تبدیل عدد | ندارد |
|
||
| `uikit/pagination` | کامپوننتهای pagination | Chakra + React + react-icons |
|
||
| `uikit/layout` | کامپوننتهای layout مثل Header | Chakra + React |
|
||
| `uikit/core` | API، BiProvider، Keycloak، Permission | Chakra + React Query + Axios + Keycloak |
|
||
| `uikit/table` | DataTable | Chakra + TanStack Table + Pagination |
|
||
|
||
---
|
||
|
||
# استفاده از utils
|
||
|
||
برای helperهای ساده نیازی به نصب هیچ dependency اضافهای نیست.
|
||
|
||
```js
|
||
import { toFaDigits, toEnDigits, toFaNumber } from "uikit/utils";
|
||
|
||
toFaDigits(123456); // ۱۲۳۴۵۶
|
||
toEnDigits("۱۲۳۴۵۶"); // 123456
|
||
toFaNumber(123456); // عدد فارسیشده
|
||
```
|
||
|
||
همچنین چون root package فقط utils را export میکند، این هم قابل استفاده است:
|
||
|
||
```js
|
||
import { toFaDigits } from "uikit";
|
||
```
|
||
|
||
---
|
||
|
||
# استفاده از pagination
|
||
|
||
اگر از pagination استفاده میکنی، این dependencyها باید در پروژه نصب باشند:
|
||
|
||
```bash
|
||
yarn add @chakra-ui/react @emotion/react @emotion/styled framer-motion react-icons
|
||
```
|
||
|
||
استفاده:
|
||
|
||
```jsx
|
||
import { Pagination, LightPagination, SimplePagination } from "uikit/pagination";
|
||
|
||
function Page() {
|
||
return (
|
||
<Pagination
|
||
currentPage={1}
|
||
totalCount={100}
|
||
totalPages={5}
|
||
onPageChange={(page) => console.log(page)}
|
||
/>
|
||
);
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
# استفاده از Header
|
||
|
||
`Header` داخل مسیر `uikit/layout` قرار دارد.
|
||
|
||
dependencyهای لازم:
|
||
|
||
```bash
|
||
yarn add @chakra-ui/react @emotion/react @emotion/styled framer-motion
|
||
```
|
||
|
||
استفاده:
|
||
|
||
```jsx
|
||
import { Header } from "uikit/layout";
|
||
|
||
function AppHeader() {
|
||
return (
|
||
<Header>
|
||
<Header.BrandSection>
|
||
<Header.Logo src="/logo.png" />
|
||
|
||
<Header.Brand>
|
||
<Header.Title>عنوان سامانه</Header.Title>
|
||
<Header.Description>توضیح کوتاه سامانه</Header.Description>
|
||
</Header.Brand>
|
||
</Header.BrandSection>
|
||
|
||
<Header.Bi />
|
||
</Header>
|
||
);
|
||
}
|
||
```
|
||
|
||
اجزای موجود:
|
||
|
||
```jsx
|
||
<Header />
|
||
<Header.BrandSection />
|
||
<Header.Logo />
|
||
<Header.Brand />
|
||
<Header.Title />
|
||
<Header.Description />
|
||
<Header.Bi />
|
||
```
|
||
|
||
---
|
||
|
||
# استفاده از core
|
||
|
||
بخش core شامل این موارد است:
|
||
|
||
* API config
|
||
* `createApi`
|
||
* `BiProvider`
|
||
* Keycloak
|
||
* Permission
|
||
* React Query provider
|
||
* Chakra provider
|
||
|
||
برای استفاده از core این dependencyها باید در پروژه نصب باشند:
|
||
|
||
```bash
|
||
yarn add @chakra-ui/react @emotion/react @emotion/styled framer-motion @tanstack/react-query axios keycloak-js
|
||
```
|
||
|
||
---
|
||
|
||
## استفاده از BiProvider
|
||
|
||
```jsx
|
||
import { BiProvider } from "uikit/core";
|
||
|
||
export default function App() {
|
||
return (
|
||
<BiProvider
|
||
apiBaseUrl="https://api.example.com"
|
||
keycloakClientId="front-client"
|
||
permissionClientId="permission-client"
|
||
>
|
||
<YourApp />
|
||
</BiProvider>
|
||
);
|
||
}
|
||
```
|
||
|
||
`BiProvider` به صورت داخلی این کارها را انجام میدهد:
|
||
|
||
* تنظیم base url برای API
|
||
* راهاندازی Keycloak در صورت فعال بودن
|
||
* گرفتن permissionها در صورت فعال بودن
|
||
* اضافه کردن `ChakraProvider`
|
||
* اضافه کردن `QueryClientProvider`
|
||
* اضافه کردن فونت و theme داخلی
|
||
* فعال کردن اختیاری `UpdateChecker`
|
||
|
||
---
|
||
|
||
## props مربوط به BiProvider
|
||
|
||
| Prop | توضیح |
|
||
| -------------------- | ------------------------------------------------------ |
|
||
| `apiBaseUrl` | آدرس اصلی API |
|
||
| `keycloakClientId` | client id مربوط به Keycloak |
|
||
| `permissionClientId` | client id مربوط به Permission |
|
||
| `loading` | کامپوننت loading هنگام init شدن Keycloak یا Permission |
|
||
| `updateChecker` | فعال یا غیرفعال کردن بررسی آپدیت |
|
||
| `updateCheckerProps` | props مربوط به UpdateChecker |
|
||
| `keycloakUrl` | آدرس Keycloak |
|
||
| `keycloakRealm` | realm مربوط به Keycloak |
|
||
| `permissionUrl` | آدرس API مربوط به permission |
|
||
| `keycloakEnabled` | فعال یا غیرفعال کردن Keycloak به صورت دستی |
|
||
| `permissionEnabled` | فعال یا غیرفعال کردن Permission به صورت دستی |
|
||
|
||
---
|
||
|
||
## مقدارهای پیشفرض BiProvider
|
||
|
||
```js
|
||
keycloakUrl = "https://auth.ibagher.ir";
|
||
keycloakRealm = "bi";
|
||
permissionUrl = "https://api.d.aiengines.ir/user_api/v1/permissions/list";
|
||
updateChecker = false;
|
||
```
|
||
|
||
نکته: اگر `keycloakClientId` داده شود، Keycloak فعال میشود. اگر `permissionClientId` داده شود، Permission فعال میشود.
|
||
|
||
برای کنترل دستی:
|
||
|
||
```jsx
|
||
<BiProvider
|
||
apiBaseUrl="https://api.example.com"
|
||
keycloakEnabled={false}
|
||
permissionEnabled={false}
|
||
>
|
||
<YourApp />
|
||
</BiProvider>
|
||
```
|
||
|
||
---
|
||
|
||
# UpdateChecker
|
||
|
||
به صورت پیشفرض `updateChecker` خاموش است.
|
||
|
||
برای فعال کردن:
|
||
|
||
```jsx
|
||
<BiProvider
|
||
apiBaseUrl="https://api.example.com"
|
||
keycloakClientId="front-client"
|
||
permissionClientId="permission-client"
|
||
updateChecker
|
||
>
|
||
<YourApp />
|
||
</BiProvider>
|
||
```
|
||
|
||
اگر `updateChecker` را فعال میکنی، پروژه باید فایل version تولید کند. معمولاً باید یک اسکریپت مثل `write-version.js` در root پروژه داشته باشی و در `package.json` پروژه target، قبل از build اجرا شود.
|
||
|
||
نمونه:
|
||
|
||
```json
|
||
{
|
||
"scripts": {
|
||
"prebuild": "node write-version.js",
|
||
"build": "next build"
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
# استفاده از API پایه
|
||
|
||
```js
|
||
import { api, fetcher, configureApi, getApi } from "uikit/core";
|
||
```
|
||
|
||
اگر از `BiProvider` استفاده میکنی، معمولاً نیازی نیست دستی `configureApi` را صدا بزنی، چون `BiProvider` این کار را انجام میدهد.
|
||
|
||
نمونه استفاده مستقیم:
|
||
|
||
```js
|
||
import { api } from "uikit/core";
|
||
|
||
const response = await api.get("/users");
|
||
```
|
||
|
||
یا:
|
||
|
||
```js
|
||
import { fetcher } from "uikit/core";
|
||
|
||
const data = await fetcher("/users");
|
||
```
|
||
|
||
---
|
||
|
||
# استفاده از createApi
|
||
|
||
`createApi` برای ساختن یک API ساده بر پایه React Query استفاده میشود.
|
||
|
||
```js
|
||
import { createApi } from "uikit/core";
|
||
|
||
export const profileApi = createApi({
|
||
key: "profile",
|
||
url: "/content/rokn/profiles",
|
||
|
||
actions: {
|
||
update: {
|
||
method: "patch",
|
||
url: (id) => `/content/rokn/profiles/${id}`,
|
||
},
|
||
|
||
delete: {
|
||
method: "delete",
|
||
},
|
||
|
||
generate: {
|
||
method: "post",
|
||
url: "/content/rokn/generate/profile",
|
||
},
|
||
|
||
updateImages: {
|
||
method: "post",
|
||
url: (id) => `/content/rokn/profile-update/${id}/images`,
|
||
body: ({ formData }) => formData,
|
||
},
|
||
},
|
||
});
|
||
```
|
||
|
||
---
|
||
|
||
## گرفتن لیست
|
||
|
||
```jsx
|
||
const { data, isLoading, error } = profileApi.useGet({
|
||
page: 1,
|
||
size: 20,
|
||
is_confirmed: true,
|
||
});
|
||
```
|
||
|
||
این request ساخته میشود:
|
||
|
||
```txt
|
||
GET /content/rokn/profiles?page=1&size=20&is_confirmed=true
|
||
```
|
||
|
||
---
|
||
|
||
## اجرای action
|
||
|
||
```jsx
|
||
const updateProfile = profileApi.useAction("update");
|
||
|
||
await updateProfile.mutateAsync({
|
||
id: 10,
|
||
username: "new_username",
|
||
});
|
||
```
|
||
|
||
در این حالت:
|
||
|
||
```txt
|
||
PATCH /content/rokn/profiles/10
|
||
```
|
||
|
||
body:
|
||
|
||
```js
|
||
{
|
||
username: "new_username"
|
||
}
|
||
```
|
||
|
||
`id` به صورت پیشفرض از body حذف میشود، چون برای ساختن URL استفاده شده است.
|
||
|
||
---
|
||
|
||
## action با همان URL اصلی
|
||
|
||
اگر action همان URL اصلی را لازم دارد، نیازی نیست `url` را دوباره بنویسی.
|
||
|
||
```js
|
||
export const identityGenerateApi = createApi({
|
||
key: "identity-generate",
|
||
url: "/content/rokn/identity-generate/logs",
|
||
|
||
actions: {
|
||
delete: {
|
||
method: "delete",
|
||
},
|
||
},
|
||
});
|
||
```
|
||
|
||
یعنی:
|
||
|
||
```txt
|
||
DELETE /content/rokn/identity-generate/logs
|
||
```
|
||
|
||
---
|
||
|
||
## action با FormData
|
||
|
||
```js
|
||
const updateImages = profileApi.useAction("updateImages");
|
||
|
||
const formData = new FormData();
|
||
formData.append("image", file);
|
||
|
||
await updateImages.mutateAsync({
|
||
id: 10,
|
||
formData,
|
||
});
|
||
```
|
||
|
||
در تعریف action:
|
||
|
||
```js
|
||
updateImages: {
|
||
method: "post",
|
||
url: (id) => `/content/rokn/profile-update/${id}/images`,
|
||
body: ({ formData }) => formData,
|
||
}
|
||
```
|
||
|
||
`body` مشخص میکند چه چیزی واقعاً به عنوان body ارسال شود.
|
||
|
||
---
|
||
|
||
# استفاده از Keycloak
|
||
|
||
```js
|
||
import {
|
||
getKeycloakToken,
|
||
loginKeycloak,
|
||
logoutKeycloak,
|
||
updateKeycloakToken,
|
||
} from "uikit/core";
|
||
|
||
const token = getKeycloakToken();
|
||
|
||
await updateKeycloakToken(30);
|
||
|
||
loginKeycloak();
|
||
|
||
logoutKeycloak({
|
||
redirectUri: window.location.origin,
|
||
});
|
||
```
|
||
|
||
---
|
||
|
||
# استفاده از Permission
|
||
|
||
```jsx
|
||
import { Can, usePermission } from "uikit/core";
|
||
|
||
function Page() {
|
||
const { can, cannot, permissions } = usePermission();
|
||
|
||
return (
|
||
<>
|
||
{can("users:list") && <div>لیست کاربران</div>}
|
||
|
||
<Can perm="users:create" fallback={null}>
|
||
<button>ایجاد کاربر</button>
|
||
</Can>
|
||
</>
|
||
);
|
||
}
|
||
```
|
||
|
||
نکته: prop کامپوننت `Can` برابر `perm` است.
|
||
|
||
```jsx
|
||
<Can perm="users:list">
|
||
<UsersList />
|
||
</Can>
|
||
```
|
||
|
||
---
|
||
|
||
# استفاده از DataTable
|
||
|
||
`DataTable` داخل مسیر `uikit/table` قرار دارد.
|
||
|
||
dependencyهای لازم:
|
||
|
||
```bash
|
||
yarn add @chakra-ui/react @emotion/react @emotion/styled framer-motion @tanstack/react-table react-icons
|
||
```
|
||
|
||
استفاده:
|
||
|
||
```jsx
|
||
import { DataTable } from "uikit/table";
|
||
|
||
function UsersTable({ data, columns }) {
|
||
return (
|
||
<DataTable
|
||
data={data}
|
||
columns={columns}
|
||
page={1}
|
||
size={20}
|
||
pagination={{
|
||
totalCount: 100,
|
||
totalPages: 5,
|
||
}}
|
||
onPageChange={(page) => console.log(page)}
|
||
getRowId={(row) => String(row.id)}
|
||
enableSelection
|
||
showIndex
|
||
>
|
||
<DataTable.Topbar>
|
||
<DataTable.Title>لیست کاربران</DataTable.Title>
|
||
<DataTable.Spacer />
|
||
<DataTable.Actions>
|
||
<DataTable.SelectAll />
|
||
</DataTable.Actions>
|
||
</DataTable.Topbar>
|
||
|
||
<DataTable.Content>
|
||
<DataTable.Table />
|
||
<DataTable.Pagination />
|
||
</DataTable.Content>
|
||
</DataTable>
|
||
);
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
# dependencyهای پیشنهادی بر اساس نیاز
|
||
|
||
## فقط utils
|
||
|
||
نیازی به نصب dependency اضافه نیست.
|
||
|
||
```bash
|
||
yarn add git+https://git.d.aiengines.ir/bi/uikit.git
|
||
```
|
||
|
||
---
|
||
|
||
## pagination یا layout
|
||
|
||
```bash
|
||
yarn add @chakra-ui/react @emotion/react @emotion/styled framer-motion react-icons
|
||
```
|
||
|
||
برای `layout` معمولاً `react-icons` لازم نیست، ولی اگر پروژه از pagination هم استفاده میکند، نصبش کن.
|
||
|
||
---
|
||
|
||
## core
|
||
|
||
```bash
|
||
yarn add @chakra-ui/react @emotion/react @emotion/styled framer-motion @tanstack/react-query axios keycloak-js
|
||
```
|
||
|
||
---
|
||
|
||
## table
|
||
|
||
```bash
|
||
yarn add @chakra-ui/react @emotion/react @emotion/styled framer-motion @tanstack/react-table react-icons
|
||
```
|
||
|
||
---
|
||
|
||
# نکات مهم توسعه
|
||
|
||
بعد از تغییر فایلها، build بگیر:
|
||
|
||
```bash
|
||
yarn build
|
||
```
|
||
|
||
فایلهای خروجی مورد انتظار:
|
||
|
||
```txt
|
||
dist/index.js
|
||
dist/utils.js
|
||
dist/pagination.js
|
||
dist/layout.js
|
||
dist/core.js
|
||
dist/table.js
|
||
```
|
||
|
||
اگر entry جدید اضافه شد، باید هم در `vite.config.js` و هم در `package.json > exports` ثبت شود.
|
||
|
||
---
|
||
|
||
# قانون کلی
|
||
|
||
هر چیزی که dependency سنگین ندارد:
|
||
|
||
```txt
|
||
uikit/utils
|
||
```
|
||
|
||
هر چیزی که UI سبک است و Chakra میخواهد:
|
||
|
||
```txt
|
||
uikit/pagination
|
||
uikit/layout
|
||
```
|
||
|
||
هر چیزی که مربوط به API، auth، permission و provider است:
|
||
|
||
```txt
|
||
uikit/core
|
||
```
|
||
|
||
هر چیزی که مربوط به table است:
|
||
|
||
```txt
|
||
uikit/table
|
||
```
|
||
|
||
Root package یعنی:
|
||
|
||
```txt
|
||
uikit
|
||
```
|
||
|
||
باید سبک بماند و فقط چیزهایی مثل utils را export کند.
|