39327-vm/recipes.md
2026-03-26 05:05:02 +00:00

702 lines
37 KiB
Markdown
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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.

# General instructions:
- Follow existing patterns and project instructions.
- Do not silently swallow errors. If you handle an error, log it and rethrow.
- Avoid unnecessary defensive checks that hide bugs.
# How to find the current user
1. Import hooks (if they are not already imported):
import { useAppDispatch, useAppSelector } from '../stores/hooks';
2. Get the current user:
const { currentUser } \= useAppSelector((state) \=\> state.auth);
Make sure that the useAppSelector call to retrieve currentUser is placed inside your component function so that it has access to the component's lifecycle and state.
3. Structure of currentUser:
id
firstName
lastName
phoneNumber
email
disabled
emailVerified
provider
Other fields for account management.
4. Use the data:
Access the user information via the properties of currentUser, for example, to display or validate.
# How to change sidebar styles
1. Open frontend/src/styles.ts
2. Locate the "white" object with sidebar styles (e.g., aside, asideMenuItem, asideMenuItemActive).
3. Modify the desired parameters (colors, fonts, etc.)
# Modifying sidebar elements (hiding or removing items)
1. Open file /frontend/src/menuAside.ts.
2. Edit or remove the corresponding objects in the menuAside array to achieve the desired changes.
# Remove the Tutorial from the App
1. In the file frontend/src/pages/\_app.tsx, delete the tutorial-related imports:
2. Remove the import for 'intro.js/introjs.css'.
3. Remove the import for the IntroGuide component.
4. Remove the imports of the tutorial steps arrays (e.g., appSteps, landingSteps, loginSteps, usersSteps, rolesSteps).
5. Remove the state and effect handling for the tutorial:
6. Delete the React.useState hooks for stepsEnabled, stepName, and steps.
7. Delete the React.useEffect that checks router.pathname and uses localStorage to determine which tutorial steps to set.
8. Remove the IntroGuide component from the JSX return.
9. In the return block, delete the \<IntroGuide ... /\> element.
10. Optionally, remove any related CSS or assets if theyre used exclusively for the tutorial.
# How to Globally Change the Corner Radius of the Application
1. Locate the Styles File: Open the main styles file where global styles are defined, typically found at: frontend/src/styles.ts
2. Modify the Tailwind Corner Radius Parameter: Locate the parameter corners within the file. This parameter controls the corner radius for the application using Tailwind CSS classes.
3. Change its value to your desired Tailwind CSS corner radius class. For example:
const corners \= 'rounded-lg'; // Change to your desired Tailwind class (e.g., 'rounded')
# Change Application Background Color
1. Open frontend/src/styles.ts.
2. Locate the style object for the theme you wish to modify (e.g., the "white" theme).
3. Find the bgLayoutColor parameter. It may look like: bgLayoutColor: 'bg-gray-50',
4. Update the value to your desired Tailwind CSS background color class (or custom CSS class). For example: bgLayoutColor: 'bg-blue-100',
5. Save the changes and refresh your application to see the new background color applied.
# How to Modify Table Columns
1. Locate the Configuration File: Find the relevant configuration file for the entity you are working with. The path will typically be structured as follows:
frontend/src/components/\[EntityName\]/configure\[EntityName\]Cols.tsx
Where \[EntityName\] is the name of the entity you are modifying (e.g., Orders, Users).
2. Edit Column Definitions: Open the found file and locate the column definitions. Here, you can modify the columns based on user requests. For example:
To remove the supplier column: Delete or comment out the supplier column definition from the array.
To replace the location column with a user column\*\*: Modify the existing location column to reflect the user performing the quantity logs instead.
# Change Global Font
1. Open frontend/tailwind.config.js.
2. In the theme.extend block, add a fontFamily key.
For example: fontFamily: { sans: \['YourCustomFont', 'sans-serif'\] }
# Modifying front-end routing
For modifying front-end routing, edit the file "frontend/src/menuAside.ts" to update the routes.
# Create new custom page on frontend
1. Create the page file at: frontend/src/pages/{PageName}.tsx
2. Use the following basic structure for your page:
import { mdiChartTimelineVariant } from '@mdi/js';
import Head from 'next/head';
import React, { ReactElement } from 'react';
import CardBox from '../components/CardBox';
import LayoutAuthenticated from '../layouts/Authenticated';
import SectionMain from '../components/SectionMain';
import SectionTitleLineWithButton from '../components/SectionTitleLineWithButton';
import { getPageTitle } from '../config';
const PageName= () \=\> {
return (\<\>\<Head\>\<title\>{getPageTitle('Edit profile')}\</title\>\</Head\>\<SectionMain\>\<SectionTitleLineWithButton icon={mdiChartTimelineVariant} title='Edit profile' main\> {''}\</SectionTitleLineWithButton\>\<CardBox\>{Content goes here}\</CardBox\>\</SectionMain\>\</\>);};
PageName.getLayout \= function getLayout(page: ReactElement) {return \<LayoutAuthenticated\>{page}\</LayoutAuthenticated\>;};
export default PageName;
3. Add a corresponding route on the front-end navigation by editing the file: frontend/src/menuAside.ts
For example, add: { href: '/page-href', label: 'Page Label', icon: icon.mdiAccountCircle },
# Integrate an External API in the Backend
1. Create a new route:
In backend/src/routes, create a file (e.g., thirdParty.js).
Define a new endpoint that triggers the business logic for integration.
2. Implement the route handler:
Import necessary services and middleware.
Wrap the handler with a helper (like wrapAsync) to manage errors.
Example:
router.get('/third-party', wrapAsync(async (req, res) \=\> { const result \= await ThirdPartyService.callExternalAPI(req.query); res.status(200).send(result); }));
3. Register the route:
In backend/src/index.js,
import the new route file.
Add it via app.use('/api/third-party', thirdPartyRoutes).
4. Implement the business logic:
In backend/src/services, create a file (e.g., thirdParty.js).
Write a function (e.g., callExternalAPI) that:
Accepts necessary parameters (e.g., from req.query).
Performs the request to the external API (using axios or similar).
Handle errors explicitly. If you use try/catch, log the error and rethrow.
Returns the API result.
5. Add validation and testing:
Implement any required input validation.
# Use Existing Components from Frontend Directory
1. When generating UI code, check if a reusable component already exists in the folder frontend/src/components/.
For example, review the BaseButton component in frontend/src/components/BaseButton.tsx:
Open the file and inspect its props, styling, and usage logic.
Make sure you understand how the component works and its expected parameters.
2. When writing new code, reference or import BaseButton (or other matching components) instead of creating new ones.
3. Maintain consistency across the project by reusing and possibly extending existing components where appropriate.
# Making Backend API Requests with Axios
1. In the \_app.tsx file, the Axios default baseURL is set for API (or falls back to a predefined baseURL). This baseURL already includes the /api/ prefix. This means all backend requests use the full base URL automatically.
2. When making API calls, only use the relative endpoint without including the /api/ prefix. For example, axios.get('/orders') or axios.post('/orders/deleteByIds', { data }).
Ensure error handling is in place.
STRICT RULE: Do NOT include the /api/ prefix in frontend Axios requests. This prefix is already handled automatically by axios.defaults.baseURL and adding it will result in incorrect URLs.
# Check Project Dependencies
To view frontend dependencies, open the file frontend/package.json.
To view backend dependencies, open the file backend/package.json.
Review the "dependencies" and "devDependencies" sections in each file to see the installed packages.
# Adding a New Language to the Application with next-i18next
1\. Create a Localization File for the New Language
Create a new JSON file inside the localization directory:
frontend/public/locales/{language\_code}/common.json
For example, if adding Italian (it):
frontend/public/locales/it/common.json
This file should have the same structure as frontend/public/locales/en/common.json (which is the default language file).
2\. Register the New Language in next-i18next.config.mjs
Open: frontend/next-i18next.config.mjs
Add the new language to the locales array:
export const i18n \= { defaultLocale: 'en', locales: \['fr', 'en', 'de', 'es', 'it'\], // Added Italian };
3\. Add the New Language to the Language Switcher Component
Open: frontend/src/components/LanguageSwitcher.tsx
Add the new language to the LANGS array:
const LANGS \= \[ { value: 'en', label: 'EN' }, { value: 'fr', label: 'FR' }, { value: 'es', label: 'ES' }, { value: 'de', label: 'DE' }, { value: 'it', label: 'IT' } // Added Italian \];
# Translating Content on a Page with next-i18next
1\. Import the Translation Hook
To translate any text within a page or component, first import useTranslation:
import { useTranslation } from 'next-i18next';
2\. Retrieve the Translation Function
Inside the component function, initialize the translation function by specifying the namespace:
const { t } \= useTranslation('common');
The 'common' namespace refers to the JSON translation file (e.g., frontend/public/locales/en/common.json).
3\. Use t() to Translate Text
Replace hardcoded text with t() calls:
{t('pages.dashboard.pageTitle')}
You can also set a default value:
t('pages.dashboard.pageTitle', { defaultValue: 'Overview' })
4\. Dynamic Text with Variables
If your translation includes placeholders (e.g., "Hello, {{name}}"), pass values as arguments:
t('greeting', { name: currentUser.firstName })
# How to change the styling of the app
1. Open frontend/src/styles.ts This file defines theme presets (like basic, white) as objects mapping UI elements (e.g., aside, cardsColor) to Tailwind CSS classes.
To change an existing theme: Locate the desired theme object and modify the Tailwind classes for specific properties (e.g., aside, navBarItemLabel, cardsColor).
2. Open frontend/src/colors.ts This file defines the color palette and how colors are applied to elements like buttons.
Modify lookup objects (e.g., colorsBgLight, colorsText, colorsOutline) to change color definitions.
Adjust the getButtonColor function logic and its internal lookup objects (e.g., colors.bgHover, colors.text) to alter button appearance based on color keys and states.
# How to change basic styling and layout within common components
To adjust hardcoded spacing, layout, sizes, or other base visual properties that are defined directly inside a component's file, you need to edit that component's source code. (Note: Theme-based colors, corners, focus rings, and color-specific styles like button colors are controlled elsewhere).
1. Open the .tsx file for the component you want to modify (e.g., frontend/src/components/BaseButton.tsx).
2. Locate the className strings or the logic that constructs them based on props or internal conditions.
3. Edit the Tailwind CSS classes directly within these strings, or modify the logic that adds/removes classes.
Here are specific areas to look at for each component:
* BaseDivider (BaseDivider.tsx): Look at the className on the \<hr\> tag and the classAddon logic. Edit classes like border-t (border style), my-4, \-mx-4 (margins), or conditional classes applied for navBar.
* BaseButtons (BaseButtons.tsx): Look at the className on the main div and the logic using type, mb, classAddon, noWrap props. Edit flex/layout classes (flex, items-center, justify-end, flex-wrap) or default margin values (mb, classAddon).
* CardBoxComponentBody (CardBoxComponentBody.tsx): Look at the className on the main div. Edit flex-1 or the conditional p-4 class applied when noPadding is false to change internal padding.
* BaseButton (BaseButton.tsx): Look at the componentClass array construction logic. Edit base classes like inline-flex, justify-center, items-center, whitespace-nowrap, text-sm, border, transition-colors, focus:ring, duration-100. Edit padding classes (p-0.5, py-1, px-2, px-4) and the logic determining them based on small, label, icon. Edit the rounded-full application logic. Edit opacity classes like opacity-50 or opacity-70 for disabled.
* CardBoxComponentFooter (CardBoxComponentFooter.tsx): Look at the className on the \<footer\>. Edit the padding class (p-4) to change internal padding.
* CardBoxComponentTitle (CardBoxComponentTitle.tsx): Look at className on the wrapper div and the \<h1\>. Edit wrapper layout/spacing classes (flex, items-center, justify-center, mb-2) or the title text size (text-xl).
* FormField (FormField.tsx): Look at className on wrapper divs, label, help text div, and the controlClassName string construction. Edit hardcoded margins (mb-4, mb-1, mt-0.5), label/help text size/spacing (text-sm, text-xs). Edit the elementWrapperClass logic for grid layouts. Edit base padding/height/border/background classes within controlClassName (e.g., px-2, py-1, max-w-full, w-full, h-20, h-9, border-gray-300, dark:border-dark-700). Edit conditional classes based on props like isBorderless, isTransparent, hasTextareaHeight, disabled, borderButtom. Edit hardcoded icon classes (positioning, size) on the BaseIcon.
# How to style layout components (NavBar, Aside, Footer, SectionMain, etc.)
Layout components like NavBar, AsideMenu, FooterBar, SectionMain, and their layers define the main structure and container styling of the application. Their styling is controlled by hardcoded classes, component props, theme properties defined in frontend/src/styles.ts, and some constants in frontend/src/config.ts.
* To change hardcoded layout, spacing, sizes, positioning, or basic visual traits within a component:
Open the component's .tsx file (e.g., frontend/src/components/NavBar.tsx, frontend/src/components/AsideMenuLayer.tsx, frontend/src/components/FooterBar.tsx, frontend/src/components/SectionMain.tsx). Locate the className strings on the HTML elements (\<nav\>, \<aside\>, \<footer\>, div wrappers, или главный wrapper в SectionMain) or logic based on props (like isAsideMobileExpanded, useMargin, display). Edit or add Tailwind CSS classes directly here (e.g., change h-12 to h-16 for Navbar height, modify py-1 px-4 for Footer padding, change w-48 to w-56 for Aside width, adjust fixed, absolute, hidden, block, left-0, \-left-48 positioning classes, или изменить внутренние отступы/маргины в SectionMain).
* To change layout background colors, aside styles, aside brand area styles, aside scrollbars, or navbar item text styles based on the current theme:
These styles are read from the application's theme state, defined in frontend/src/styles.ts. Open frontend/src/styles.ts. Find the relevant property (e.g., bgColor, asideStyle, asideBrandStyle, asideScrollbarsStyle, navBarItemLabelStyle, navBarItemLabelHoverStyle) within the theme objects (basic, white, etc.). Modify the Tailwind classes assigned to that property. Layout components reading these properties will update when the theme changes.
* To change the maximum width of main content containers (used in NavBar and Footer, and potentially wrapping content within SectionMain):
This is controlled by a constant in the configuration file. Open frontend/src/config.ts. Modify the containerMaxW constant (e.g., change 'container lg:mx-auto px-6' to max-w-screen-xl mx-auto px-4).
* To apply custom styles from where a component is used (overriding default styles):
Layout components like NavBar, AsideMenuLayer, FooterBar, SectionMain often accept a className prop. Add Tailwind CSS classes or custom CSS class names to the className prop when using the component in layouts like LayoutAuthenticated or directly on pages.
# How to create a public page (without authentication)
Goal: Create a new page accessible without requiring login, using a clean URL path.
1. Create the Page File Create a new file in the frontend/src/pages/web\_pages/ directory. Name it appropriately, for example, my-public-page.tsx. The default direct access path will be /web\_pages/my-public-page.
2. Add the Basic Page Structure Copy the following basic structure into your new file. This includes necessary imports, the page component structure with Header, Footer, and main content area, and the assignment of the public layout (LayoutGuest).
TypeScript
import React from 'react';
import type { ReactElement } from 'react';
import Head from 'next/head';
// You might need useAppSelector if Header/Footer use global state (e.g., project name)
// import { useAppSelector } from '../../stores/hooks'; // Adjust path if needed
import LayoutGuest from '../../layouts/Guest'; // Public Layout \- Adjust path if needed
import WebSiteHeader from '../../components/WebPageComponents/Header'; // Adjust path if needed
import WebSiteFooter from '../../components/WebPageComponents/Footer'; // Adjust path if needed
// \--- Customize: Change function name (e.g., MyPublicPage) \---
export default function PageName() {
// Example of getting project name, if needed (adjust path if using useAppSelector)
// const projectName \= useAppSelector((state) \=\> state.style.projectName);
const projectName \= 'Your Project Name'; // Customize or get from state/config
// Optional: Example useEffect for client-side logic
// useEffect(() \=\> { /\* ... \*/ }, \[\]);
// Example data for Header/Footer, if menu items are needed
// const pages \= \[ /\* ... \*/ \];
return (
\<\>
\<Head\>
{/\* Customize: Page Title and Meta Description \*/}
\<title\>Your Public Page Title\</title\>
\<meta
name="description"
content="A description for your public page."
/\>
\</Head\>
{/\* Customize: Pass necessary props to Header/Footer \*/}
\<WebSiteHeader projectName={projectName} /\* pages={pages} \*/ /\>
\<main className="flex-grow"\>{/\* Your page content goes here \*/}\</main\>
{/\* Customize: Pass necessary props to Footer \*/}
\<WebSiteFooter projectName={projectName} /\* pages={pages} \*/ /\>
\</\>
);
}
// \--- IMPORTANT: Assign Guest Layout \---
// This line makes the page public (accessible without authentication)
PageName.getLayout \= function getLayout(page: ReactElement) {
return \<LayoutGuest\>{page}\</LayoutGuest\>; // Use LayoutGuest
}`;`
(Note: Ensure import paths for LayoutGuest, WebSiteHeader, WebSiteFooter, and potentially useAppSelector are correct relative to the pages/web\_pages/ directory. Replace PageName with your actual component name).
3. Add Page Content Add the actual content for your public page inside the \<main\>{/\* Your page content goes here \*/}\</main\> tags.
4. Add Rewrite Rule To access your page via a cleaner URL path (e.g., /my-public-page instead of /web\_pages/my-public-page), add a rewrite rule in next.config.mjs.
* Open frontend/next.config.mjs.
* Inside async rewrites(), add a new object to the array:
JavaScript
async rewrites() {
return \[
// ... existing rules ...
{
source: '/my-public-page', // \<-- Clean URL path you want
destination: '/web\_pages/{file-name}', // \<-- Actual location of the page file (without .tsx)
},
// ...
\];
},
*
* Replace /my-public-page and /web\_pages/{file-name} with the actual desired paths and file location (use the file name without .tsx in the destination).
5. Add a link to public navigation If you want this page to appear in the header/footer navigation for public pages, edit the file frontend/src/menuNavBar.ts. Locate the webPagesNavBar array and add a new object to it. Use the source path from your rewrite rule for the href:
TypeScript
export const webPagesNavBar \= \[
// ... existing links ...
{
href: '/my-public-page', // \<-- Use the 'source' path from your rewrite rule
label: 'Your Page Label',
},
//
\];
# Public Read Access for Entity Data
To allow unauthenticated users to read data for a specific entity (e.g., Courses), you need to adjust the backend router, the database permissions, and the frontend fetch logic.
1. Backend: Remove Auth Middleware
In backend/src/index.js, remove passport.authenticate from the specific entity's router definition.
Change in backend/src/index.js:
JavaScript
```
// Before:
// app.use('/api/courses', passport.authenticate('jwt', { session: false }), coursesRoutes);
// After:
app.use('/api/courses', coursesRoutes);
```
2. Backend (DB): Grant Public READ Permission
In your roles/permissions database migration, add the READ\_\<ENTITY\_NAME\> permission for the Public role. Replace \<ENTITY\_NAME\> (e.g., COURSES).
Change in Migration File (conceptual):
JavaScript
```
// In the bulkInsert for rolesPermissionsPermissions table:
{ createdAt, updatedAt, roles_permissionsId: getId("Public"), permissionId: getId('READ_COURSES') }, // Add this line
```
3. Frontend: Fetch Data
In your public page component, use axios.get (or Workspace) with the relative path to the entity endpoint.
Change in Frontend Component:
JavaScript
```
import axios from 'axios'; // or import fetch
useEffect(() => {
axios.get('/courses') // Use relative path without '/api/'
.then(response => {
// Handle { rows: [...], count: ... } format
const dataList = Array.isArray(response.data.rows) ? response.data.rows : [];
// Use dataList...
})
.catch(err => console.error(err));
}, []);
```
# Create a Public Entity Details Page (View by ID)
Create a public page accessible without authentication to display the details of a single entity item, fetched from the backend using its ID provided in the URL query parameter (e.g., /public/users-details?id=abc-123).
Steps:
1. Backend: Make the GET /api/\[EntityName\]/:id Endpoint Public
* Explanation: The backend route used to fetch a single item by its ID needs to allow unauthenticated requests.
* Action:
* In backend/src/index.js, identify the router for your entity (e.g., usersRoutes for /api/users).
* Remove the passport.authenticate middleware from this specific router if you want any requests to /api/\[EntityName\] (including GET by ID) to bypass JWT auth. Be cautious: This makes all endpoints under /api/\[EntityName\] potentially accessible. A more granular approach might be needed if only GET is public.
* Ensure your checkCrudPermissions('entityName') middleware is applied to this router.
* In your database migration file for roles/permissions, grant the READ\_\<ENTITY\_NAME\> permission to the Public role. Our modified checkPermissions middleware will handle the authorization check based on the 'Public' role when the user is unauthenticated.
* Location: backend/src/index.js and Database Migration file (backend/src/db/seeders/...).
2. JavaScript
```javascript
// --- Backend: index.js (Example for users) ---
// Find this section:
// app.use(
// '/api/users',
// passport.authenticate('jwt', { session: false }), // <-- REMOVE THIS LINE
// usersRoutes, // <-- Assuming checkCrudPermissions('users') is inside usersRoutes or applied here
// );
// Change to:
app.use(
'/api/users',
usersRoutes, // <-- Now accessible without JWT auth (permissions check still applies)
);
// --- Backend (DB): Migration file (Example for READ_USERS) ---
// In the bulkInsert for rolesPermissionsPermissions table, add:
{ createdAt, updatedAt, roles_permissionsId: getId("Public"), permissionId: getId('READ_USERS') }, // <--- Add this line
```
3. Frontend: Create the Public Page File
* Explanation: Create a new Next.js page file for the entity details. Place it in a directory intended for public pages (e.g., pages/public/) to keep public routes organized and separate from authenticated ones.
* Location: frontend/src/pages/public/\[EntityName\]-details.tsx (Replace \[EntityName\] with your entity name, e.g., users-details.tsx).
* Action: Create the file and add the basic structure using LayoutGuest.
4. TypeScript
```javascript
// frontend/src/pages/public/[EntityName]-details.tsx (Example for users-details.tsx)
import React, { useEffect, useState } from 'react';
import type { ReactElement } from 'react';
import Head from 'next/head';
import { useRouter } from 'next/router';
// Import your fetch logic (e.g., using Redux/ Zustand actions or a simple axios call)
// import { useDispatch, useAppSelector } from '../../stores/hooks'; // If using Redux/Zustand
// import { fetchUserById } from '../../stores/users/usersSlice'; // Example action
import LayoutGuest from '../../layouts/Guest'; // Use the public layout
// Import Header/Footer components for public pages if needed
// import WebSiteHeader from '../../components/WebPageComponents/Header';
// import WebSiteFooter from '../../components/WebPageComponents/Footer';
// --- Customize: Change function name and component name ---
export default function EntityDetailsPage() { // Example: UsersDetailsPage
const router = useRouter();
// Get the ID from the URL query parameters
// router.query.id might be undefined initially on client-side render
const { id } = router.query;
// --- Customize: Add state for fetching/data ---
// If using Redux/Zustand:
// const dispatch = useDispatch();
// const { entityData, loading, error } = useAppSelector(state => state.yourEntitySlice); // Adapt selector
// If using simple state with axios:
const [entityData, setEntityData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
// import axios from 'axios'; // Need axios
// --- Customize: Fetch data when ID is available ---
useEffect(() => {
// Fetch data only if ID is present and is a string
if (id && typeof id === 'string') {
console.log(`Workspaceing entity with ID: ${id}`); // Debugging
// --- Customize: Your data fetching logic ---
// Example using Redux/Zustand dispatch:
// dispatch(fetchUserById({ id })); // Assuming this action fetches and updates state
// Example using simple axios:
const fetchItem = async () => {
try {
setLoading(true);
setError(null);
// Call the backend GET /api/[entityName]/:id endpoint
// Use relative path for axios - it adds the baseURL (pointing to backend)
const response = await axios.get(`/users/${id}`); // <-- Adjust endpoint path
const data = response.data; // Assuming backend returns the item object directly for /:id
setEntityData(data);
} catch (err) {
console.error("Failed to fetch entity:", err);
setError(err); // Store error state
} finally {
setLoading(false); // Always set loading to false
}
};
fetchItem(); // Execute the async fetch function
} else if (!id) {
// Handle case where ID is missing in the URL
setLoading(false); // Stop loading state
// Optionally set an error or redirect
// setError(new Error("Entity ID is missing in the URL."));
console.warn("Entity ID is missing in the URL query.");
// Maybe redirect to a list page or 404 page
// router.push('/public/users'); // Example redirect
}
// Depend on 'id' and dispatch (if using dispatch)
}, [id]); // Re-run effect if ID changes
// --- Customize: Display Loading, Error, or Data ---
if (loading) {
return (
<>
<Head><title>Loading...</title></Head>
{/* <WebSiteHeader /> */}
<main className="flex-grow p-4"><p>Loading entity details...</p></main>
{/* <WebSiteFooter /> */}
</>
);
}
if (error) {
return (
<>
<Head><title>Error</title></Head>
{/* <WebSiteHeader /> */}
<main className="flex-grow p-4"><p>Error loading details: {error.message}</p></main>
{/* <WebSiteFooter /> */}
</>
);
}
// If data is loaded and exists, display it
if (!entityData) {
return (
<>
<Head><title>Not Found</title></Head>
{/* <WebSiteHeader /> */}
<main className="flex-grow p-4"><p>Entity not found.</p></main>
{/* <WebSiteFooter /> */}
</>
);
}
return (
<>
<Head>
{/* Customize: Page Title */}
<title>{`Details: ${entityData.name || entityData.title || 'Unnamed Entity'}`}</title> {/* Example title from data */}
</Head>
{/* <WebSiteHeader /> */}
<main className="flex-grow p-4"> {/* Add padding */}
<h1>{/* Customize: Display entity title/name */}</h1>
{/* --- Customize: Display other entity details --- */}
<div>ID: {entityData.id}</div>
{/* Example: */}
{/* <div>Name: {entityData.name}</div> */}
{/* <div>Description: {entityData.description}</div> */}
{/* ... other fields ... */}
</main>
{/* <WebSiteFooter /> */}
</>
);
}
// --- IMPORTANT: Assign Guest Layout ---
// This line makes the page public (accessible without authentication)
EntityDetailsPage.getLayout = function getLayout(page: ReactElement) { // Example: UsersDetailsPage.getLayout
return <LayoutGuest>{page}</LayoutGuest>; // Use LayoutGuest
};
// Remove this line if using getLayout assignment below
// export default EntityDetailsPage; // Example: export default UsersDetailsPage;
```
5. Frontend: Add Link to the Page (Optional)
* Explanation: If you have a list page or another place where you want to link to the details of a specific item, create a link passing the item's ID as a query parameter.
* Location: The page or component where the list is displayed (e.g., a list page, a card component).
* Action: Use the Link component from next/link and provide the path to your new details page with the id in the query object.
6. TypeScript
```javascript
// Example in a list item rendering
import Link from 'next/link';
// Assuming 'item' is an object from your list fetch
// Adjust href path to match your new details page file location
<Link href={{ pathname: '/public/users-details', query: { id: item.id } }}>
<a>{item.name || item.title}</a> {/* Link text */}
</Link>
```
Now, when you navigate to /public/users-details?id=YOUR\_ENTITY\_ID, the page will fetch and display the details for that specific entity, and it will be accessible without requiring a login due to the LayoutGuest. Remember to replace placeholders like \[EntityName\], users-details.tsx, WorkspaceUserById, /users/${id}, and the data display logic with your actual entity's details.
# How to add AI functionality
Purpose: Generate custom text, creative content, or ideas using AI within your application.
Handle User-Provided API Key from Chat:
\!\!\!The first step is to ask the user for the key if they don't have it yet. Do not follow any further steps without the key\!\!\!
* Ask the user for the API key. Save it to the file backend/src/config.js under the name gpt\_key if the key is not already present there.
How I Assist: I will provide you with the specific text prompt string you need to send to the AI.
How You Use It in Code (Triggered on Component Load):
1. Import: Import the necessary action, hooks, and useEffect:
TypeScript
```javascript
import React, { useEffect } from 'react'; // Import useEffect
import { useAppDispatch, useAppSelector } from '../stores/hooks'; // Import Redux hooks
import { askGpt } from '../stores/openAiSlice'; // Import the GPT action
```
2. Setup & State: Get the dispatch function and select the relevant state from your openAiSlice:
TypeScript
```javascript
// Inside your functional component
const dispatch = useAppDispatch();
const { gptResponse, isAskingQuestion, errorMessage } = useAppSelector((state) => state.openAi);
```
3. Define Prompt: Define the prompt string you want to send.
TypeScript
```javascript
const promptForAchievement = "Write a short (max 20 words), inspiring, and plausible 'achievement of the day' related to productivity or learning.";
```
4. Use useEffect to Dispatch: Use the useEffect hook to dispatch the action when the component mounts.
TypeScript
```javascript
useEffect(() => {
// Dispatch the askGpt action with your prompt
dispatch(askGpt(promptForAchievement));
// The empty dependency array [] means this effect will run
// only once after the initial render (on mount).
// In development with React Fast Refresh, this effect might run twice.
}, [dispatch]); // Add dispatch to dependencies (React hooks recommendation)
```
5. Display Result: Use the state variables (isAskingQuestion, gptResponse, errorMessage) in your component's JSX to show loading status, the generated text, or any errors.
\<\!-- end list \--\>
TypeScript
```javascript
// Example Component Structure (AchievementOfTheDay.tsx)
import React, { useEffect } from 'react';
import { useAppDispatch, useAppSelector } from '../stores/hooks';
import { askGpt } from '../stores/openAiSlice';
function AchievementOfTheDay() {
const dispatch = useAppDispatch();
const { gptResponse, isAskingQuestion, errorMessage } = useAppSelector((state) => state.openAi);
// Define the prompt
const achievementPrompt = "Write a short (max 20 words), inspiring, and plausible 'achievement of the day' related to productivity or learning.";
// Use useEffect to trigger the API call on component mount
useEffect(() => {
dispatch(askGpt(achievementPrompt));
// Empty dependency array means run once on mount
}, [dispatch]); // Dependency array includes dispatch
return (
<div>
<h3>Your Achievement of the Day:</h3>
{/* Display loading state */}
{isAskingQuestion && <p>Generating achievement...</p>}
{/* Display the result if received */}
{gptResponse && <p>{gptResponse}</p>}
{/* Display error if any */}
{errorMessage && <p style={{ color: 'red' }}>{errorMessage}</p>}
</div>
);
}
export default AchievementOfTheDay; // Export your component
```
Important Notes:
* useEffect Dependencies: The dependency array \[\] makes the effect run once on mount. If you need the request to trigger based on changes to props or state (e.g., a user ID changing), include those variables in the dependency array (e.g., \[dispatch, userId\]).
* Fast Refresh: In development mode with Fast Refresh, effects with \[\] might run twice. This is expected development behavior.
* Controlling Calls: Be mindful of where you put this useEffect. If placed in a component that renders frequently or in \_app.tsx or a layout without careful conditions, it could lead to many unintended API calls. Dispatch only when necessary.
More Prompt Examples (Ideas for what you can ask me to generate a prompt for):
Here are more ideas for features where you could use this tool, and the type of prompt you would ask me to create for you to use in your askGpt dispatch:
* Generate a Product Description: Ask me for a prompt to draft a description for an item.
Prompt Idea: "Write a concise product description (max 60 words) for a \[Product Category\] highlighting \[Key Feature\] and \[Main Benefit\]."
* Suggest User Profile Interests: Ask me for a prompt to suggest interests based on a theme.
* Prompt Idea: "Suggest 5 hobbies or interests related to 'outdoor activities' suitable for a user profile."
* Create a Welcome Email Subject Line: Ask me for a prompt to generate email subject lines.
* Prompt Idea: "Write 3 catchy and welcoming subject lines for a new user registration confirmation email."
* Explain a Concept Simply: Ask me for a prompt to get a simple explanation of something.
* Prompt Idea: "Explain the concept of 'asynchronous programming' in simple terms, suitable for someone new to coding."
* Generate Test Data (Text Format): Ask me for a prompt to generate sample text data.
* Prompt Idea: "Generate a sample short user review for a hypothetical product, commenting on its 'ease of use'."
* Draft an Error Message: Ask me for a prompt to write a user-friendly error message.
* Prompt Idea: "Write a helpful and polite error message for when a file upload fails because the file is too large."
Now, just tell me what kind of text or idea you need, specify that you want it for triggering on component load (if that's the case), and I'll provide the prompt string for you to use in your useEffect dispatch\!