UNPKG

@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
# 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.