@weareconceptstudio/cs-admin
Version:
A comprehensive React-based admin panel system with dynamic actions, form components, and design system
803 lines (637 loc) • 19.2 kB
Markdown
# Core Stores Documentation
This document provides comprehensive documentation for all stores and state management systems available in CS Admin Core.
## Table of Contents
- [Overview](#overview)
- [Store Architecture](#store-architecture)
- [Global Store](#global-store)
- [List Store](#list-store)
- [Current Store](#current-store)
- [Media Store](#media-store)
- [Core Context](#core-context)
- [Usage Examples](#usage-examples)
- [Best Practices](#best-practices)
---
## Overview
CS Admin Core provides a comprehensive state management system built on Zustand, offering stores for different aspects of the application:
- **Global Store**: Application-wide state and utilities
- **List Store**: Data listing and pagination management
- **Current Store**: Single record management and relationships
- **Media Store**: File and media asset management
- **Core Context**: Configuration and notification management
### Key Features
- **Zustand-based**: Lightweight and performant state management
- **TypeScript Support**: Full type definitions
- **API Integration**: Built-in API communication
- **Error Handling**: Comprehensive error management
- **Notifications**: Integrated notification system
- **Internationalization**: Multi-language support
---
## Store Architecture
### Store Structure
```jsx
// Store imports
import { useGlobalStore, useList, useCurrent, useMedia } from '@weareconceptstudio/cs-admin-core';
// Store usage
const globalState = useGlobalStore();
const listState = useList();
const currentState = useCurrent();
const mediaState = useMedia();
```
### Store Dependencies
```mermaid
graph TD
A[Global Store] --> B[List Store]
A --> C[Current Store]
A --> D[Media Store]
E[Core Context] --> A
F[API Utils] --> B
F --> C
F --> D
```
---
## Global Store
The global store manages application-wide state and utilities.
### State Properties
| Property | Type | Description |
| ------------------ | -------- | -------------------------------- |
| `defaultLanguage` | string | Default application language |
| `navigate` | function | React Router navigation function |
| `openNotification` | function | Notification display function |
### Methods
| Method | Parameters | Description |
| ---------------- | ---------- | --------------------------------- |
| `setGlobalStore` | `callback` | Update global state with callback |
### Usage
```jsx
import { useGlobalStore } from '@weareconceptstudio/cs-admin-core';
const MyComponent = () => {
const { defaultLanguage, navigate, openNotification } = useGlobalStore();
const handleNavigate = () => {
navigate('/admin/posts');
};
const handleNotification = () => {
openNotification({
type: 'success',
message: 'Success',
description: 'Operation completed',
});
};
return (
<div>
<p>Current language: {defaultLanguage}</p>
<button onClick={handleNavigate}>Navigate</button>
<button onClick={handleNotification}>Show Notification</button>
</div>
);
};
```
### Initialization
```jsx
import { makeGlobalStore } from '@weareconceptstudio/cs-admin-core';
// Initialize global store
makeGlobalStore({
defaultLanguage: 'en',
openNotification: (notification) => {
// Custom notification handler
},
});
```
---
## List Store
The list store manages data listing, pagination, and list operations.
### State Properties
| Property | Type | Description |
| ------------ | ------- | ---------------------- |
| `loading` | boolean | Loading state |
| `error` | object | Error information |
| `list` | object | List data by path |
| `pagination` | object | Pagination information |
### Methods
| Method | Parameters | Description |
| ----------------- | --------------------------------------- | ------------------ |
| `getList` | `{ path, params }` | Fetch list data |
| `sortList` | `{ path, model, oldIndex, newIndex }` | Sort list items |
| `replicateRecord` | `{ editPath, id, model }` | Replicate a record |
| `handleDelete` | `{ path, param, isSinglePage, params }` | Delete a record |
### Usage
```jsx
import { useList } from '@weareconceptstudio/cs-admin-core';
const PostsList = () => {
const { loading, list, pagination, getList, handleDelete } = useList();
useEffect(() => {
getList({
path: 'posts',
params: { page: 1, per_page: 10 },
});
}, []);
const handleDeletePost = (id) => {
handleDelete({
path: 'posts',
param: id,
isSinglePage: false,
});
};
if (loading) return <div>Loading...</div>;
return (
<div>
{list.posts?.map((post) => (
<div key={post.id}>
<h3>{post.title}</h3>
<button onClick={() => handleDeletePost(post.id)}>Delete</button>
</div>
))}
</div>
);
};
```
### Pagination
```jsx
const PaginatedList = () => {
const { list, pagination, getList } = useList();
const handlePageChange = (page) => {
getList({
path: 'posts',
params: {
page,
per_page: pagination.pageSize,
},
});
};
return (
<div>
{/* List content */}
<Pagination
current={pagination.current}
total={pagination.total}
pageSize={pagination.pageSize}
onChange={handlePageChange}
/>
</div>
);
};
```
### Sorting
```jsx
const SortableList = () => {
const { list, sortList } = useList();
const handleSort = (oldIndex, newIndex) => {
sortList({
path: 'posts',
model: 'posts',
oldIndex,
newIndex,
});
};
return <DragDropContext onDragEnd={handleSort}>{/* Sortable items */}</DragDropContext>;
};
```
---
## Current Store
The current store manages single record operations and relationships.
### State Properties
| Property | Type | Description |
| ---------------- | ------- | --------------------------- |
| `loading` | boolean | Loading state |
| `current` | object | Current record data by path |
| `currentOptions` | object | Current operation options |
| `relations` | object | Related data |
### Methods
| Method | Parameters | Description |
| -------------- | -------------------------------------------------------- | ------------------- |
| `getOne` | `{ path, param, params, lang, type }` | Fetch single record |
| `getSingle` | `{ path, params, lang }` | Fetch single data |
| `getRelations` | `{ path, endpoints, type, params, func, handleLoading }` | Fetch related data |
| `create` | `{ path, data }` | Create new record |
| `update` | `{ path, param, data, redirect, isList }` | Update record |
| `updateSingle` | `{ path, data }` | Update single data |
### Usage
```jsx
import { useCurrent } from '@weareconceptstudio/cs-admin-core';
const PostDetail = ({ postId }) => {
const { loading, current, relations, getOne, getRelations, update } = useCurrent();
useEffect(() => {
// Load post and related data
getRelations({
path: 'posts',
endpoints: ['categories', 'tags'],
func: 'getOne',
}).then(() => {
getOne({
path: 'posts',
param: postId,
params: { isEdit: true },
});
});
}, [postId]);
const handleUpdate = (data) => {
update({
path: 'posts',
param: postId,
data,
redirect: true,
});
};
if (loading) return <div>Loading...</div>;
const post = current.posts;
return (
<div>
<h1>{post?.title}</h1>
<p>Category: {relations.categories?.find((c) => c.id === post?.category_id)?.name}</p>
{/* Form for editing */}
</div>
);
};
```
### Creating Records
```jsx
const CreatePost = () => {
const { create, getRelations } = useCurrent();
useEffect(() => {
getRelations({
path: 'posts',
endpoints: ['categories', 'tags'],
func: 'getOne',
});
}, []);
const handleCreate = (formData) => {
create({
path: 'posts',
data: formData,
});
};
return <form onSubmit={handleCreate}>{/* Form fields */}</form>;
};
```
### Updating Records
```jsx
const EditPost = ({ postId }) => {
const { current, update } = useCurrent();
const handleUpdate = (data) => {
update({
path: 'posts',
param: postId,
data,
redirect: false, // Stay on current page
isList: true, // Update list state
});
};
return <form onSubmit={handleUpdate}>{/* Form fields with current.posts data */}</form>;
};
```
---
## Media Store
The media store manages file uploads, media assets, and media operations.
### State Properties
| Property | Type | Description |
| --------------- | ------- | ----------------------- |
| `assets` | array | Available media assets |
| `loading` | boolean | Loading state |
| `pendingAssets` | array | Assets pending upload |
| `assetToEdit` | object | Currently editing asset |
### Methods
| Method | Parameters | Description |
| ----------------------- | ------------------------ | ----------------------- |
| `getAssets` | `params` | Fetch media assets |
| `setAssetToEdit` | `assetToEdit` | Set asset for editing |
| `updateAsset` | `id, data` | Update asset metadata |
| `deleteAsset` | `ids` | Delete assets |
| `changeAssetDimensions` | `values` | Change asset dimensions |
| `addToPendingAssets` | `nextAssets` | Add assets to pending |
| `updatePendingAsset` | `nextAsset` | Update pending asset |
| `removePendingAsset` | `assetToRemove` | Remove pending asset |
| `replaceAsset` | `nextAsset` | Replace existing asset |
| `downloadAsset` | - | Download current asset |
| `getSelectedAssets` | `ids` | Get selected assets |
| `addUploadedFile` | `newAsset, pendingAsset` | Add uploaded file |
| `updateMediaState` | `callback` | Update media state |
### Usage
```jsx
import { useMedia } from '@weareconceptstudio/cs-admin-core';
const MediaManager = () => {
const { assets, loading, getAssets, deleteAsset, updateAsset } = useMedia();
useEffect(() => {
getAssets({ acceptTypes: ['image/*'] });
}, []);
const handleDelete = (ids) => {
deleteAsset(ids);
};
const handleUpdate = (id, data) => {
updateAsset(id, data);
};
if (loading) return <div>Loading media...</div>;
return (
<div className='media-grid'>
{assets.map((asset) => (
<div
key={asset.id}
className='media-item'>
<img
src={asset.url}
alt={asset.filename}
/>
<button onClick={() => handleDelete([asset.id])}>Delete</button>
</div>
))}
</div>
);
};
```
### File Upload
```jsx
const FileUpload = () => {
const { addToPendingAssets, addUploadedFile } = useMedia();
const handleFileSelect = (files) => {
const fileArray = Array.from(files);
addToPendingAssets(fileArray);
};
const handleUpload = async (file) => {
const formData = new FormData();
formData.append('file', file);
try {
const response = await fetch('/api/upload', {
method: 'POST',
body: formData,
});
const newAsset = await response.json();
addUploadedFile(newAsset, file);
} catch (error) {
console.error('Upload failed:', error);
}
};
return (
<input
type='file'
multiple
onChange={(e) => handleFileSelect(e.target.files)}
/>
);
};
```
### Asset Management
```jsx
const AssetEditor = ({ assetId }) => {
const { assets, assetToEdit, setAssetToEdit, updateAsset, downloadAsset } = useMedia();
const asset = assets.find((a) => a.id === assetId);
const handleUpdate = (data) => {
updateAsset(assetId, data);
};
const handleDownload = () => {
setAssetToEdit(asset);
downloadAsset();
};
return (
<div>
<img
src={asset?.url}
alt={asset?.filename}
/>
<button onClick={handleDownload}>Download</button>
{/* Edit form */}
</div>
);
};
```
---
## Core Context
The core context provides configuration and notification management.
### Context Properties
| Property | Type | Description |
| ----------------- | ------ | ------------------- |
| `siteName` | string | Site name |
| `logo` | string | Site logo |
| `defaultLanguage` | string | Default language |
| `languages` | array | Available languages |
| `adminPrefix` | string | Admin URL prefix |
| `apiPrefix` | string | API URL prefix |
| `menu` | object | Menu configuration |
| `inputConfig` | object | Input configuration |
### Methods
| Method | Parameters | Description |
| ------------------ | -------------- | ----------------- |
| `updateCoreState` | `callback` | Update core state |
| `openNotification` | `notification` | Show notification |
### Usage
```jsx
import { useCoreContext } from '@weareconceptstudio/cs-admin-core';
const MyComponent = () => {
const { siteName, defaultLanguage, openNotification, updateCoreState } = useCoreContext();
const handleNotification = () => {
openNotification({
type: 'success',
message: 'Success',
description: 'Operation completed',
duration: 5,
});
};
const handleConfigUpdate = () => {
updateCoreState((state, setState) => {
setState({
...state,
siteName: 'New Site Name',
});
});
};
return (
<div>
<h1>{siteName}</h1>
<p>Language: {defaultLanguage}</p>
<button onClick={handleNotification}>Show Notification</button>
<button onClick={handleConfigUpdate}>Update Config</button>
</div>
);
};
```
### Configuration
```jsx
import { CoreProvider } from '@weareconceptstudio/cs-admin-core';
const App = () => {
const config = {
siteName: 'My Admin Panel',
defaultLanguage: 'en',
languages: [
{ id: 'en', name: 'English' },
{ id: 'es', name: 'Spanish' },
],
adminPrefix: '/admin',
apiPrefix: '/api/admin',
menu: {
main: [
{
title: 'Content',
children: [{ title: 'Posts', path: '/admin/posts', key: 'posts' }],
},
],
},
};
return <CoreProvider {...config}>{/* Your app content */}</CoreProvider>;
};
```
---
## Usage Examples
### Complete Data Management
```jsx
import { useList, useCurrent, useMedia, useGlobalStore } from '@weareconceptstudio/cs-admin-core';
const PostsManager = () => {
const { getList, list, loading } = useList();
const { getOne, current, create, update } = useCurrent();
const { getAssets, assets } = useMedia();
const { openNotification } = useGlobalStore();
useEffect(() => {
// Load posts and media
Promise.all([getList({ path: 'posts' }), getAssets({ acceptTypes: ['image/*'] })]);
}, []);
const handleCreatePost = async (data) => {
try {
await create({ path: 'posts', data });
openNotification({
type: 'success',
message: 'Post created successfully',
});
getList({ path: 'posts' }); // Refresh list
} catch (error) {
openNotification({
type: 'error',
message: 'Failed to create post',
});
}
};
const handleEditPost = async (id, data) => {
try {
await update({ path: 'posts', param: id, data });
openNotification({
type: 'success',
message: 'Post updated successfully',
});
} catch (error) {
openNotification({
type: 'error',
message: 'Failed to update post',
});
}
};
return <div>{/* Posts list and management UI */}</div>;
};
```
### Store Integration
```jsx
const IntegratedComponent = () => {
// List operations
const { getList, list, pagination } = useList();
// Current record operations
const { getOne, current, relations, getRelations } = useCurrent();
// Media operations
const { getAssets, assets, addToPendingAssets } = useMedia();
// Global operations
const { navigate, openNotification } = useGlobalStore();
const handleCompleteWorkflow = async () => {
try {
// 1. Load related data
await getRelations({
path: 'posts',
endpoints: ['categories', 'tags'],
});
// 2. Load current post
await getOne({
path: 'posts',
param: '123',
});
// 3. Load media
await getAssets({ acceptTypes: ['image/*'] });
// 4. Show success notification
openNotification({
type: 'success',
message: 'Data loaded successfully',
});
// 5. Navigate to edit page
navigate('/admin/posts/edit/123');
} catch (error) {
openNotification({
type: 'error',
message: 'Failed to load data',
});
}
};
return (
<div>
<button onClick={handleCompleteWorkflow}>Load Complete Workflow</button>
</div>
);
};
```
---
## Best Practices
### 1. Store Organization
```jsx
// Group related store operations
const usePostOperations = () => {
const listStore = useList();
const currentStore = useCurrent();
const globalStore = useGlobalStore();
const createPost = async (data) => {
try {
await currentStore.create({ path: 'posts', data });
await listStore.getList({ path: 'posts' }); // Refresh list
globalStore.openNotification({
type: 'success',
message: 'Post created',
});
} catch (error) {
globalStore.openNotification({
type: 'error',
message: 'Failed to create post',
});
}
};
return { createPost, ...listStore, ...currentStore };
};
```
### 2. Error Handling
```jsx
const useErrorHandling = () => {
const { openNotification } = useGlobalStore();
const handleError = (error, context = '') => {
console.error(`Error in ${context}:`, error);
openNotification({
type: 'error',
message: 'An error occurred',
description: error.message || 'Please try again',
});
};
return { handleError };
};
```
### 3. Loading States
```jsx
const useLoadingStates = () => {
const { loading: listLoading } = useList();
const { loading: currentLoading } = useCurrent();
const { loading: mediaLoading } = useMedia();
const isLoading = listLoading || currentLoading || mediaLoading;
return { isLoading, listLoading, currentLoading, mediaLoading };
};
```
### 4. Performance Optimization
```jsx
// Use selectors to prevent unnecessary re-renders
const useOptimizedList = () => {
const list = useList((state) => state.list);
const loading = useList((state) => state.loading);
const getList = useList((state) => state.getList);
return { list, loading, getList };
};
```
### 5. TypeScript Integration
```tsx
interface Post {
id: number;
title: string;
content: string;
status: 'draft' | 'published';
}
const useTypedList = () => {
const list = useList<Record<string, Post[]>>((state) => state.list);
const getList = useList((state) => state.getList);
return { list, getList };
};
```
This documentation covers all the core stores and state management systems available in CS Admin Core. These stores provide a comprehensive foundation for managing application state, data operations, and user interactions.