uikit/README.md
Mohamadzadeh 9bd798188d
All checks were successful
Build and Deploy Next.js + Nginx Docker Image / build-and-deploy (push) Successful in 49s
Build and Deploy Next.js + Nginx Docker Image / deploy (push) Successful in 7s
fix sidebar
2026-08-19 19:00:54 +03:30

848 lines
18 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# uikit
Shared library for BI next.js projects.
---
## Install
```bash
yarn add git+https://git.d.aiengines.ir/bi/uikit.git
```
---
## Update
```bash
yarn upgrade uikit
```
or
```bash
rm -rf .next
rm -rf node_modules/.vite
rm -rf node_modules/.cache
rm -rf node_modules/uikit
yarn upgrade uikit
```
---
# imports
| مسیر | کاربرد | dependency اضافی |
| ------------------ | ------------------------------------- | --------------------------------------- |
| `uikit` | utility functions | - |
| `uikit/utils` | utility functions | - |
| `uikit/pagination` | pagination | Chakra + React + react-icons |
| `uikit/layout` | Header, Sidebar | Chakra + React + React Icons |
| `uikit/core` | API، BiProvider، Keycloak، Permission | Chakra + React Query + Axios + Keycloak + Query Params |
| `uikit/table` | DataTable | Chakra + TanStack Table + Pagination |
| `uikit/remote/ui` | Remote UI components مثل `GBadge` | Chakra + React + remote static file |
---
# utils
```js
import { toFaDigits, toEnDigits, toFaNumber } from "uikit/utils";
toFaDigits(123456);
toEnDigits("۱۲۳۴۵۶");
toFaNumber(123456);
```
or
```js
import { toFaDigits } from "uikit";
```
---
# pagination
required dependencies
```bash
yarn add @chakra-ui/react@2
yarn add @chakra-ui/icons@2
yarn add @emotion/react@11
yarn add @emotion/styled@11
yarn add chakra-react-select@5
yarn add react-select@5
yarn add framer-motion@10
yarn add react-icons
```
using
```jsx
import { Pagination, LightPagination, SimplePagination } from "uikit/pagination";
function Page() {
return (
<Pagination
currentPage={1}
totalCount={100}
totalPages={5}
onPageChange={(page) => console.log(page)}
/>
);
}
```
---
# Header
required dependencies:
```bash
yarn add @chakra-ui/react @emotion/react @emotion/styled framer-motion
```
using:
```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>
);
}
```
---
# Sidebar
required dependencies:
```bash
yarn add @chakra-ui/react @emotion/react @emotion/styled framer-motion react-icons
yarn add @tanstack/react-query axios keycloak-js
yarn add next-query-params@^5.1.0 use-query-params@^2.2.2
```
`Sidebar` همان UI ناوبری سامانه را برای دسکتاپ و موبایل ارائه می‌دهد. در دسکتاپ به صورت نوار آیکون کناری و در موبایل به صورت bottom navigation + drawer «بیشتر» نمایش داده می‌شود.
آیتم‌ها به صورت config به کامپوننت داده می‌شوند:
```jsx
import { SlUserFollowing } from "react-icons/sl";
const navigationItems = [
{
title: "آنالیز شبکه متعارف - فالوور/فالووینگ",
mobileTitle: "ارتباط کاربران",
icon: SlUserFollowing,
path: "/user-connections/analyze",
perm: ["analyze"],
},
];
```
نمونه استفاده در Next.js Pages Router:
```jsx
import NextLink from "next/link";
import { useRouter } from "next/router";
import { Sidebar } from "uikit/layout";
function AppSidebar() {
const router = useRouter();
return (
<Sidebar
items={navigationItems}
currentPath={router.asPath}
linkComponent={NextLink}
/>
);
}
```
`Sidebar` باید داخل `BiProvider` استفاده شود. Permission هر آیتم از `perm` توسط خود UI Kit بررسی می‌شود و خروج با Keycloak توسط خود UI Kit انجام می‌شود. سامانه مقصد نیازی به `usePermission` یا `logoutKeycloak` برای Sidebar ندارد.
کنترل دسترسی کلی `panel_access` عمداً داخل Sidebar انجام نمی‌شود تا navigation هنگام render باعث redirect و لغو route در Next.js نشود. اگر سامانه به این guard نیاز دارد، از `PanelAccessGuard` موجود در `uikit/core` در سطح Layout/App استفاده کنید.
`aliases` نیز پشتیبانی می‌شود و برای تشخیص آیتم active استفاده می‌شود:
```jsx
{
title: "آنالیز شبکه های اجتماعی خارجی",
mobileTitle: "شبکه خارجی",
icon: TbAnalyze,
path: "/posts/final_analyze",
aliases: ["/posts/analyze"],
perm: ["analyze"],
}
```
اگر `perm` آرایه باشد، همه permissionهای داخل آرایه برای فعال بودن آیتم لازم هستند. آیتم بدون `perm` محدودیت permission ندارد.
---
# Core
required dependencies:
```bash
yarn add @chakra-ui/react@^2
yarn add @emotion/react@^11
yarn add @emotion/styled@^11
yarn add framer-motion@^12
yarn add @tanstack/react-query@^5
yarn add axios@^1
yarn add keycloak-js
yarn add next-query-params@^5.1.0
yarn add use-query-params@^2.2.2
```
---
## 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 props
| Prop |
| ----------------------------- |
| `apiBaseUrl` |
| `keycloakClientId` |
| `permissionClientId` |
| `loading` |
| `updateChecker` |
| `updateCheckerProps` |
| `keycloakUrl` |
| `keycloakRealm` |
| `permissionUrl` |
| `keycloakEnabled` |
| `permissionEnabled` |
| `remoteUiEnabled` |
| `remoteUiUrl` |
| `remoteUiChakraProviderProps` |
---
## BiProvider defaults
```js
keycloakUrl = "https://auth.ibagher.ir";
keycloakRealm = "bi";
permissionUrl = "https://api.d.aiengines.ir/user_api/v1/permissions/list";
updateChecker = false;
remoteUiEnabled = true;
remoteUiUrl = "https://uikit.d.aiengines.ir/GBadge.js";
```
---
## BiProvider with Remote UI
`BiProvider` به صورت خودکار dependencyهای لازم برای کامپوننت‌های `remote/ui` را setup می‌کند.
پس اگر سامانه مقصد از `BiProvider` استفاده می‌کند، برای استفاده از `GBadge` کار اضافه‌ای لازم نیست.
```jsx
import { BiProvider } from "uikit/core";
import { GBadge } from "uikit/remote/ui";
export default function App() {
return (
<BiProvider
apiBaseUrl="https://api.example.com"
keycloakClientId="front-client"
permissionClientId="permission-client"
>
<GBadge colorScheme="blue" py={1} px={2} borderRadius="full">
فعال
</GBadge>
</BiProvider>
);
}
```
در این حالت `GBadge` همچنان از Chakra استفاده می‌کند، ولی فایل remote آن، یعنی `GBadge.js`، خودش Chakra و React و font را داخل bundle نمی‌آورد.
dependencyهای لازم از خود سامانه مقصد گرفته می‌شوند.
---
## Change Remote UI URL
اگر فایل remote روی آدرس دیگری deploy شده باشد:
```jsx
import { BiProvider } from "uikit/core";
export default function App() {
return (
<BiProvider
apiBaseUrl="https://api.example.com"
keycloakClientId="front-client"
permissionClientId="permission-client"
remoteUiUrl="https://static.example.com/uikit/GBadge.js"
>
<YourApp />
</BiProvider>
);
}
```
---
## Disable Remote UI setup in BiProvider
اگر نمی‌خواهید `BiProvider` به صورت خودکار remote ui را setup کند:
```jsx
import { BiProvider } from "uikit/core";
export default function App() {
return (
<BiProvider
apiBaseUrl="https://api.example.com"
keycloakClientId="front-client"
permissionClientId="permission-client"
remoteUiEnabled={false}
>
<YourApp />
</BiProvider>
);
}
```
---
To enable `updateChecker` you should have a script `write-version.js` in the root of the project and in the `package.json` of the target project, run before the build:
```json
{
"scripts": {
"prebuild": "node write-version.js",
"build": "next build"
}
}
```
---
# API
```js
import { api, fetcher, configureApi, getApi } from "uikit/core";
```
```js
import { api } from "uikit/core";
const response = await api.get("/users");
```
or
```js
import { fetcher } from "uikit/core";
const data = await fetcher("/users");
```
---
# createApi
```js
import { createApi } from "uikit/core";
export const sampleApi = createApi({
key: "sample",
url: "/...",
actions: {
create: {
method: "post",
url: "/...",
},
update: {
method: "patch",
url: (id) => `/.../${id}`,
},
delete: {
method: "delete",
url: (id) => `/.../${id}`,
},
confirm: {
method: "post",
url: "/...",
},
updateImage: {
method: "post",
url: (id) => `/.../${id}/image`,
body: ({ formData }) => formData,
},
},
});
```
---
# Keycloak
```js
import {
getKeycloakToken,
loginKeycloak,
logoutKeycloak,
} from "uikit/core";
const token = getKeycloakToken();
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>
</>
);
}
```
---
# Remote UI
`remote/ui` برای کامپوننت‌هایی است که باید بدون build مجدد سامانه مقصد، از طریق فایل remote به‌روزرسانی شوند.
مثلاً `GBadge` از این مسیر استفاده می‌شود:
```jsx
import { GBadge } from "uikit/remote/ui";
function Page() {
return (
<GBadge colorScheme="blue" py={1} px={2} borderRadius="full">
فعال
</GBadge>
);
}
```
---
## GBadge
required dependencies:
```bash
yarn add @chakra-ui/react @emotion/react @emotion/styled framer-motion
```
using:
```jsx
import { GBadge } from "uikit/remote/ui";
function Page({ item }) {
return (
<GBadge colorScheme="blue" py={1} px={2} borderRadius="full">
{item}
</GBadge>
);
}
```
`GBadge` از Chakra `Badge` استفاده می‌کند.
اما برای کم شدن حجم فایل remote، این موارد داخل `GBadge.js` bundle نمی‌شوند:
```txt
React
ReactDOM
Chakra
Emotion
theme
Fonts
font files
```
این موارد باید در سامانه مقصد وجود داشته باشند.
---
## GBadge with BiProvider
اگر سامانه مقصد از `BiProvider` استفاده می‌کند، نیازی به setup دستی نیست:
```jsx
import { BiProvider } from "uikit/core";
import { GBadge } from "uikit/remote/ui";
function App() {
return (
<BiProvider
apiBaseUrl="https://api.example.com"
keycloakClientId="front-client"
permissionClientId="permission-client"
>
<GBadge colorScheme="blue" py={1} px={2} borderRadius="full">
فعال
</GBadge>
</BiProvider>
);
}
```
---
## GBadge without BiProvider
اگر سامانه مقصد از `BiProvider` استفاده نمی‌کند، دو حالت وجود دارد.
### حالت ساده
اگر theme خاصی ندارید، فقط استفاده از `GBadge` کافی است:
```jsx
import { GBadge } from "uikit/remote/ui";
function Page() {
return (
<GBadge colorScheme="blue" py={1} px={2} borderRadius="full">
فعال
</GBadge>
);
}
```
در این حالت wrapper مربوط به `GBadge` خودش dependencyهای لازم را setup می‌کند.
---
### حالت با theme اختصاصی
اگر سامانه مقصد theme اختصاصی Chakra دارد، بهتر است در entry اصلی پروژه یک بار setup انجام شود.
مثلاً در `main.jsx`، `App.jsx` یا `_app.jsx`:
```jsx
import React from "react";
import { createRoot } from "react-dom/client";
import { ChakraProvider } from "@chakra-ui/react";
import { setupUikitRemoteDeps } from "uikit/remote/ui";
import { theme } from "./theme";
import App from "./App";
setupUikitRemoteDeps({
theme,
});
createRoot(document.getElementById("root")).render(
<ChakraProvider theme={theme}>
<App />
</ChakraProvider>,
);
```
برای Next.js:
```jsx
import { ChakraProvider } from "@chakra-ui/react";
import { setupUikitRemoteDeps } from "uikit/remote/ui";
import { theme } from "@/theme";
setupUikitRemoteDeps({
theme,
});
export default function App({ Component, pageProps }) {
return (
<ChakraProvider theme={theme}>
<Component {...pageProps} />
</ChakraProvider>
);
}
```
---
## Remote URL without BiProvider
اگر سامانه مقصد از `BiProvider` استفاده نمی‌کند و آدرس فایل remote متفاوت است:
```jsx
import { setupUikitRemoteDeps } from "uikit/remote/ui";
import { theme } from "./theme";
setupUikitRemoteDeps({
theme,
remoteUrl: "https://static.example.com/uikit/GBadge.js",
});
```
یا مستقیم روی خود کامپوننت:
```jsx
import { GBadge } from "uikit/remote/ui";
function Page() {
return (
<GBadge
remoteUrl="https://static.example.com/uikit/GBadge.js"
colorScheme="blue"
py={1}
px={2}
borderRadius="full"
>
فعال
</GBadge>
);
}
```
---
## Font
فونت داخل فایل remote باندل نمی‌شود.
اگر سامانه مقصد از `BiProvider` استفاده می‌کند، فونت و theme اصلی از همان `BiProvider` می‌آید.
اگر سامانه مقصد `BiProvider` ندارد، فونت را در خود سامانه مقصد ست کنید:
```css
@font-face {
font-family: Estedad;
src: url("/fonts/estedad.woff2") format("woff2");
font-weight: 400;
font-style: normal;
font-display: swap;
}
:root {
--uikit-font-family: Estedad, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
}
body {
font-family: var(--uikit-font-family);
}
```
`GBadge` از این مقدار استفاده می‌کند:
```css
var(--uikit-font-family, inherit)
```
---
## Build Remote UI
برای ساخت فایل remote:
```bash
yarn build:remote
```
خروجی remote:
```txt
dist/remote/GBadge.js
```
این فایل باید روی static server یا CDN داخلی deploy شود.
مثلاً:
```txt
https://uikit.d.aiengines.ir/GBadge.js
```
---
## Deploy Remote UI
بعد از تغییر در `GBadge.remote.jsx` یا کامپوننت‌های remote:
```bash
yarn build:remote
```
سپس فایل زیر را deploy کنید:
```txt
dist/remote/GBadge.js
```
سامانه‌های مقصد بعد از reload صفحه، نسخه جدید remote را دریافت می‌کنند.
اگر cache سمت browser یا CDN فعال است، باید cache فایل remote کنترل شود.
---
## Important Notes
اگر از مسیر زیر استفاده شود:
```js
import { GBadge } from "uikit/remote/ui";
```
خود کامپوننت از فایل remote استفاده می‌کند.
اما اگر کامپوننتی از مسیر معمولی package import شود، مثل:
```js
import { SomeComponent } from "uikit";
```
یا:
```js
import { SomeComponent } from "uikit/table";
```
آن کامپوننت داخل build سامانه مقصد bundle می‌شود و برای آپدیت شدن نیاز به rebuild یا `yarn upgrade` دارد.
پس:
```txt
uikit/remote/ui => update by remote file
uikit/table => update by package upgrade
uikit/core => update by package upgrade
uikit/layout => update by package upgrade
```
---
# DataTable
required dependencies:
```bash
yarn add @chakra-ui/react @emotion/react @emotion/styled framer-motion @tanstack/react-table react-icons
```
using:
```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>
);
}
```
## Sidebar.Exit
`Sidebar` به‌صورت پیش‌فرض گزینه خروج را نمایش نمی‌دهد. برای فعال‌کردن خروج Keycloak داخلی UI Kit، آن را به‌صورت compound component اضافه کنید:
```jsx
<Sidebar
items={items}
currentPath={router.asPath}
linkComponent={NextLink}
>
<Sidebar.Exit />
</Sidebar>
```
عنوان و آیکون خروج قابل تغییر هستند:
```jsx
<Sidebar.Exit title="خروج از سامانه" icon={MyExitIcon} />
```
`Sidebar.Exit` از `logoutKeycloak` داخلی UI Kit استفاده می‌کند و callback خروج از سامانه مقصد نیاز ندارد.
### Sidebar active route notes
`Sidebar` compares `currentPath` with each item's `path` and `aliases`, ignoring query strings, hashes, and trailing slashes. Use the actual application route as `path`; use `aliases` only for alternate routes that should keep the same item active.
Active sidebar items are rendered as non-links to avoid redundant same-route navigations. This is especially useful on pages that synchronize URL query params.