diff --git a/docs/en/Adding-New-Metronic-Theme-React.md b/docs/en/Adding-New-Metronic-Theme-React.md new file mode 100644 index 00000000..0d8fe904 --- /dev/null +++ b/docs/en/Adding-New-Metronic-Theme-React.md @@ -0,0 +1,229 @@ +# Adding New Metronic Theme + +Metronic theme currently has 13 different themes and ASP.NET Zero includes them all. However, you might want to add a new theme option designed by your team to those options. This document explains step by step how to add a new theme option to ASP.NET Zero React UI. Note that the added theme must be a Metronic theme or at least be compatible with Metronic. + +The rest of this document will use **ThemeX** as the new theme name. + +--- + +## .NET Part + +* Go to `*.Application.Shared` project, open `AppConsts.cs` and add a new field named `ThemeX`. + +* Go to `*.Web.Core` project: + + - Create a new UI Customizer named `ThemeXUiCustomizer.cs`. Copy `ThemeDefaultUiCustomizer.cs` into `ThemeXUiCustomizer.cs` and change necessary settings. (It has setting methods; if your new ThemeX also has those settings, keep them; otherwise, delete them.) + + - Open `UiThemeCustomizerFactory.cs` and add the ThemeX code in the `GetUiCustomizerInternal` function: + + ```csharp + if (theme.Equals(AppConsts.ThemeX, StringComparison.InvariantCultureIgnoreCase)) + { + return _serviceProvider.GetService(); + } + ``` + +* Go to `*.Core` project. Open `AppSettingProvider.cs`: + + - Add a method named `GetThemeXSettings` which returns ThemeX settings. + + - Call it in the `GetSettingDefinitions` function: + + ```csharp + return GetHostSettings().Union(GetTenantSettings()).Union(GetSharedSettings()) + // theme settings + .Union(GetDefaultThemeSettings()) + .Union(GetTheme2Settings()) + // ... other themes ... + .Union(GetTheme13Settings()) + .Union(GetThemeXSettings()); // add ThemeXSettings + ``` + +--- + +## React Part + +### 1. Add Theme Assets + +Add your theme's CSS bundle files to the `metronic/themes/` folder: + +``` +metronic/themes/themeX/ +├── css/ +│ └── style.bundle.css +└── plugins/ + └── global/ + └── plugins.bundle.css +``` + +Also add a customization CSS file: + +``` +public/assets/common/styles/themes/themeX/ +└── metronic-customize.css +``` + +### 2. Create Theme Layout Components + +Go to `src/pages/admin/components/layout/themes/` folder: + +1. Create a new folder named `themeX/` +2. Create the following React components inside the folder: + - `ThemeXLayout.tsx` - Main layout component + - `ThemeXHeader.tsx` - Header component + - `ThemeXBrand.tsx` - Brand/logo component + +Copy the contents from the default theme components (`default/DefaultLayout.tsx`, `default/DefaultHeader.tsx`, `default/DefaultBrand.tsx`) and modify as needed. + +**Example `ThemeXLayout.tsx`:** + +```tsx +import React, { useEffect } from "react"; +import { Outlet } from "react-router-dom"; +import ThemeXHeader from "./ThemeXHeader"; +import { Footer } from "../../Footer"; +import { useTheme } from "@/hooks/useTheme"; +import { SidebarMenu } from "../../sidebar-menu/SidebarMenu"; +import ThemeXBrand from "./ThemeXBrand"; + +const ThemeXLayout: React.FC = () => { + const { containerClass } = useTheme(); + + useEffect(() => { + document.body.classList.add("app-themeX"); + return () => { + document.body.classList.remove("app-themeX"); + }; + }, []); + + return ( + // ... your layout JSX + ); +}; + +export default ThemeXLayout; +``` + +### 3. Register the Theme Layout + +Open `src/pages/admin/components/layout/themes/index.tsx` and add your theme: + +1. Import the new layout component: + + ```tsx + import ThemeXLayout from "./themeX/ThemeXLayout"; + ``` + +2. Add the case to the switch statement in `ThemedLayout`: + + ```tsx + const ThemedLayout: React.FC = () => { + const activeThemeName = useSelector( + (s: RootState) => s.ui.activeThemeName || "default", + ); + + switch (activeThemeName) { + // ... existing cases ... + case "themeX": + return ; + case "default": + default: + return ; + } + }; + ``` + +### 4. Add Theme to Selection Panel + +Open `src/pages/admin/components/layout/theme-selection/ThemeSelectionPanel.tsx` and add your theme to the `THEMES` array: + +```tsx +const THEMES: string[] = [ + "default", + "theme2", + "theme3", + // ... other themes ... + "theme13", + "themeX", // Add your new theme +]; +``` + +### 5. Create UI Customization Settings Form + +Go to `src/pages/admin/ui-customization/components/` folder: + +1. Create `ThemeXSettingsForm.tsx` by copying from `DefaultThemeSettingsForm.tsx` +2. Modify the form fields based on your theme's available settings + +**Example `ThemeXSettingsForm.tsx`:** + +```tsx +import React from "react"; +import { Form, Select, Switch } from "antd"; +import type { ThemeSettingsDto } from "@/api/generated/service-proxies"; +import L from "@/lib/L"; + +interface Props { + settings: ThemeSettingsDto; + onSave: (values: ThemeSettingsDto) => void; +} + +const ThemeXSettingsForm: React.FC = ({ settings, onSave }) => { + const [form] = Form.useForm(); + + // ... form implementation +}; + +export default ThemeXSettingsForm; +``` + +### 6. Register the Settings Form + +Open `src/pages/admin/ui-customization/index.tsx`: + +1. Import your settings form: + + ```tsx + import ThemeXSettingsForm from "./components/ThemeXSettingsForm"; + ``` + +2. Add the form to the theme panel rendering logic (in the switch or conditional rendering section). + +### 7. Add Theme Preview Image + +Add a preview image for your theme: + +``` +public/assets/common/images/metronic-themes/themeX.png +``` + +This image is displayed in the UI Customization page for theme selection. + +### 8. Add Localization + +Add localization entries for your theme name in the localization files: + +```json +{ + "Theme_ThemeX": "Theme X" +} +``` + +--- + +## Summary of Files to Create/Modify + +| Action | File Path | +|--------|-----------| +| Create | `metronic/themes/themeX/css/style.bundle.css` | +| Create | `metronic/themes/themeX/plugins/global/plugins.bundle.css` | +| Create | `public/assets/common/styles/themes/themeX/metronic-customize.css` | +| Create | `src/pages/admin/components/layout/themes/themeX/ThemeXLayout.tsx` | +| Create | `src/pages/admin/components/layout/themes/themeX/ThemeXHeader.tsx` | +| Create | `src/pages/admin/components/layout/themes/themeX/ThemeXBrand.tsx` | +| Modify | `src/pages/admin/components/layout/themes/index.tsx` | +| Modify | `src/pages/admin/components/layout/theme-selection/ThemeSelectionPanel.tsx` | +| Create | `src/pages/admin/ui-customization/components/ThemeXSettingsForm.tsx` | +| Modify | `src/pages/admin/ui-customization/index.tsx` | +| Create | `public/assets/common/images/metronic-themes/themeX.png` | + diff --git a/docs/en/Customization-React.md b/docs/en/Customization-React.md new file mode 100644 index 00000000..a1a30afe --- /dev/null +++ b/docs/en/Customization-React.md @@ -0,0 +1,22 @@ +## Customization + +### Changing Logos + +The default template has 5 main logos and they are located in `public/assets/common/images/`: + +1. `app-logo-on-dark.svg` - Full logo for dark backgrounds +2. `app-logo-on-dark-sm.svg` - Small logo for dark backgrounds +3. `app-logo-on-light.svg` - Full logo for light backgrounds +4. `app-logo-on-light-sm.svg` - Small logo for light backgrounds +5. `logo.svg` - General purpose logo + +**Naming convention:** + +- `*-dark*` versions are used on **dark backgrounds** (light-colored themes use these) +- `*-light*` versions are used on **light backgrounds** (dark-colored themes use these) +- `*-sm.svg` versions are smaller variants used in footers or collapsed sidebars + +**Additional variants:** + +The project also includes alternative logo versions (`app-logo-on-dark-2.svg`, `app-logo-on-dark-sm-2.svg`) that can be used for different branding scenarios. + diff --git a/docs/en/Deleting-A-Metronic-Theme-React.md b/docs/en/Deleting-A-Metronic-Theme-React.md new file mode 100644 index 00000000..a5224537 --- /dev/null +++ b/docs/en/Deleting-A-Metronic-Theme-React.md @@ -0,0 +1,109 @@ +# Deleting a Metronic Theme + +Metronic theme currently has 13 different themes and ASP.NET Zero includes them all. However, you might want to use only specific themes and delete some others. This document explains how to delete a theme option from ASP.NET Zero. In this document, deleting **Theme2** will be explained. You can apply the same steps to delete other theme options. + +--- + +## .NET Part + +* Go to `*.Application.Shared` project. Open `AppConsts.cs` and delete the `Theme2` field. + +* Go to `*.Web.Core` project: + * Delete `Theme2UiCustomizer.cs` + * Open `UiThemeCustomizerFactory.cs` and delete Theme2 code parts in `GetUiCustomizerInternal` function. + +* Go to `*.Core` project. Open `AppSettingProvider.cs` and delete `GetTheme2Settings` function and its call in `GetSettingDefinitions`. + +--- + +## React Part + +### 1. Delete Theme Layout Components + +Go to `src/pages/admin/components/layout/themes/` folder: + +* Delete the `theme2/` folder entirely (contains `Theme2Layout.tsx`, `Theme2Header.tsx`, `Theme2Brand.tsx`) + +### 2. Update Theme Layout Registry + +Open `src/pages/admin/components/layout/themes/index.tsx`: + +* Remove the import statement: + ```tsx + import Theme2Layout from "./theme2/Theme2Layout"; + ``` + +* Remove the case from the switch statement: + ```tsx + case "theme2": + return ; + ``` + +### 3. Update Theme Selection Panel + +Open `src/pages/admin/components/layout/theme-selection/ThemeSelectionPanel.tsx`: + +* Remove `"theme2"` from the `THEMES` array: + ```tsx + const THEMES: string[] = [ + "default", + // "theme2", // Remove this line + "theme3", + // ... + ]; + ``` + +### 4. Delete UI Customization Settings Form + +Go to `src/pages/admin/ui-customization/components/` folder: + +* Delete `Theme2SettingsForm.tsx` + +### 5. Update UI Customization Page + +Open `src/pages/admin/ui-customization/index.tsx`: + +* Remove the import statement: + ```tsx + import Theme2SettingsForm from "./components/Theme2SettingsForm"; + ``` + +* Remove any Theme2-related rendering logic in the component. + +### 6. Delete Theme Assets (Optional) + +If you want to reduce bundle size, delete the theme assets: + +* Delete `metronic/themes/theme2/` folder +* Delete `public/assets/common/styles/themes/theme2/` folder +* Delete `public/assets/common/images/metronic-themes/theme2.png` + +--- + +## Database Cleanup + +If you are deleting a theme on an already published application, don't forget to delete records in the `AbpSettings` table: + +* Records where `Name` equals `App.UiManagement.Theme` and `Value` equals `theme2` +* Records where `Name` starts with `Theme2.` + +```sql +DELETE FROM AbpSettings WHERE Name = 'App.UiManagement.Theme' AND Value = 'theme2'; +DELETE FROM AbpSettings WHERE Name LIKE 'Theme2.%'; +``` + +--- + +## Summary of Files to Delete/Modify + +| Action | File Path | +|--------|-----------| +| Delete | `src/pages/admin/components/layout/themes/theme2/` (entire folder) | +| Modify | `src/pages/admin/components/layout/themes/index.tsx` | +| Modify | `src/pages/admin/components/layout/theme-selection/ThemeSelectionPanel.tsx` | +| Delete | `src/pages/admin/ui-customization/components/Theme2SettingsForm.tsx` | +| Modify | `src/pages/admin/ui-customization/index.tsx` | +| Delete | `metronic/themes/theme2/` (optional) | +| Delete | `public/assets/common/styles/themes/theme2/` (optional) | +| Delete | `public/assets/common/images/metronic-themes/theme2.png` (optional) | + diff --git a/docs/en/Deployment-React-Docker.md b/docs/en/Deployment-React-Docker.md new file mode 100644 index 00000000..932e753f --- /dev/null +++ b/docs/en/Deployment-React-Docker.md @@ -0,0 +1,106 @@ +# Publishing to Docker Containers + +ASP.NET Zero solution has a **build folder** which contains scripts to **build & publish** your solution to the **output** folder. + +## Prerequisites + +* Node.js (LTS version recommended) +* Docker Desktop installed and running +* npm packages installed (`npm install`) + +## Building the React Application + +Before creating a Docker image, build the React application: + +```bash +npm run build +``` + +This will create a production build in the `dist/` folder using Vite. + +## Creating a Docker Image + +### Sample Dockerfile + +Create a `Dockerfile` in the project root: + +```dockerfile +# Build stage +FROM node:20-alpine AS build +WORKDIR /app +COPY package*.json ./ +RUN npm ci +COPY . . +RUN npm run build + +# Production stage +FROM nginx:alpine +COPY --from=build /app/dist /usr/share/nginx/html +COPY nginx.conf /etc/nginx/conf.d/default.conf +EXPOSE 80 +CMD ["nginx", "-g", "daemon off;"] +``` + +### Sample nginx.conf + +Create an `nginx.conf` file for SPA routing: + +```nginx +server { + listen 80; + server_name localhost; + root /usr/share/nginx/html; + index index.html; + + location / { + try_files $uri $uri/ /index.html; + } +} +``` + +### Building and Running + +Build the Docker image: + +```bash +docker build -t aspnetzero-react . +``` + +Run the container: + +```bash +docker run -d -p 4200:80 aspnetzero-react +``` + +## Docker Compose + +For running with Docker Compose, create a `docker-compose.yml`: + +```yaml +version: '3.8' +services: + react-app: + build: . + ports: + - "4200:80" + environment: + - NODE_ENV=production +``` + +Run with: + +```bash +docker compose up -d +``` + +## Configuration + +Before building the Docker image, ensure your API endpoint is correctly configured. Update the remote service URL in your environment configuration to point to your production API server. + +## Notes + +* The React UI is a static SPA that connects to the ASP.NET Core backend API +* Ensure CORS is properly configured on your backend to allow requests from the React container +* For production deployments, consider using HTTPS with proper SSL certificates + + diff --git a/docs/en/Deployment-React-Publish-Azure.md b/docs/en/Deployment-React-Publish-Azure.md new file mode 100644 index 00000000..049e910f --- /dev/null +++ b/docs/en/Deployment-React-Publish-Azure.md @@ -0,0 +1,167 @@ +# Step By Step Publish To Azure + +## Introduction + +Before reading this document, it's suggested to read [Getting Started](Getting-Started-React) to run the application and explore the user interface. This will help you to have a better understanding of concepts defined here. + +## Create The Azure Resources + +ASP.NET Zero's React client app and server-side Web.Host API app can be published together or separately. In this document, we will publish both apps separately. + +Go to your Azure Portal and create two resources: +- **Azure App Service** for the Web.Host API project +- **Azure Static Web App** or **App Service** for the React application + +### Creating an Azure App Service for Host API + +On Azure portal menu, go to App Services and click "Create App Service" button. Fill the form correctly and create the app service for the API. + + + +### Creating an Azure Resource for React + +For the React application, you have two options: + +1. **Azure Static Web Apps** (Recommended for SPAs) - Optimized for static content with built-in CI/CD +2. **Azure App Service** - Traditional web app hosting + +## Create the Database Service + +On Azure portal, go to SQL databases menu and create a new empty database. If you haven't created a new Server on Azure, click "create new" link under the Server selection combobox and first create a Server. + + + +## Publish Host Application to Azure + +Here are the quick steps to publish the **Host Application** to Azure: + +1. Run the migrations on Azure +2. Configure the `.Web.Host/appsettings.production.json` +3. Publish the application to Azure + +### Run Migrations on Azure + +One of the best ways to run migrations on Azure is running `update-database` command in Visual Studio. +In order to do that, your public IP address should have access to the Azure SQL Database. + +#### Configuring the Firewall for Client Access + +**The easiest way:** Open SQL Server Management Studio and enter the Azure database settings, then click connect. +If you are already logged in to Azure, the following info screen will be shown: + + + +Now your client IP address has access to Azure. This operation can also be done via the [Azure Portal](https://portal.azure.com). Check [here](https://docs.microsoft.com/en-us/azure/sql-database/sql-database-firewall-configure) to learn how to configure the firewall for client access via Azure Portal. + +#### Apply Migrations + +Open **appsettings.json** in **.Web.Host** project and change connection settings according to the Azure Database: + + + +Open Package Manager Console in Visual Studio, set **.EntityFrameworkCore** as the Default Project and run the `update-database` command as shown below: + + + +As an alternative, you can use the Migrator project in your solution instead of running Update-Database command. It is suggested to use the Migrator project for migration operations. + +### Configure appsettings.production.json + +Since your application will run in Production environment, Azure will use **appsettings.production.json** that is placed in **Web.Host**, so this file should be configured like following: + + + +- `ServerRootAddress`: The URL of your Host application on Azure +- `ClientRootAddress`: Your React UI's URL on Azure +- `CorsOrigins`: Allowed websites that can make requests to your Host application. Must include your React app's URL. + +### Publish + +Right click the **Web.Host** project and select "**Publish**". Select "**Azure**" and follow the wizard to publish to your App Service. + + + +## Publish React to Azure + +Here are the steps to publish the React application to Azure: + +### 1. Configure Environment Variables + +The React application uses Vite environment variables for configuration. Create or update the `.env.production` file in your project root: + +```env +VITE_API_URL=https://your-host-api.azurewebsites.net +``` + +This URL should point to your published Host API application. + +### 2. Build the Application + +Run the following commands to build the React application: + +```bash +npm install +npm run build +``` + +This will create a production build in the `dist/` folder. + +### 3. Deploy to Azure + +#### Option A: Azure Static Web Apps (Recommended) + +1. In Azure Portal, create a new Static Web App +2. Connect it to your Git repository +3. Configure the build settings: + - **App location**: `/` + - **Output location**: `dist` + - **Build command**: `npm run build` + +Azure will automatically build and deploy your app on each push. + +#### Option B: Azure App Service with FTP + +1. Create a `web.config` file in the `dist/` folder for IIS URL rewriting: + +```xml + + + + + + + + + + + + + + + + + + + + +``` + +2. Upload all files from the `dist/` folder to the `wwwroot` folder on Azure via FTP or Azure Portal. + + + +### 4. Verify Deployment + +Browse to your React application URL (e.g., `https://your-react-app.azurewebsites.net`) and verify it works correctly. + + + +## Summary + +| Component | Azure Resource | Configuration | +|-----------|---------------|---------------| +| Host API | App Service | `appsettings.production.json` | +| React UI | Static Web App or App Service | `.env.production` with `VITE_API_URL` | +| Database | Azure SQL Database | Connection string in Host API | + + diff --git a/docs/en/Deployment-React-Publish-IIS.md b/docs/en/Deployment-React-Publish-IIS.md new file mode 100644 index 00000000..d4138302 --- /dev/null +++ b/docs/en/Deployment-React-Publish-IIS.md @@ -0,0 +1,86 @@ +# Step By Step Publish To IIS + +## Host Application Publishing + +Publishing ASP.NET Zero Host project is no different to any other ASP.NET Core Application. + +### Publishing Web Site + +- Right click `\*.Web.Host` project and click **Publish**. +- Click **Start** and select **Folder** tab and choose the URL that you want to publish. + + + +- Create a folder on the server where **IIS** is located. (for example: `C:\inetpub\wwwroot\mywebsite`). +- Copy published files to server. (from `*.Web.Host/bin/Release/net8.0/publish/` to `C:\inetpub\wwwroot\mywebsite`). +- Change `appsettings.production.json` configurations with your own settings. + +### Create IIS Web Site + +- Right click **Sites** and click **Add Website**. + + + +- **mywebsite** application pool will be created automatically. Click **Application Pools**, double click **mywebsite** application pool and change its configuration. + + + +The project contains a `web.config` file, its contents may be changed during development, please check the` web.config` file after publishing, especially the `ASPNETCORE_ENVIRONMENT` setting. + +Check [Host ASP.NET Core on Windows with IIS](https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/iis/index?view=aspnetcore-2.1) document for more detail. + +## React Application Publishing + +The React application is built using Vite and can be deployed as static files to IIS: + +### Build the Application + +1. Configure production settings by creating or updating `.env.production` in the project root: + +```env +VITE_API_URL=https://your-host-api-url +``` + +2. Run `npm run build`. This command outputs the production build to the `dist` folder. + +3. Copy all files from the `dist` folder to your IIS website directory (for example: `C:\inetpub\wwwroot\reactwebsite`). + +### Configure IIS for SPA Routing + +**Important:** React uses client-side routing. If you refresh a page (F5), IIS will handle the request and will not find the requested path, returning an HTTP 404 error. You must configure IIS to redirect all requests to the `index.html` page. + +**Prerequisites:** Install the [URL Rewrite module](https://www.iis.net/downloads/microsoft/url-rewrite) for IIS. + +Create a `web.config` file in the website's root folder with the following content: + +```xml + + + + + + + + + + + + + + + + + + + + + +``` + +### Create IIS Web Site for React + +1. Right click **Sites** and click **Add Website** +2. Set the physical path to your React build folder (e.g., `C:\inetpub\wwwroot\reactwebsite`) +3. Configure the site name and port +4. Ensure the application pool is set to **No Managed Code** since React is a static site + diff --git a/docs/en/Deployment-React.md b/docs/en/Deployment-React.md new file mode 100644 index 00000000..c2d70dd6 --- /dev/null +++ b/docs/en/Deployment-React.md @@ -0,0 +1,58 @@ +# Deployment + +## About Deployment + +ASP.NET Zero React UI is built using [Vite](https://vite.dev/) as the build tool and bundler. Vite provides fast development server and optimized production builds out of the box. + +### Building for Production + +To build the React application for production, run the following commands in the root directory of the React project: + +1. Run `npm install` to install dependencies. +2. Run `npm run build` to create a production build. + +The build output will be placed in the `dist/` folder, ready for deployment to any static file server. + +### Configuration + +Before deploying, configure the production API endpoint by creating a `.env.production` file in the project root: + +```env +VITE_API_URL=https://your-production-api-url +``` + +This URL should point to your ASP.NET Zero Host API application. + +### Merged Solution (Optional) + +ASP.NET Zero also provides a merged solution where the React client-side application is included in the server-side Host project. Both applications can be published together and hosted under the same website. + +To publish the merged solution: + +1. Run `npm run build` in the React project to build the client application. +2. Run `dotnet publish -c Release` in the root directory of the `*.Host` project. See the [dotnet publish documentation](https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-publish) for additional parameters. +3. When the publish is complete, ensure the React build output is copied to the `wwwroot` folder of the published Host application. + +If you are using the merged solution, also configure settings in **appsettings.Production.json** file for the Host API. + +## Production Optimizations + +Vite automatically applies the following optimizations for production builds: + +- **Code Splitting**: Automatically splits code into smaller chunks for better caching +- **Tree Shaking**: Removes unused code from the final bundle +- **Minification**: Minifies JavaScript and CSS files +- **Asset Optimization**: Optimizes and hashes static assets for cache busting + +You can preview the production build locally by running: + +```bash +npm run preview +``` + +# Next + +* [Publishing to Azure](Deployment-React-Publish-Azure) +* [Publishing to IIS](Deployment-React-Publish-IIS) +* [Publishing to Docker](Deployment-React-Docker) + diff --git a/docs/en/Developing-React-Customizable-Dashboard.md b/docs/en/Developing-React-Customizable-Dashboard.md new file mode 100644 index 00000000..2442353f --- /dev/null +++ b/docs/en/Developing-React-Customizable-Dashboard.md @@ -0,0 +1,460 @@ +# Customizable Dashboard + +You can create new widgets and widget filters for the customizable dashboards. + +Let's create a new widget and widget filter step by step. + +## Pre-Note: Dashboard Configuration Locations + +Customizable dashboard configurations are stored in two places: + +- **Server-side definitions** (permissions, multi-tenancy side, etc.) are located in `*.Core -> DashboardCustomization -> Definitions -> DashboardConfiguration.cs` so the application layer can handle permissions and other server-side logic. +- **View-side definitions** (React components) are located in the React project under `src/pages/admin/dashboard/`. +- UI applications get data from the server about the dashboard and use their view information to render it. + +## Creating a New Widget Filter + +Our widget filter name will be `FilterHelloWorld`. It will have one input and a button, and it will trigger an event when the input changes. + +### Step 1. Create Filter Component + +1. Open the React project. + +2. Go to `src/pages/admin/dashboard/components/filters/` and create a new folder named `filter-hello-world/`. + +3. Create `index.tsx` inside the folder: + +**`src/pages/admin/dashboard/components/filters/filter-hello-world/index.tsx`** + +```tsx +import React, { useState, useCallback } from "react"; +import { Input, Button, Space } from "antd"; +import L from "@/lib/L"; + +interface FilterHelloWorldProps { + onFilterChange?: (name: string) => void; +} + +const FilterHelloWorld: React.FC = ({ onFilterChange }) => { + const [inputValue, setInputValue] = useState(""); + + const handleClick = useCallback(() => { + // Trigger custom event for widgets to listen to + window.dispatchEvent( + new CustomEvent("dashboard:helloFilter:nameChange", { + detail: { name: inputValue }, + }) + ); + onFilterChange?.(inputValue); + }, [inputValue, onFilterChange]); + + return ( +
+ + setInputValue(e.target.value)} + placeholder={L("SearchWithThreeDot")} + /> + + +
+ ); +}; + +export default FilterHelloWorld; +``` + +### Step 2. Define Widget Filter + +#### View Definitions + +1. Open `src/pages/admin/dashboard/customizable-dashboard/types.ts` and add the new filter ID: + +```typescript +export const DashboardCustomizationConst = { + // ... existing code ... + filters: { + filterDateRangePicker: "Filters_DateRangePicker", + filterHelloWorld: "Filters_HelloWorld", // Add new filter ID + }, + // ... existing code ... +} as const; +``` + +2. Open `src/pages/admin/dashboard/hooks/useDashboardConfiguration.tsx` and add your filter's view definition: + +```tsx +import FilterHelloWorld from "../components/filters/filter-hello-world"; + +export function useDashboardConfiguration(): DashboardViewConfigurationService { + return useMemo(() => { + const widgetFilterDefinitions: WidgetFilterViewDefinition[] = [ + { + id: DashboardCustomizationConst.filters.filterDateRangePicker, + component: FilterDateRangePicker, + }, + // Add your new filter + { + id: DashboardCustomizationConst.filters.filterHelloWorld, + component: FilterHelloWorld, + }, + ]; + // ... rest of the code + }, []); +} +``` + +#### Server Side Definition + +1. Open your server project. + +2. Open `*.Core.Shared -> [YourAppName]DashboardCustomizationConsts.cs` and define the same ID: + +```csharp +public class [YourAppName]DashboardCustomizationConsts +{ + public class Filters + { + public const string HelloWorldFilter = "Filters_HelloWorld"; + // ... other filters + } +} +``` + +3. Go to `*.Core -> DashboardCustomization -> Definitions -> DashboardConfiguration.cs` and add the filter definition: + +```csharp +public class DashboardConfiguration +{ + public DashboardConfiguration() + { + var helloWorldFilter = new WidgetFilterDefinition( + AbpZeroTemplateDashboardCustomizationConsts.Filters.HelloWorldFilter, + "FilterHelloWorld" // localized string key + ); + + WidgetFilterDefinitions.Add(helloWorldFilter); + } +} +``` + +Now your filter is available for all widgets. You can use it in any widget definition and it will be loaded to the page automatically. + +--- + +## Creating a New Widget + +Our widget name will be `WidgetHelloWorld`. + +### Step 1. Create an API + +Create an API endpoint that your widget needs. In this scenario, we will create one endpoint in `TenantDashboardAppService.cs` named `GetHelloWorldData`. + +```csharp +public class GetHelloWorldInput +{ + public string Name { get; set; } +} + +public class GetHelloWorldOutput +{ + public string OutPutName { get; set; } +} + +public interface ITenantDashboardAppService : IApplicationService +{ + GetHelloWorldOutput GetHelloWorldData(GetHelloWorldInput input); +} + +public class TenantDashboardAppService : ... +{ + [AbpAuthorize(AppPermissions.HelloWorldPermission)] // check permission + public GetHelloWorldOutput GetHelloWorldData(GetHelloWorldInput input) + { + return new GetHelloWorldOutput() + { + OutPutName = "Hello " + input.Name + " (" + Clock.Now.Millisecond + ")" + }; + } +} +``` + +*Although ASP.NET Zero loads widgets by filtering permissions, we still must check permissions here.* + +After you create the API, run the `*.Web.Host` project, then go to the `nswag` folder in the React project. Open a terminal and run: + +```bash +npm run nswag +``` + +### Step 2. Create Widget Component + +1. Open the React project. + +2. Go to `src/pages/admin/dashboard/components/widgets/` and create a new folder named `hello-world/`. + +3. Create `index.tsx` inside the folder: + +**`src/pages/admin/dashboard/components/widgets/hello-world/index.tsx`** + +```tsx +import React, { useEffect, useState, useCallback } from "react"; +import { Card } from "antd"; +import { TenantDashboardServiceProxy } from "@api/generated/service-proxies"; +import { useServiceProxy } from "@/api/service-proxy-factory"; + +const WidgetHelloWorld: React.FC = () => { + const tenantDashboardService = useServiceProxy(TenantDashboardServiceProxy, []); + const [helloResponse, setHelloResponse] = useState(""); + + const getHelloWorld = useCallback( + async (name: string) => { + const data = await tenantDashboardService.getHelloWorldData(name); + setHelloResponse(data.outPutName ?? ""); + }, + [tenantDashboardService] + ); + + // Initial load + useEffect(() => { + getHelloWorld("First Attempt"); + }, [getHelloWorld]); + + // Subscribe to filter events + useEffect(() => { + const handleNameChange = (event: CustomEvent<{ name: string }>) => { + getHelloWorld(event.detail.name); + }; + + window.addEventListener( + "dashboard:helloFilter:nameChange", + handleNameChange as EventListener + ); + + return () => { + window.removeEventListener( + "dashboard:helloFilter:nameChange", + handleNameChange as EventListener + ); + }; + }, [getHelloWorld]); + + return ( + +
+ Hello World Works!
+ Response: {helloResponse} +
+
+ ); +}; + +export default WidgetHelloWorld; +``` + +### Step 3. Define Widget + +#### View Definitions + +1. Open `src/pages/admin/dashboard/customizable-dashboard/types.ts` and add your widget ID: + +```typescript +export const DashboardCustomizationConst = { + widgets: { + tenant: { + // ... existing widgets ... + helloWorld: "Widgets_Tenant_HelloWorld", // Add new widget ID + }, + host: { + // ... existing widgets ... + }, + }, + // ... rest of the code +} as const; +``` + +2. Open `src/pages/admin/dashboard/hooks/useDashboardConfiguration.tsx` and add your widget's view definition: + +```tsx +import WidgetHelloWorld from "../components/widgets/hello-world"; + +export function useDashboardConfiguration(): DashboardViewConfigurationService { + return useMemo(() => { + // ... filter definitions ... + + const WidgetViewDefinitions: WidgetViewDefinition[] = [ + // ... existing widgets ... + + // Add your new widget + { + id: DashboardCustomizationConst.widgets.tenant.helloWorld, + component: WidgetHelloWorld, + defaultWidth: 6, + defaultHeight: 8, + }, + ]; + + return { WidgetViewDefinitions, widgetFilterDefinitions }; + }, []); +} +``` + +#### Server Side Definition + +1. Open your server project. + +2. Open `*.Core.Shared -> [YourAppName]DashboardCustomizationConsts.cs` and define the same ID: + +```csharp +public class [YourAppName]DashboardCustomizationConsts +{ + public class Widgets + { + public class Tenant + { + public const string HelloWorld = "Widgets_Tenant_HelloWorld"; + // ... other widgets + } + } +} +``` + +3. Go to `*.Core -> DashboardCustomization -> Definitions -> DashboardConfiguration.cs` and add the widget definition: + +```csharp +public class DashboardConfiguration +{ + public DashboardConfiguration() + { + // ... existing code ... + + var helloWorld = new WidgetDefinition( + id: AbpZeroTemplateDashboardCustomizationConsts.Widgets.Tenant.HelloWorld, + name: "WidgetHelloWorld", // localized string key + side: MultiTenancySides.Tenant, + usedWidgetFilters: new List() { helloWorldFilter.Id }, + permissions: tenantWidgetsDefaultPermission + ); + + helloWorld.Permissions.Add(AppPermissions.HelloWorldPermission); + + // Add widget to dashboard + var defaultTenantDashboard = new DashboardDefinition( + AbpZeroTemplateDashboardCustomizationConsts.DashboardNames.DefaultTenantDashboard, + new List() + { + generalStats.Id, dailySales.Id, profitShare.Id, memberActivity.Id, + regionalStats.Id, topStats.Id, salesSummary.Id, + helloWorld.Id // Add your widget to dashboard + } + ); + } +} +``` + +After that, you will be able to use your new widget. + +--- + +## Handling Widget Resize Events + +If your widget contains charts or content that needs to rerender on resize, you can listen to the grid layout resize events. + +In React, you can use the `ResizeObserver` API or listen to window resize events: + +```tsx +import React, { useEffect, useRef } from "react"; + +const WidgetWithResize: React.FC = () => { + const containerRef = useRef(null); + + useEffect(() => { + const container = containerRef.current; + if (!container) return; + + const resizeObserver = new ResizeObserver((entries) => { + for (const entry of entries) { + console.log("Widget resized:", entry.contentRect); + // TODO: Reload chart or recalculate layout + } + }); + + resizeObserver.observe(container); + + return () => { + resizeObserver.disconnect(); + }; + }, []); + + return ( +
+ {/* Your widget content */} +
+ ); +}; + +export default WidgetWithResize; +``` + +--- + +## Usage + +Since we created a tenant-side widget, open the tenant dashboard. + +1. Login as a tenant admin +2. Navigate to the Dashboard page +3. Click **Edit Mode** to enable dashboard editing +4. Click **Add Widget** button to open the widget selection modal +5. Select your "Hello World" widget and add it to the page + +![Tenant Dashboard with Edit Mode](images/customizable-dashboard-edit-mode.png) + +Since the Hello World widget uses the Hello World filter (we defined it in `DashboardConfiguration`), the filter will be loaded automatically. + +To use the filter: + +1. Click the **Filters** button next to the **Edit Mode** button +2. The filter modal will open showing available filters +3. Enter a value in the Hello World filter input and click **Go** +4. The Hello World widget will update with your filtered value + +![Dashboard Filter Modal](images/customizable-dashboard-filters.png) + +--- + +## Changing Default Dashboard View + +ASP.NET Zero uses [react-grid-layout](https://github.com/react-grid-layout/react-grid-layout) for the dashboard grid system and stores view data in [app settings](https://aspnetboilerplate.com/Pages/Documents/Setting-Management). + +To change the default view of the dashboard: + +1. Open the dashboard and design it by adding/arranging widgets +2. Open the browser's developer console (F12) +3. Click the **Save** button on the page +4. In the Network tab, find the `SavePage` request + +![Save Page Request](images/customizable-dashboard-save-page-request.png) + +5. Open the request payload and copy the data + +![Save Page Request Payload](images/customizable-dashboard-save-page-payload.png) + +6. Go to `AppSettingProvider.cs` in your `*.Core` project and find the `GetDefaultReactDashboardViews` method +7. Update the default layout data with the data you copied from the request payload + +--- + +## Summary of Files to Create/Modify + +| Action | File Path | +|--------|-----------| +| Create | `src/pages/admin/dashboard/components/filters/filter-hello-world/index.tsx` | +| Create | `src/pages/admin/dashboard/components/widgets/hello-world/index.tsx` | +| Modify | `src/pages/admin/dashboard/customizable-dashboard/types.ts` | +| Modify | `src/pages/admin/dashboard/hooks/useDashboardConfiguration.tsx` | + + diff --git a/docs/en/Development-React-Docker.md b/docs/en/Development-React-Docker.md new file mode 100644 index 00000000..a5f20122 --- /dev/null +++ b/docs/en/Development-React-Docker.md @@ -0,0 +1,204 @@ +# Development with Docker Containers for React + +This document explains how to set up a Docker-based development environment for the ASP.NET Zero React UI application alongside the backend API. + +## Prerequisites + +- Docker Desktop installed and running +- Node.js (LTS version) +- The ASP.NET Core backend (Web.Host) running or containerized + +## Backend Infrastructure Setup + +The React UI requires the ASP.NET Core backend API to be running. The backend's `aspnet-core/docker` folder contains Docker projects for running the infrastructure and API. + +### 1. Setting up the Infrastructure + +Infrastructure contains **mssql-server-linux** as a database server and **redis** for caching. + +In the backend's `aspnet-core/docker/infrastructure/` folder, run the **run-infrastructure.ps1** script which uses **docker-compose.infrastructure.yml** to set up the infrastructure. + +docker-infrastructure-run + +After running the script, PowerShell will show container logs. Use **Ctrl + C** to stop tailing the logs. + +### 2. Setting up the Self-Signed Certificate + +The backend API needs a certificate to enable HTTPS. In the backend's `aspnet-core/docker/certificate/` folder, run **create-certificate.ps1** to create the certificate. + +docker-create-dev-certificate + +### 3. Running the Migrator + +In the backend's `aspnet-core/docker/migrator/` folder, run **run-migrator.ps1** to apply database migrations. + +docker-migrate + +### 4. Running the Host API + +Start the Web.Host API using Docker: + +```shell +cd aspnet-core/docker +docker compose -f docker-compose-host.yml -f docker-compose-host.override.yml up +``` + +The API will be available at `https://localhost:44301` (or the configured port). + +--- + +## Running the React UI + +The React UI is a client-side application that can be run in two ways during development: + +### Option 1: Development Server (Recommended for Development) + +Run the React UI using the Vite development server: + +```bash +npm install +npm run dev +``` + +This starts the development server at `http://localhost:5173` with hot module replacement. + +Make sure the `.env` file points to the correct backend API URL: + +```env +VITE_API_URL=https://localhost:44301 +``` + +### Option 2: Docker Container + +You can containerize the React UI for testing production builds locally. + +#### Create a Dockerfile + +Create a `Dockerfile` in the React project root: + +```dockerfile +# Build stage +FROM node:20-alpine AS build +WORKDIR /app +COPY package*.json ./ +RUN npm ci +COPY . . +RUN npm run build + +# Production stage +FROM nginx:alpine +COPY --from=build /app/dist /usr/share/nginx/html +COPY nginx.conf /etc/nginx/conf.d/default.conf +EXPOSE 80 +CMD ["nginx", "-g", "daemon off;"] +``` + +#### Create nginx.conf + +Create an `nginx.conf` file for SPA routing: + +```nginx +server { + listen 80; + server_name localhost; + root /usr/share/nginx/html; + index index.html; + + location / { + try_files $uri $uri/ /index.html; + } +} +``` + +#### Build and Run + +```bash +# Build the image +docker build -t aspnetzero-react . + +# Run the container +docker run -d -p 4200:80 aspnetzero-react +``` + +--- + +## Docker Compose for Full Stack Development + +To run both the backend API and React UI together, create a `docker-compose.yml`: + +```yaml +version: '3.8' +services: + react-ui: + build: . + ports: + - "4200:80" + depends_on: + - host-api + environment: + - VITE_API_URL=http://host-api:21021 + + host-api: + # Reference your backend's docker-compose-host configuration + # or use the pre-built image + image: your-registry/aspnetzero-host:latest + ports: + - "44301:443" + environment: + - ASPNETCORE_ENVIRONMENT=Docker + - ConnectionStrings__Default=your-connection-string +``` + +--- + +## Configuration Notes + +### Environment Variables + +The React UI uses Vite environment variables. For Docker builds, you can: + +1. Set variables at build time in the Dockerfile: + ```dockerfile + ARG VITE_API_URL + ENV VITE_API_URL=$VITE_API_URL + RUN npm run build + ``` + +2. Pass them during build: + ```bash + docker build --build-arg VITE_API_URL=https://api.example.com -t aspnetzero-react . + ``` + +### CORS Configuration + +Ensure the backend's `appsettings.json` has the correct CORS origins configured to allow requests from the React container: + +```json +{ + "App": { + "CorsOrigins": "http://localhost:5173,http://react-ui" + } +} +``` + +--- + +## Troubleshooting + +### Container Conflict Errors + +If you get container conflict errors when switching between Docker configurations, clean up the containers: + +```bash +docker compose down +docker system prune -f +``` + +docker-container-conflict + +### Connection to Backend API Fails + +1. Ensure the backend API is running and accessible +2. Check that `VITE_API_URL` is correctly set +3. Verify CORS is configured to allow the React UI origin +4. Check Docker network connectivity if both are containerized diff --git a/docs/en/Extending-Existing-Entities-Core-React.md b/docs/en/Extending-Existing-Entities-Core-React.md new file mode 100644 index 00000000..7c20d3e1 --- /dev/null +++ b/docs/en/Extending-Existing-Entities-Core-React.md @@ -0,0 +1,235 @@ +# Extending Existing Entities + +## Introduction + +This tutorial is a step by step guide to learn **how to add new properties to existing entities**, from database layer to UI layer. + +In ASP.NET Zero, **Tenant**, **User** and **Role** entities are **abstract** in the framework, others are not. There are some differences between them. So, we separated it into two sections. + +*Note: We assume that you have created your project as described in the Getting Started document* + +* [Getting Started React](Getting-Started-React) + + +## Extending Abstract Entities + +As a sample, we will work on **User** entity. We want to add an **Address** property to the User. + +### Add New Property To User + +Open Authorization\\Users\\**User.cs** (in .Core project) and add the new property: + +```csharp +public class User : AbpUser +{ + //...existing code + + public virtual string Address { get; set; } +} +``` + +Here, we hided existing code in the User class to show it simpler. You can add Address property after existing properties. + +### Add Migration + +Since we added new property, our database schema is changed. Whenever we change our entities, we should add a new database migration. Open Package Manager Console, select ".EntityFrameworkCore" project as default project in combobox and write new migration code: + + Add-Migration "Added_Address_To_User" + +This will create a new Entity Framework migration class: + +```csharp + public partial class Added_Address_To_User : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "Address", + table: "AbpUsers", + nullable: true); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "Address", + table: "AbpUsers"); + } + } +``` + +Since it's automatically created, we don't have to know what it does for most cases. Now, we can update our database with this command: + + Update-Database + +When we check **AbpUsers** table in the database, we can see the new **Address** field: + +Address for Users + +For testing purposes, we can enter some data for existing users by hand. + +### Show Address On The UI + +Note: The UI part of this document is written for ASP.NET Core React version of ASP.NET Zero. For ASP.NET Core MVC & jQuery version, see [related doc](Extending-Existing-Entities-Core.md) . + +**GetUsers** method in Authorization\\Users\\**UserAppService.cs** (in .Application project) is used for getting list of users by clients. It returns a list of **UserListDto** (we always use [DTOs](https://aspnetboilerplate.com/Pages/Documents/Data-Transfer-Objects) for client communication). So, we should add the Address property to UserListDto too: + +```csharp +public class UserListDto : EntityDto, IPassivable, IHasCreationTime +{ + //...existing code + + public string Address { get; set; } +} +``` + +Since UserListDto **auto maps** from User entity, no need to change **UserAppService.GetUsers** method. Now, we can go to UI side to add Address property to the **users table**. + +Now we can start the ***.Host** project and then run [NSwag](Infrastructure-React-NSwag.md) to regenerate the TypeScript service proxies: + +```bash +npm run nswag +``` + +Open **src/pages/admin/users/index.tsx** file and add an Address column to the table. Find the `columns` definition and add a new column: + +```tsx +const columns: ColumnsType = [ + // ... existing columns ... + { + title: L("CreationTime"), + dataIndex: "creationTime", + key: "creationTime", + sorter: true, + render: (value) => formatDate(value), + }, + // Add the new Address column + { + title: L("Address"), + dataIndex: "address", + key: "address", + sorter: true, + }, + // ... rest of columns ... +]; +``` + +That's all. Now we can start the React application and open the **users page**: + +Address in table + +### Add Address On User Create/Edit + +We may want to set Address while **creating/editing** a User. + +Clients use UserAppService.**GetUserForEdit** method to show user information on edit form. It returns **GetUserForEditOutput** object which contains a **UserEditDto** object that includes user properties. +So, we should add Address to UserEditDto to allow clients to change Address property on create/update: + +```csharp +public class UserEditDto : IPassivable +{ + //...existing code + + public string Address { get; set; } +} +``` + +Since **UserAppService** uses **auto mapping**, no need to manually map Address to the User entity. So, the server-side code is just that. We can go to the UI side to add an Address field to the form. + +Open **src/pages/admin/users/components/CreateOrEditUserModal.tsx** and add the Address input field in the form: + +```tsx +
+ + + setUserValues({ ...userValues, address: e.target.value }) + } + /> +
+``` + +After adding, the new Address field is shown on the create/edit form as shown below: + +Address on user edit form + +## Extending Non-Abstract Entities + +As a sample, we will work on **Edition** entity. + +### Derive From Edition Entity + +Since Edition is **not abstract** in the framework, we can not direcly add new properties to the Edition class. Instead, we should use OOP patterns like **inheritance** or **composition**. Since inheritance will be simpler, we can create a new class deriving from Edition entity. There is already an entity derived from edition: **SubscribableEdition**. Let we look how it is implemented, and show the steps of how **AnnualPrice** field is added (SubscribableEdition class is under Editions folder under the .Core project): + +```csharp +public class SubscribableEdition : Edition +{ + //...other fields + + public decimal? AnnualPrice { get; set; } + + //...other fields +``` + + +Notice that a DbSet property for SubscribableEdition entity is added to DbContext class defined in .EntityFrameworkCore project. + +```csharp +public class ProjectNameDbContext : AbpZeroDbContext +{ + public virtual DbSet SubscribableEditions { get; set; } + + //...other entities + + public ProjectNameDbContext() + : base("Default") + { + + } + + //...other codes +} +``` + +Also, you can see that a new migration is added after SubscribableEdition is created. + +### Show AnnualPrice On The UI + +Editions\\**EditionAppService.cs** (in .Application project) is used to get list of editions by clients. It returns a list of **EditionListDto** (we always use [DTOs](https://aspnetboilerplate.com/Pages/Documents/Data-Transfer-Objects) for client communication). So, AnnualPrice property is added to EditionListDto too: + +```csharp +public class EditionListDto : EntityDto +{ + //...other fields + + public decimal? AnnualPrice { get; set; } + + //...other fields +} +``` + +**Auto mapping from MyEdition** is already added. No need to change **EditionAppService.GetEditions** method thanks to auto mapping. Now, you can go to the UI side to see how the AnnualPrice property is used in the **editions** table in **src/pages/admin/editions/index.tsx**: + +```tsx +{ + title: L("Price"), + key: "price", + render: (_, record) => ( + <> + {record.monthlyPrice || record.annualPrice ? ( + + $ {record.monthlyPrice} {L("Monthly")} / $ {record.annualPrice} {L("Annual")} + + ) : ( + {L("Free")} + )} + + ), +} +``` + +That's all. You can check **EditionCreateDto** and **src/pages/admin/editions/components/CreateOrEditEditionModal.tsx** to see how the AnnualPrice field is set during edition creation. diff --git a/docs/en/Feature-Dynamic-Entity-Parameters-Custom-Input-Types-React.md b/docs/en/Feature-Dynamic-Entity-Parameters-Custom-Input-Types-React.md new file mode 100644 index 00000000..4f4707dc --- /dev/null +++ b/docs/en/Feature-Dynamic-Entity-Parameters-Custom-Input-Types-React.md @@ -0,0 +1,255 @@ +# Create Custom Input Types + +Dynamic property system comes with 4 built-in input types to meet most needs: + +- **SINGLE_LINE_STRING** - Simple text input +- **CHECKBOX** - Boolean checkbox +- **COMBOBOX** - Single-select dropdown +- **MULTISELECTCOMBOBOX** - Multi-select dropdown + +But you can also create your own input types as shown below. + +## Backend: Register the Input Type in Core + +1. Go to `*.Core` and create a folder named `CustomInputTypes`. + +2. Create a class named `UserSelectInputType` in that folder. + + ```csharp + /// + /// User Select value UI type. + /// + [Serializable] + [InputType("USERSELECT")] + public class UserSelectInputType : InputTypeBase + { + } + ``` + +3. Go to `AppDynamicEntityParameterDefinitionProvider` and add new input type. + + ```csharp + public class AppDynamicEntityParameterDefinitionProvider : DynamicEntityParameterDefinitionProvider + { + public override void SetDynamicEntityParameters(IDynamicEntityParameterDefinitionContext context) + { + // ... existing code + context.Manager.AddAllowedInputType(); + } + } + ``` + +## Frontend: Understanding the Input Type System + +Custom input types in React are implemented as functional components that implement the `InputTypeComponentProps` interface. All input type components are located in: + +``` +src/pages/admin/components/common/input-types/ +├── input-components/ # Input component implementations +│ ├── SingleLineStringInput.tsx +│ ├── CheckboxInput.tsx +│ ├── ComboboxInput.tsx +│ └── MultiSelectComboboxInput.tsx +├── input-types.constants.ts # Input type registry +└── types.ts # TypeScript interfaces +``` + +## Frontend: Creating a Custom Input Component + +First, understand the interface your component must implement. The `InputTypeComponentProps` interface is defined in [types.ts](../src/pages/admin/components/common/input-types/types.ts): + +```typescript +export interface InputTypeComponentProps { + selectedValues: string[]; + allValues: string[]; + onChange: (values: string[]) => void; + onInstance?: (instance: unknown) => void; +} +``` + +- **selectedValues**: The currently selected value(s) as a string array +- **allValues**: Available options (for dropdown-type inputs) +- **onChange**: Callback to notify parent of value changes +- **onInstance**: Optional callback to expose component methods to parent + +### Sample Component: User Autocomplete Input + +Create a new file at `src/pages/admin/components/common/input-types/input-components/UserAutocompleteInput.tsx`: + +```tsx +import React, { useEffect, useRef, useState } from "react"; +import { AutoComplete } from "antd"; +import { NameValueDto } from "@/api/generated/api"; +import { useServiceProxy } from "@/hooks/useServiceProxy"; +import type { InputTypeComponentProps } from "../types"; + +const UserAutocompleteInput: React.FC = ({ + selectedValues, + onChange, + onInstance, +}) => { + const [value, setValue] = useState(selectedValues?.[0] ?? ""); + const [options, setOptions] = useState<{ label: string; value: string }[]>([]); + const [selectedUser, setSelectedUser] = useState(null); + const ref = useRef<{ getSelectedValues: () => string[] } | null>(null); + + const { commonLookupService } = useServiceProxy(); + + // Sync with external selectedValues + useEffect(() => { + setValue(selectedValues?.[0] ?? ""); + }, [selectedValues]); + + // Expose getSelectedValues method to parent + useEffect(() => { + ref.current = { + getSelectedValues: () => (selectedUser?.value ? [selectedUser.value] : []), + }; + if (onInstance) onInstance(ref.current); + }, [selectedUser, onInstance]); + + // Load initial user if selectedValue is provided + useEffect(() => { + if (selectedValues?.[0] && !selectedUser) { + commonLookupService + .findUsers({ + filterText: "", + maxResultCount: 1, + excludeCurrentUser: false, + }) + .then((result) => { + if (result.items && result.items.length === 1) { + setSelectedUser(result.items[0]); + setValue(result.items[0].name ?? ""); + } + }); + } + }, [selectedValues, selectedUser, commonLookupService]); + + const handleSearch = async (searchText: string) => { + if (!searchText) { + setOptions([]); + return; + } + + const result = await commonLookupService.findUsers({ + filterText: searchText, + maxResultCount: 50, + excludeCurrentUser: false, + }); + + setOptions( + (result.items ?? []).map((user) => ({ + label: user.name ?? "", + value: user.value ?? "", + })) + ); + }; + + const handleSelect = (selectedValue: string, option: { label: string; value: string }) => { + const user: NameValueDto = { name: option.label, value: selectedValue }; + setSelectedUser(user); + setValue(option.label); + onChange([selectedValue]); + }; + + return ( + setValue(text)} + placeholder="Search users..." + style={{ width: "100%" }} + /> + ); +}; + +export default UserAutocompleteInput; +``` + +## Frontend: Registering the Custom Input Type + +After creating the input component, you need to register it in the input types configuration. + +### Step 1: Add the Type Name + +Open [types.ts](../src/pages/admin/components/common/input-types/types.ts) and add your new type to the `InputTypeName` union: + +```typescript +export type InputTypeName = + | "SINGLE_LINE_STRING" + | "CHECKBOX" + | "COMBOBOX" + | "MULTISELECTCOMBOBOX" + | "USERSELECT"; // Add your custom type +``` + +### Step 2: Register in Constants + +Open [input-types.constants.ts](../src/pages/admin/components/common/input-types/input-types.constants.ts) and add your component: + +```typescript +import type { InputTypeConfigurationDefinition } from "./types"; +import SingleLineStringInput from "./input-components/SingleLineStringInput"; +import CheckboxInput from "./input-components/CheckboxInput"; +import ComboboxInput from "./input-components/ComboboxInput"; +import MultiSelectComboboxInput from "./input-components/MultiSelectComboboxInput"; +import UserAutocompleteInput from "./input-components/UserAutocompleteInput"; + +export const INPUT_TYPES = { + SINGLE_LINE_STRING: "SINGLE_LINE_STRING", + CHECKBOX: "CHECKBOX", + COMBOBOX: "COMBOBOX", + MULTISELECTCOMBOBOX: "MULTISELECTCOMBOBOX", + USERSELECT: "USERSELECT", // Add the constant +} as const; + +export const InputTypeConfigurationDefinitions: InputTypeConfigurationDefinition[] = [ + { + name: "SINGLE_LINE_STRING", + component: SingleLineStringInput, + hasValues: false, + }, + { name: "CHECKBOX", component: CheckboxInput, hasValues: false }, + { name: "COMBOBOX", component: ComboboxInput, hasValues: true }, + { + name: "MULTISELECTCOMBOBOX", + component: MultiSelectComboboxInput, + hasValues: true, + }, + { + name: "USERSELECT", + component: UserAutocompleteInput, + hasValues: false, // false because values come from API, not pre-defined + }, +]; +``` + +The `hasValues` property indicates whether the input type requires a pre-defined list of values (like combobox options) or fetches/generates values on its own. + +## Using the Custom Input Type + +That's all! Your new custom input type is ready to use. Navigate to the Dynamic Entity Properties page and create a new entity property. You will see your `USERSELECT` input type in the dropdown and can use it for any entity. + +## Input Type Configuration Summary + +| Property | Description | +|----------|-------------| +| `name` | Unique identifier for the input type (must match backend `InputType` attribute and `InputTypeName` union) | +| `component` | React component that implements `InputTypeComponentProps` | +| `hasValues` | Whether the input requires pre-defined values (shown in admin UI) | + +## Best Practices + +1. **Always implement `getSelectedValues`** - Expose this method via `onInstance` so the parent can retrieve values programmatically. + +2. **Sync with `selectedValues` prop** - Use `useEffect` to update internal state when the prop changes. + +3. **Call `onChange` on value changes** - Notify the parent component whenever the selected value changes. + +4. **Return values as string array** - All input types must return their values as `string[]`, even for single-value inputs. + +5. **Match backend input type name** - The `name` in `InputTypeConfigurationDefinitions` must exactly match the `[InputType("NAME")]` attribute on your backend class. + diff --git a/docs/en/Feature-Dynamic-Entity-Parameters-React.md b/docs/en/Feature-Dynamic-Entity-Parameters-React.md new file mode 100644 index 00000000..11285ef2 --- /dev/null +++ b/docs/en/Feature-Dynamic-Entity-Parameters-React.md @@ -0,0 +1,165 @@ +# Dynamic Property System + +**Dynamic Property System** is a system that allows you to add and manage new properties on entity objects at runtime without any code changes. With this system, you can define dynamic properties on entity objects and perform operations on these objects easily. For example, it can be used for cities, counties, gender, status codes etc. + +Check AspNet Boilerplate side of [Dynamic Property System](https://aspnetboilerplate.com/Pages/Documents/Dynamic-Parameter-System) + +### Defining + +* First of all you need to define input types and entities you want to use with dynamic properties as described [here](https://aspnetboilerplate.com/Pages/Documents/Dynamic-Parameter-System#dynamic-property-definition) + +* Then go to http://localhost:5173/app/admin/dynamic-property + +* Add Dynamic Properties that you need + +* Assign Dynamic Properties to your entity + + +dynamic-properties + +* Then you will be able to use dynamic property for the items of your entity. + +### Managing Dynamic Properties of an Entity + +You can navigate users to the dynamic property value management page using React Router. The route pattern is: + +``` +/app/admin/dynamic-entity-property-value/manage-all/:entityFullName/:rowId +``` + +Here's an example of adding a "Dynamic Properties" action to a user's action menu in [users/index.tsx](../src/pages/admin/users/index.tsx): + +```tsx +import { useNavigate } from "react-router-dom"; +import { useServiceProxy } from "@/api/service-proxy-factory"; +import { + DynamicEntityPropertyDefinitionServiceProxy, +} from "@api/generated/service-proxies"; + +const UsersPage: React.FC = () => { + const navigate = useNavigate(); + const dynamicEntityPropertyDefinitionService = useServiceProxy( + DynamicEntityPropertyDefinitionServiceProxy, + [], + ); + + // State to track if entity has dynamic properties + const [entityHasDynamicProperties, setEntityHasDynamicProperties] = useState(false); + + // Check if the User entity has any dynamic properties assigned + useEffect(() => { + const checkDynamicProperties = async () => { + const entities = await dynamicEntityPropertyDefinitionService.getAllEntities(); + const hasProperties = (entities ?? []).some( + (e) => e === "MyCompanyName.AbpZeroTemplate.Authorization.Users.User" + ); + setEntityHasDynamicProperties(hasProperties); + }; + checkDynamicProperties(); + }, [dynamicEntityPropertyDefinitionService]); + + // Navigate to dynamic properties management page + const showDynamicProperties = (user: UserListDto) => { + navigate( + `/app/admin/dynamic-entity-property-value/manage-all/MyCompanyName.AbpZeroTemplate.Authorization.Users.User/${user.id}` + ); + }; + + // Add to menu items + const getMenuItems = (record: UserListDto) => { + const items = []; + + // ... other menu items ... + + if (entityHasDynamicProperties) { + items.push({ + key: "dynamicProperties", + label: L("DynamicProperties"), + onClick: () => showDynamicProperties(record), + }); + } + + return items; + }; + + // ... rest of the component +}; +``` + +### Using DynamicPropertyValueManager Component Directly + +You can also use the `DynamicPropertyValueManager` component directly in a modal or inline. The component is located at [DynamicPropertyValueManager.tsx](../src/pages/admin/dynamic-properties/dynami-entity-property-values/components/DynamicPropertyValueManager.tsx): + +```tsx +import { useRef } from "react"; +import { Modal } from "antd"; +import DynamicPropertyValueManager, { + type DynamicPropertyValueManagerRef, +} from "@/pages/admin/dynamic-properties/dynami-entity-property-values/components/DynamicPropertyValueManager"; +import L from "@/lib/L"; + +interface Props { + visible: boolean; + entityFullName: string; + entityId: string; + onClose: () => void; +} + +const DynamicPropertiesModal: React.FC = ({ + visible, + entityFullName, + entityId, + onClose, +}) => { + const managerRef = useRef(null); + + const handleSave = () => { + managerRef.current?.saveAll(); + onClose(); + }; + + return ( + + + + ); +}; +``` + +dynamic-property-of-entity + +_____ + + + + + + + + + + + + + + + + + + + + + + +
PropertySummary
PropertyName*Unique name of the dynamic property
Input Type*Input type name of the dynamic property
PermissionRequired permission to manage anything about that property
(DynamicPropertyValue, EntityDynamicProperty, EntityDynamicPropertyValue)
+ diff --git a/docs/en/Features-React-Active-Directory.md b/docs/en/Features-React-Active-Directory.md new file mode 100644 index 00000000..fa55b1ff --- /dev/null +++ b/docs/en/Features-React-Active-Directory.md @@ -0,0 +1,19 @@ +# Active Directory (LDAP) + +LDAP (Active Directory) Authentication is disabled by default. + +To enable LDAP on server-side open `*CoreModule.cs` in ***.Core** project, uncomment the following line + +```csharp +Configuration.Modules.ZeroLdap().Enable(typeof(AppLdapAuthenticationSource)); +``` + +See [server side](Features-Mvc-Core-Tenant-Active-Directory) to enable LDAP. Once we enable, we can see **LDAP settings** section in the settings page: + +LDAP Settings + +We can check "**Enable LDAP Authentication**" to enable it. If the server works in domain and application runs with a domain user or local system, then generally even **no need** to set Domain name, user and password. You can logout and then login with your **domain user name and password**. If not, you should set these credentials. + +## Next + +- [Maintenance](Features-React-Maintenance) diff --git a/docs/en/Features-React-Audit-Logs.md b/docs/en/Features-React-Audit-Logs.md new file mode 100644 index 00000000..d377918b --- /dev/null +++ b/docs/en/Features-React-Audit-Logs.md @@ -0,0 +1,63 @@ +# Audit Logs + +### Operation Logs + +In audit logs page, we can see all user interactions with the application under the operation logs tab: + +Audit logs + +All application service methods and MVC controller actions are automatically logged and can be viewed here. See [audit logs documentation](https://aspnetboilerplate.com/Pages/Documents/Audit-Logging) to learn how to configure it. When we click the magnifier icon, we can see all details an audit log: + +Audit Log + +Audit log report is provided by **AuditLogAppService** class. + +### Periodic Log Deletion + +ASP.NET Zero has built-in periodic log deletion system (`*.Application/Auditing/ExpiredAuditLogDeleterWorker.cs`). To enable it, go to `appsettings.json` and set `App:AuditLog:AutoDeleteExpiredLogs:IsEnabled` to true; (default `false`) + +```json +"App": { + "AuditLog": { + "AutoDeleteExpiredLogs": { + "IsEnabled": true + } + } +} +``` + +Then periodic log deletion will be enabled. + +#### Periodic Log Deletion Backup + +Periodic log deletion system also has backup implementation. It uses `IExpiredAndDeletedAuditLogBackupService` to backup deleted items. It's default implementation uses excel to create backup. To enable it, go to `appsettings.json` and set `App:AuditLog:AutoDeleteExpiredLogs:ExcelBackup:IsEnabled` to true; (default `false`). Then deleted items will be stored in the given file path as an excel file. + +```json +"App": { + "AuditLog": { + "AutoDeleteExpiredLogs": { + "IsEnabled": true, + "ExcelBackup": { + "IsEnabled": true, + "FilePath": "App_Data/AuditLogsBackups/" + } + } + } +} +``` + +________ + + +`*.Application/Auditing/ExpiredAuditLogDeleterWorker.cs` has two more parameter. + +**CheckPeriodAsMilliseconds:** Time to wait between two controls. + +**MaxDeletionCount:** The maximum number of records that can be deleted at once. + +> Note: To perform smaller operations with more frequent intervals you can decrease `MaxDeletionCount` and `CheckPeriodAsMilliseconds`. + +## Next + +- [Entity History](Features-React-Entity-History) + diff --git a/docs/en/Features-React-Azure-Key-Vault.md b/docs/en/Features-React-Azure-Key-Vault.md new file mode 100644 index 00000000..8df7b215 --- /dev/null +++ b/docs/en/Features-React-Azure-Key-Vault.md @@ -0,0 +1,25 @@ +# Azure Key Vault Support + +ASP.NET Core provides many configuration providers out of the box. One of them is Azure Key Vault Configuration Provider. **Azure Key Vault** is a cloud service that provides a secure store for secrets. You can securely store **keys**, passwords, certificates, and other secrets. For more information about Azure Key Vault, please refer to https://docs.microsoft.com/en-us/aspnet/core/security/key-vault-configuration. + +ASP.NET Zero provides built-in integration for Azure Key Vault. You can easily configure Azure Key Vault for ASP.NET Zero by filling the configurations displayed below in appsettings.json of your application. For other environments like Production or Staging, use the correct setting file (appsettings.Production.json or appsettings.Staging.json). + +````json + "Configuration": { + "AzureKeyVault": { + "IsEnabled": "false", + "KeyVaultName": "", + "TenantId": "", + "ClientId": "", + "ClientSecret": "" + } + } +```` + +* ```IsEnabled```: Enables or disables using Azure Key Vault configuration. +* ```KeyVaultName```: Key Vault Name. +* ```TenantId```: Azure TenantId. +* ```ClientId```: Key vault clientId. +* ```ClientSecret```: Key vault client secret. + +For more information, you can check documentation of [https://www.nuget.org/packages/Azure.Extensions.AspNetCore.Configuration.Secrets](https://www.nuget.org/packages/Azure.Extensions.AspNetCore.Configuration.Secrets) diff --git a/docs/en/Features-React-Chat.md b/docs/en/Features-React-Chat.md new file mode 100644 index 00000000..8cc4407b --- /dev/null +++ b/docs/en/Features-React-Chat.md @@ -0,0 +1,38 @@ +# Chat + +Chat icon is located next to user's profile image on top right corner of the page. The number in the red circle shows total unread chat message count. + +User menu + +When user clicks this icon, chat panel appears on the right of page. This panel contains friends of user and list of blocked users. + +User menu + +User can add new friends by writing the username into username TextBox above friend list. If "**Chat with other tenants**" feature is enabled for tenant, users of other tenants can be added as a friend by writing +\[tenancy name\]\\\[user name\] (ex: Default\\admin). If "Chat with host users" feature is enabled, host users can be added as friend by writing **.\\\[user name\]** in the same TextBox. Note that, a user can search users of the tenant who he/she belongs to but can't search users of Host. So, when adding a user of Host as a friend, the exact username must be written. + +While online friends/users have a green circle on their profile image, offline friends/users have a gray circle. + +User can pin or unpin the chat panel by clicking the pin icon on top right corner of the chat panel. Application tries to remember last state of chat panel and restores it when user login to application. + +When a friend/user is selected, conversation panel is opened. + +User menu + +Chat system also allows sending images, files and link of current page to friends + +Chat attachments + +User can block or unblock a friend in this area. There is a down arrow icon right of the selected user's username. This icon opens an action menu and this menu contains block user or unblock user actions according to user's block status. + +## Chat Features + +User menu + +There are three chat features in the system. These are "Chat", "Chat with host", "Chat with other tenants". These features can be enabled/disabled per edition/tenant. By using these features host can enable/disable chat with other tenant's users or host users. + +## Next + +- [User Menu](Features-React-User-Menu) + + diff --git a/docs/en/Features-React-Common-Payment-System.md b/docs/en/Features-React-Common-Payment-System.md new file mode 100644 index 00000000..2616efab --- /dev/null +++ b/docs/en/Features-React-Common-Payment-System.md @@ -0,0 +1,48 @@ +# Common Payment System + +ASP.NET Zero provides a payment system to get payments easily. In order to start a payment, just use `CreatePayment` method of `IPaymentManager`. You can create a new application service or you can use existing `CreatePayment` method of `PaymentAppService`. Then, you need to redirect the user to the `gateway-selection` route of the `account` module as shown below; + +```tsx +import { useNavigate } from "react-router-dom"; + +// Inside your component +const navigate = useNavigate(); + +// Navigate to gateway selection +navigate(`/account/gateway-selection?paymentId=${paymentId}`); +``` + +ASP.NET Zero's common payment system will handle the rest of the payment flow. + +- If the payment process is successfull, user will be redirected to `SuccessUrl` provided when creating the payment request. +- If the payment process is unsuccessfull, user will be redirected to `ErrorUrl` provided when creating the payment request. +- If `SuccessUrl` or `ErrorUrl` is not provided, user will be redirected to a common page and result of the payment process will be displayed. + +## Creating Payment Request + +### SubscriptionPayment Entity + +`IPaymentManager` is used to create a payment requet. It's `CreatePayment` method requires a `SubscriptionPayment` entity. Here is the detials of `SubscriptionPayment` entity. + +* `TenantId`: Represents which Tenant this payment request belongs to. +* `PaymentPeriodType`: Period type of the payment if this is a payment for a specific period. Currently, Monthly and Annual are supported. +* `DayCount`: Integer value of `PaymentPeriodType` field. +* `Gateway`: Name of payment gateway which processed this payment. This is set by ASP.NET Zero when the payment is successfull. +* `Status`: Status of payment. This is set by ASP.NET Zero. +* `ExternalPaymentId`: Id of the payment in the external payment gateway system like Stripe or PayPal. This is set by ASP.NET Zero. +* `InvoiceNo`: Invoice number if an invoice generated in ASP.NET Zero for this payment. +* `SuccessUrl`: URL to redirect user if payment is successfull. +* `ErrorUrl`: URL to redirect user if payment is failed. +* `IsRecurring`: Represents if this is a recurring payment or not. If it is recurring, user's credit card will be charged at the end of every payment cycle. This is only supported by Stripe at the moment. +* `IsProrationPayment`: This is a special field. If the tenant is on a recurring payment plan and operation is upgrade, then it is a proration payment. +* `ExtraProperties`: A dictionary to store additional information on the payment object. +* `SubscriptionPaymentProducts`: List of products to be purchased for this payment. + +### SubscriptionPaymentProduct Entity + +* `SubscriptionPaymentId`: Id of the related payment record. +* `Description`: Description of the purchased product. +* `Amount`: Price of the product. +* `Count`: Count of products to be purchased. +* `TotalAmount`: Total price of products to be purchased. +* `ExtraProperties`: A dictionary to store additional information on the product object. diff --git a/docs/en/Features-React-Customizable-Dashboard.md b/docs/en/Features-React-Customizable-Dashboard.md new file mode 100644 index 00000000..caab5933 --- /dev/null +++ b/docs/en/Features-React-Customizable-Dashboard.md @@ -0,0 +1,49 @@ + + +# Customizable Dashboard + +Users can customize their dashboard. + +To do that. Go to dashboard and enable edit mode. + +![customizable-dashboard-edit-mode](images/customizable-dashboard-edit-mode.png) + + + +Then you will see dashboard customization bar. + +![customizable-dashboard-edit-bar](images/customizable-dashboard-edit-bar.png) + + + + + +### Customize Your Dashboard + +##### Adding New Page + +You can create new page by clicking "**Add Page"**" button. + +![customizable-dashboard-add-new-page](images/customizable-dashboard-add-new-page.png) + +![customizable-dashboard-new-page](images/customizable-dashboard-new-page.png) + + + +**Adding New Widget** + +Click "**Add Widget**" button and select the widgets that you want to see on selected page. + +![customizable-dashboard-add-widget](images/customizable-dashboard-add-widget.png) + + + +**Resize and Move on Page** + +Then you can resize and move widgets on page. + +![customizable-dashboard-add-widget-in-page](images/customizable-dashboard-add-widget-in-page.png) + + + +![customizable-dashboard-example](images/customizable-dashboard-example.png) diff --git a/docs/en/Features-React-Edition-Management.md b/docs/en/Features-React-Edition-Management.md new file mode 100644 index 00000000..b5a793c3 --- /dev/null +++ b/docs/en/Features-React-Edition-Management.md @@ -0,0 +1,33 @@ +# Edition Management + +*If you're not developing a multi-tenant application, you can skip this section.* + +Most **SaaS** (multi-tenant) applications have **editions** (packages) those have different **features**. Thus, they can provide different **price and feature options** to their tenants (customers). **Editions page** (available in host login) is used to manage application's editions: + +Editions Page + +Editions are used to group feature values and assign to tenants. We can create a new edition by clicking "**Create new edition**" button: + +Edit Edition + +An edition can be free or paid. If it's a paid edition then you should enter monthly and annual prices. You can allow tenants to use trial version of this edition for a specified days. Then you can determine an expire strategy: How many days to allow a tenant to use the application after subscription expires. And finally, you can deactivate tenant or assign to a free edition if they don't extend their subscription. + +Features tab is used to determining features available for the edition: + +Edit edition features + +After creating an edition, only name and features of the edition can be changed. If you have made a mistake while creating the edition or if you want users to stop subscribing this edition, just delete the edition and create another one. Since Edition is a [soft-delete](https://aspnetboilerplate.com/Pages/Documents/Data-Filters#isoftdelete) entity, it will not be deleted from database but will be marked as deleted. + +In order to delete and edition, all tenants of that edition must be moved to another edition. You can easily do that in Editions page by clicking the "Actions" button for an Edition and then select "Move tenants to another edition" item. After that, you will see a modal page like below: + +Move Tenants to Another Edition + +You can click the link on this page to see which tenants will be moved to the newly selected edition. + +See [feature management](https://aspnetboilerplate.com/Pages/Documents/Feature-Management) and [edition management](https://aspnetboilerplate.com/Pages/Documents/Zero/Edition-Management) documents for more information. + + + +## Next + +- [Tenant Management](Features-React-Tenant-Management) diff --git a/docs/en/Features-React-Email-Activation.md b/docs/en/Features-React-Email-Activation.md new file mode 100644 index 00000000..59ade861 --- /dev/null +++ b/docs/en/Features-React-Email-Activation.md @@ -0,0 +1,12 @@ +# Email Activation + +When a new user registers, an email confirmation code is sent to the user's email address. If the user cannot receive the email for any reason, it is possible to re-send the confirmation +code again by clicking email activation link. + +Email activation + +## Next + +- [Tenant Sign Up](Features-React-Tenant-Sign-Up) + + diff --git a/docs/en/Features-React-Entity-History.md b/docs/en/Features-React-Entity-History.md new file mode 100644 index 00000000..3b8100b2 --- /dev/null +++ b/docs/en/Features-React-Entity-History.md @@ -0,0 +1,55 @@ +# Entity History + +In change logs under audit logs menu, we can see all change logs (entity history) in the application: + +Change Logs + +When we click on the **View Change** field in the Actions button, we can see all the details of a change log: + +Change Log Detail + +When we click on the **All Changes** button in the Actions section, we can see all the details of the changes made to the entity in a timeline. + +Additionally, if you want to view all the changes made to an entity, you can redirect to `{AppPath}/{AppAreaName}/EntityChanges/{Entity_ID}/{Entity_Full_Name}` within the application. This URL directs to a page that contains all the historical changes for the specified entity. + +Entity History Timeline Detail + +You should add entity type that you want to track to ***.Core\EntityHistory\EntityHistoryHelper.TrackedTypes**. Make sure you uncomment following lines in ***.EntityFrameworkCore\EntityFrameworkCore\{YourProjectName}EntityFrameworkCoreModule.cs** and set **Configuration.EntityHistory.IsEnabled** to **true**. + +```csharp +// Set this setting to true for enabling entity history. +Configuration.EntityHistory.IsEnabled = false; + +// Uncomment below line to write change logs for the entities below: +// Configuration.EntityHistory.Selectors.Add("AbpZeroTemplateEntities", EntityHistoryHelper.TrackedTypes); +// Configuration.CustomConfigProviders.Add(new EntityHistoryConfigProvider(Configuration)); +``` + +The first commented line here adds entity types defined in `EntityHistoryHelper.TrackedTypes` to entity history config, so changes on those entities will be recorded. The second commented line adds a custom config provider to `CustomConfigProviders` list. ASP.NET Zero executes `GetConfig` method of each `CustomConfigProvider` and returns the result of `GetConfig` to React client app. + +So, in this particular case, `EntityHistoryConfigProvider` returns if entity history feature is enabled or not and if enabled, it returns list of entities enabled for entity history. React client app uses those information to show entity history related buttons on each entity page. + +For example, when entity history is enabled for an Entity, React client shows history dropdown menu item for each entity record on the related page. Here is a sample screenshot for role list: + +Entity History Action + +In this way, history of an entity can be retrieved both on change logs tab in audit logs page or on the list page of the entity itself. + +The entity history config can be accessed on the React app like below: + +````javascript +abp.custom.EntityHistory +```` + +The abp.custom.EntityHistory object contains properties below: + +* **isEnabled**: Boolean field represents if entity history is enabled or not. +* **enabledEntities**: Array of string contains full name of entity types entity history is enabled for. + +Here is a sample screenshot which show the value of **abp.custom.EntityHistory**: + +Entity history custom config + +## Next + +- [Subscription](Features-React-Subscription) diff --git a/docs/en/Features-React-Forgot-Password.md b/docs/en/Features-React-Forgot-Password.md new file mode 100644 index 00000000..de3fd527 --- /dev/null +++ b/docs/en/Features-React-Forgot-Password.md @@ -0,0 +1,15 @@ +# Forgot Password + +If a user forgets the password, it can be retrieved by clicking the **Forgot Password** link. On the forgot password screen, user enters the email address and password reset email will be sent to entered email address. + + +Forgot password + +After receiving the email, user can click to Reset button in the email and user will be redirected to password reset page of AspNet Zero. In this page, user can change the existing password. + + +Reset password + +## Next + +* [Token Based Authentication](Features-React-Token-Based-Authentication) diff --git a/docs/en/Features-React-Host-Dashboard.md b/docs/en/Features-React-Host-Dashboard.md new file mode 100644 index 00000000..02b76d1b --- /dev/null +++ b/docs/en/Features-React-Host-Dashboard.md @@ -0,0 +1,14 @@ +# Host Dashboard + +Host dashboard is used to show some statistics about tenants, editions +and income: + +Host dashboard + +This is a fully implemented dashboard except two sample statistics (sample statistics 1 & 2) those are placeholders for your own statistics. + +## Next + +- [Organization Units](Features-React-Organization-Units) + + diff --git a/docs/en/Features-React-Host-Settings.md b/docs/en/Features-React-Host-Settings.md new file mode 100644 index 00000000..df197eb4 --- /dev/null +++ b/docs/en/Features-React-Host-Settings.md @@ -0,0 +1,91 @@ +# Host Settings + +Host settings page is used to configure some system settings. + +## General + +![General Host Settings](images/host-settings-general-7.png) + +**Timezone** is an important setting in this page. ASP.NET Zero can work in multiple zones. Each user can see dates and times in their own time zone. Timezone setting in this page allows you to set default time zone for the application including all tenants and users. Tenants and users can change time zone in their own settings. Timezone setting is available only if you are using UTC clock provider. [See documentation](https://aspnetboilerplate.com/Pages/Documents/Timing) to +switch to UTC. By default ASP.NET Zero uses UnspecifiedClockProvider and in that case ASP.NET Zero doesn't modify dates sent from client to server and vice versa. If you configure your app to use another clock provider, then ASP.NET Zero normalizes dates according to selected clock provider. + +You can change the used clock provider in the PreInitialize method of your Core module. + +````csharp +Clock.Provider = ClockProviders.Utc; +```` + +## Tenant Management + +![Tenant Management Settings](images/host-settings-tenant-management.png) + +You can configure settings related to tenant management under "Tenant Management" tab. You can enable/disable tenants from registering the system. You can also make newly registered tenants active or passive. + +You can enable/disable captcha on tenant registration page. + +You can enable/disable captcha on tenant reset password page. + +You can enable/disable captcha on tenant email activation page. + +You can enable/disable email domain restriction for users. + +You can also select a default edition, so a newly registered tenant will be assigned to this edition automatically unless the tenant subscribes to a specific edition. + +## User Management + +![User Management Settings](images/host-settings-user-management-6.png) + +User related settings can be configured under this tab. You can force email confirmation for login. You can enable phone number verification. Also, you can enable cookie consent so ASP.NET Zero shows a cookie consent bar for the users to accept cookie policy of your application. + +You can enable/disable captcha on users login page. + +> Note: **Token Based Authentication** has `ReCaptchaIgnoreWhiteList` located in `WebConsts`. If you want a client app to be ignored for reCaptcha control during login, add a value to `ReCaptchaIgnoreWhiteList` and send the same value in the `User-Agent` request header for your login request from the client app. You can check the Xamarin mobile app in AspNet Zero to see how `ReCaptchaIgnoreWhiteList` works. + +You can also enable/disable session timeout control. If it is enable and the user does not provide any input to the site during the timeout period, a countdown modal will be displayed to user. If the user still does not provide an entry to the site during the modal countdown period, user will be log out. + +Each tenant can allow tenant users to use Gravatar profile picture or not. Additionally, you can adjust the size of the profile picture in megabytes (MB) and set the dimensions in pixels (px) for width and height. + +## Security + +![Security Settings](images/host-settings-security-6.png) + +**Security** tab in host settings page contains password complexity settings. Host can define system wide password complexity settings in this tab. Each tenant can override this setting in tenant settings page. + +This tab also contains user lock-out settings and two factor login settings as well. + +> Note: +> +> * To use two factor login with **SMS verification** you have to enable **Phone number verification enabled (via SMS)** setting in **User Management** tab. +> +> * If user does not have a verified phone number, user will be logged in without sms verification. + +##### Password + +You can enable/disable password expiration on the settings page. If you enable it, users will have to change their password after defined days passed. + +You can also prevent user's new password from being same as any of last x passwords. If you enable it, you will need to define how many previous password you want to prevent. Users will not be able to use some of the previously used password as a new password. + +## Email + +![Email Settings](images/host-settings-email.png) + +**Email(SMTP)** tab allows you to configure smtp settings for your app. ASP.NET Zero uses MailKit to send emails. By default, smtp certificate validation is disabled in **YourProjectNameMailKitSmtpBuilder.cs** class. If you are able to validate mail server's certificate, you need to modify **ServerCertificateValidationCallback** in **YourProjectNameMailKitSmtpBuilder.cs**. + +If you want each Tenant to configure their own SMTP settings, you can go to **YourProjectNameConsts.cs** which is in ***.Core.Shared** project and set **AllowTenantsToChangeEmailSettings** to true. In that way, each tenant can configure and use their own SMTP settings. + +## Invoice + +![Invoice Settings](images/host-settings-invoice-1.png) + +Under this tab, you can configure the legal name and the address of the Host company which will be displayed on the generated invoices as the service provider. + +## Other settings + +![Other Settings](images/host-settings-others.png) + +Under this tab, there is only one setting which is used to enable/disable quick theme selection icon on the layout of ASP.NET Zero next to language selection dropdown. + +## Next + +- [Tenant Settings](Features-React-Tenant-Settings) + diff --git a/docs/en/Features-React-Language-Management.md b/docs/en/Features-React-Language-Management.md new file mode 100644 index 00000000..f416a1ab --- /dev/null +++ b/docs/en/Features-React-Language-Management.md @@ -0,0 +1,25 @@ +# Language Management + +Language management page is used to manage (add/edit/delete) **application languages** and change **localized texts**: + +Language management + +We can create new language, edit/delete an existing language and **set a language as default**. Note that; tenants can not create/edit/delete default languages, but host users can do. + +When we click to **Change text** for any language, we are redirected to a new view to edit language texts: + +Language texts + +We can select any language as a **base** (reference) and change **target** language's texts. Base language is just to help the translation progress. Since there maybe different [localization sources](https://aspnetboilerplate.com/Pages/Documents/Localization#DocLocalizationSources), we select the source to translate. When we click the edit icon, we can see the edit modal for the selected text: + +Language text editing + +**Host** users (if allowed) can edit languages and localized texts. These languages will be default for all tenants for a multi-tenant application. **Tenants** inherit languages and localized texts and can **override** localized texts. + +If you want to add a new language, you can review the documentation on [How to Add a New Language in an ASP.NET Zero React Application](Adding-New-Localization-React) + +See [language management](https://aspnetboilerplate.com/Pages/Documents/Zero/Language-Management) and [localization](https://aspnetboilerplate.com/Pages/Documents/Localization) documents for more information. + +## Next + +- [Audit Logs](Features-React-Audit-Logs) diff --git a/docs/en/Features-React-Login.md b/docs/en/Features-React-Login.md new file mode 100644 index 00000000..4c68c6c5 --- /dev/null +++ b/docs/en/Features-React-Login.md @@ -0,0 +1,28 @@ +# Login + +Default route for the account pages is the login page: + +Login page + +The **tenant selection** section above login section is shown only in a **multi-tenant** application and if "tenancy name detection" is **not possible from url** (for example, if you use subdomain as tenancy names, +no need to show a tenant selection). When we click Change link, tenant change dialog appears and we can change the tenant. There is a single tenant named **Default** in the initial database. Leave tenancy name input as blank to login as **host**. + +We can use **admin** user name and **123qwe** password in first run the application. + +If you select "Should change password on next login" while creating a user, the user will see the change password screen. This is not selected for initially created users. + +Change password + +After login, we are redirected to the main application (App routes/pages). + +## User Lockout + +You can configure user lockout settings on Settings page. Users are locked out for specified duration if they enter wrong password for a specified amount of times. + +Both values can be configured under the Security tab of Settings page in the application. + +## Next + +* [Social Logins](Features-React-Social-Logins) + + diff --git a/docs/en/Features-React-Main-Menu-Layout.md b/docs/en/Features-React-Main-Menu-Layout.md new file mode 100644 index 00000000..09e2c290 --- /dev/null +++ b/docs/en/Features-React-Main-Menu-Layout.md @@ -0,0 +1,89 @@ +# Main Menu and Layout + +Menu and Layout files are located under the following folders: + +- Menu definition: [src/lib/navigation/appNavigation.tsx](../src/lib/navigation/appNavigation.tsx) +- Layout components: [src/pages/admin/components/layout/](../src/pages/admin/components/layout/) + +ASP.NET Zero has 13 theme options and some of them use left menu while others use top menu. Both menu types get the menu definition from the `buildRawMenu` function and `useAppMenu` hook in [appNavigation.tsx](../src/lib/navigation/appNavigation.tsx). If you need to add new menu items, modify this file. + +React routes are defined in: + +- [src/routes/AppRouter.tsx](../src/routes/AppRouter.tsx) - defines all application routes + +## Menu Item Properties + +A menu item is defined using the `AppMenuItem` interface: + +```typescript +export interface AppMenuItem { + id: string; + title: string; + permissionName?: string; + icon?: string; + fontIcon?: string; + route?: string; + routeTemplates?: string[]; + children?: AppMenuItem[]; + external?: boolean; + parameters?: Record; + requiresAuthentication?: boolean; + featureDependency?: () => boolean; +} +``` + +| Property | Description | +|----------|-------------| +| `id` | Unique identifier for the menu item | +| `title` | Display name (use `L()` function for localization) | +| `permissionName` | Permission required to show this menu item | +| `icon` | Keenicons icon name to display | +| `fontIcon` | Font icon class name (alternative to icon) | +| `route` | React Router route path (e.g., `/app/admin/users`) | +| `routeTemplates` | Additional route patterns for active state detection | +| `children` | Child menu items for nested menus | +| `external` | If true, opens an external URL in a new tab | +| `parameters` | Additional parameters for the route | +| `requiresAuthentication` | Show to authenticated users without specific permission | +| `featureDependency` | Function to check feature dependency | + +## Adding a New Menu Item + +To add a new menu item, modify the `buildRawMenu` function in [appNavigation.tsx](../src/lib/navigation/appNavigation.tsx): + +```typescript +export const buildRawMenu = (): AppMenuItem[] => [ + // ... existing items + { + id: "MyNewPage", + title: L("MyNewPage"), + permissionName: "Pages.MyNewPage", + icon: "my-icon", + route: "/app/admin/my-new-page", + }, + // ... more items +]; +``` + +Make sure to: +1. Add the localization key `MyNewPage` to your localization files +2. Create the corresponding route in [AppRouter.tsx](../src/routes/AppRouter.tsx) +3. Create the page component + +## Feature Dependency Example + +You can conditionally show menu items based on features: + +```typescript +{ + id: "Chat", + title: L("Chat"), + icon: "message-text-2", + route: "/app/chat", + featureDependency: () => abp.features.isEnabled("App.ChatFeature"), +} +``` + +## Next + +- [Edition Management](Features-React-Edition-Management) diff --git a/docs/en/Features-React-Maintenance.md b/docs/en/Features-React-Maintenance.md new file mode 100644 index 00000000..edf17543 --- /dev/null +++ b/docs/en/Features-React-Maintenance.md @@ -0,0 +1,19 @@ +# Maintenance + +Maintenance page is available to **host side** for multi tenant applications (for single tenant applications it's shown in tenant side) and shown as below: + +Maintenance cache + +In the **Caches** tab, we can clear some or all caches. Clearing caches may be needed if you manually change database and want to refresh application cache. **Website Logs** tab is used to see and download logs: + +Maintenance logs + +## New Version Notification + +When a **new version** of the application is available you can **send a new version notification** to the users. The notification will be shown a **popup** to the users. After clicking the **ok** button their browser will be **refreshed** and the **browser cache** will be **cleared**. + +![New version notification](images/new-version-notification-2.png) + +## Next + +- [Tenant Dashboard](Features-React-Tenant-Dashboard) diff --git a/docs/en/Features-React-Notifications.md b/docs/en/Features-React-Notifications.md new file mode 100644 index 00000000..94ec1000 --- /dev/null +++ b/docs/en/Features-React-Notifications.md @@ -0,0 +1,49 @@ +# Notifications + +Notification icon is located next to the language selection button. + +notifications + +User can see 3 recent notifications by clicking this icon. + +notifications + +User can marks all notifications as read by clicking the **Set all as read** link or can mark a single notification by clicking the **Set as read** link next to each notification. Notifications are sent real-time using SignalR. In addition, a **desktop push notification** is shown when a notification is received. + +## Notification Settings + +**Settings** link opens notification settings dialog. + +notifications + +In this dialog there is a global setting for user to enable/disable receiving notifications. If this setting is enabled, then user can enable/disable each notification individually. + +You can also define your custom notifications in **AppNotificationProvider** class. For example, new user registration notification is defined in the **AppNotificationProvider** as below. + +````csharp +context.Manager.Add( + new NotificationDefinition( + AppNotificationNames.NewUserRegistered, + displayName: L("NewUserRegisteredNotificationDefinition"), + permissionDependency: new SimplePermissionDependency(AppPermissions.Pages_Administration_Users) + ) +); +```` + +See [notification definitions](https://aspnetboilerplate.com/Pages/Documents/Notification-System#notification-definitions) section for detailed information. + +**AppNotifier** class is used to publish notifications. **NotificationAppService** class is used to manage application logic for notifications. + +When a notification is sent, the React app receives it via SignalR. Notification formatting is handled in [utils.ts](../src/pages/admin/notifications/utils.ts) which contains the `formatAbpUserNotification` function. If you want to customize how notification URLs are generated, you can modify the URL building logic in this file. + +See [notifications documentation](https://aspnetboilerplate.com/Pages/Documents/Notification-System) for detailed information. + +## Notification List + +All notifications of the user are listed in this page. We can delete and mark a notification as read in this page. + +Notification list + +## Next + +- [Chat](Features-React-Chat) diff --git a/docs/en/Features-React-Organization-Units.md b/docs/en/Features-React-Organization-Units.md new file mode 100644 index 00000000..310b6d84 --- /dev/null +++ b/docs/en/Features-React-Organization-Units.md @@ -0,0 +1,26 @@ +# Organization Units + +Organization units (OU) are used to hierarchically group user and entities. Then you can get user or entities based on their OUs. When we click Administration/Organization units, we enter the related page: + +Organization units page + +Here, we can manage OUs (create, edit, delete, move), members of organization units (add/remove) and roles of organization units. When a role is added to an organization unit, all members of that organization unit are granted with the permissions of the role. +In that way, you can easily assign a role to all users of an organization unit. + +In the left OU tree, we can **right click** to an OU to open **context menu** for OU operations. + +We can also add new members with the upper right button of members tab. + +Select a user dialog + +This is a user selection modal that allows searching and selecting users to add to the organization unit. + +We can also add new roles with the upper right button of roles tab. A role selection modal is used to select roles to assign. + +Select a role dialog + +Further information see [ASP.NET Boilerplate Organization Unit Management Document](https://aspnetboilerplate.com/Pages/Documents/Zero/Organization-Units). + +## Next + +- [Role Management](Features-React-Role-Management) diff --git a/docs/en/Features-React-Qr-Login.md b/docs/en/Features-React-Qr-Login.md new file mode 100644 index 00000000..272a37f6 --- /dev/null +++ b/docs/en/Features-React-Qr-Login.md @@ -0,0 +1,40 @@ +# QR Login Integration in ASP.NET Zero with MAUI + +## Introduction + ASP.NET Zero offers a QR Login feature which allows users to log in to the web application by scanning a QR code from the MAUI application. This document explains how to enable and use QR Login in ASP.NET Zero with MAUI. + +## Enabling QR Login +Before you can use the QR Login functionality, it needs to be enabled in the ASP.NET Zero web application settings. Follow these steps to enable QR Login: + +1. Navigate to **Settings** in the ASP.NET Zero Web application. +2. Go to the **User Management** tab, locate the **QR Login** setting, and enable it. +3. Save the settings. + +Once enabled, the login page on the web application will display a QR code for authentication. + +> **Note:** After enabling QR Login on the Host Settings page, each tenant can independently enable or disable the QR Login feature in their specific tenant settings. + +![Qr Code on Login Page](images/login-page-with-qr-login.png) + +## Using QR Login in MAUI +The MAUI application allows users to authenticate by scanning the QR code displayed on the web login page. Here’s how users can log in through QR Login: + +1. Open the **MAUI application** and log in. +2. Navigate to the **Sidebar Menu**. +3. Select **QR Login** from the menu. +4. If you haven't granted camera permission before, allow access to the camera. +5. The QR Code Reader will activate. +6. Scan the QR code displayed on the web application's login page. +7. If the QR code is valid, you will be automatically logged in to the web application. + +## One-Way Authentication +QR Login in ASP.NET Zero provides a streamlined authentication flow. It supports one-way authentication, meaning that the user can scan the QR code without entering credentials manually, assuming they are already authenticated in the MAUI app. + +![Qr Login Authentication](images/qr-login-authentication.gif) + +## Summary +QR Login provides authentication method for users who are already signed into the MAUI app. By scanning a QR code from the web login page, users can instantly access the web application without entering credentials again. + +## Next + +- [Sign Up](Features-React-Sign-Up) diff --git a/docs/en/Features-React-Role-Management.md b/docs/en/Features-React-Role-Management.md new file mode 100644 index 00000000..46ca8e92 --- /dev/null +++ b/docs/en/Features-React-Role-Management.md @@ -0,0 +1,35 @@ +# Role Management + +When we click Administration / Roles menu, we enter to the role management page: + +Role management page + +Roles are used to **group permissions**. When a user has a role, then the user will have all permissions of that role. + +A role is represented by the **Role** class. Role class [can be extended](Extending-Existing-Entities.md) by adding new properties. + +Roles can be dynamic or static: + +- **Static role**: A static role has a known **name** (like 'admin') and this name can't be changed. (But the **display name** can be changed). It's created on the system startup and can not be deleted on the UI. Thus, we can write codes based on a static role name. +- **Dynamic role**: We can create a dynamic role after deployment. Then we can grant permissions for that role, we can assign the role to some users and we can delete it. We can not know names of dynamic roles in development time. + +One or more roles can be set as **default**. Default roles are assigned to newly added/registered users by default. This is not a development time property and can be set or changed after deployment. + +In startup project, we have static **admin** role for host (for multi-tenant apps). Also, we have static **admin** and **user** roles for tenants. **Admin** roles have all permissions granted by default. +**User** role is the **default** role for new users and has no permission by default. These can be changed easily in the **AppRoleConfig.cs** class under "**/Authorization/Roles/**" folder in the *.Core project. + +Static roles are seeded in **HostRoleAndUserCreator.cs** and **TenantRoleAndUserBuilder.cs** classes under "**Migrations/Seed/**" folder in *.EntityFrameworkCore project. + +#### Role Permissions + +Since roles are used to group permissions, we can set permissions of a role while creating or editing as shown below: + +Role Permissions + +*Note that not all permissions shown in the figure above.* + +Every tenant has it's **own roles** and any change in roles for a tenant does not effect other tenants. Also, host has also it's own isolated roles. + +## Next + +- [User Management](Features-React-User-Management) diff --git a/docs/en/Features-React-Setup-Page.md b/docs/en/Features-React-Setup-Page.md new file mode 100644 index 00000000..1952f9e9 --- /dev/null +++ b/docs/en/Features-React-Setup-Page.md @@ -0,0 +1,13 @@ +# Setup Page + +ASP.NET Zero application can be set-up using install page. This page is developed to create initial database, apply migrations and configure the application according to user's input on this page. Setup page can be accessed on **http://yourwebsite.com/account/install**. + +Just note that, this page is only visible when the database is not created. If the database is created, you will be redirected to login page. + +install page + +## Next + +- [Migrator Console App](Migrator-Console-Application) + + diff --git a/docs/en/Features-React-Sign-Up.md b/docs/en/Features-React-Sign-Up.md new file mode 100644 index 00000000..ba1eb570 --- /dev/null +++ b/docs/en/Features-React-Sign-Up.md @@ -0,0 +1,13 @@ +# Sign Up + +When we click the "**Create Account**" link (which is only available for tenants for multi-tenant applications) in the login page, a registration form is shown: + +![](images/registration-form-small-1.png) + +**Recaptcha** (security question) is optional. It uses Google's Recaptcha service. Recaptcha service works per domain. So, to make it properly work, you should create your own private and public keys for your domain on https://www.google.com/recaptcha and replace keys in **appsettings.json** file under the ***.Web.Host** project and in the **appconfig.json** in the client side. + +Each tenant can enable/disable user registration under the "User management" tab of settings page. If user registration is disabled for a tenant, "Create Account" link will not be visible on the login page for that tenant. + +## Next + +- [Email Activation](Features-React-Email-Activation) diff --git a/docs/en/Features-React-Social-Logins.md b/docs/en/Features-React-Social-Logins.md new file mode 100644 index 00000000..2a82e881 --- /dev/null +++ b/docs/en/Features-React-Social-Logins.md @@ -0,0 +1,141 @@ +# Social and External Logins + +ASP.NET Zero supports social media logins and external logins as well. To enable one of them, we should change the following settings in **appsettings.json** file. + +```json + "Authentication": { + "Facebook": { + "IsEnabled": "false", + "AppId": "", + "AppSecret": "" + }, + "Google": { + "IsEnabled": "false", + "ClientId": "", + "ClientSecret": "", + "UserInfoEndpoint": "https://www.googleapis.com/oauth2/v2/userinfo" + }, + "Microsoft": { + "IsEnabled": "false", + "ConsumerKey": "", + "ConsumerSecret": "" + }, + "OpenId": { + "IsEnabled": "false", + "ClientId": "", + "Authority": "", + "LoginUrl": "", + "ValidateIssuer": "true", + "ClaimsMapping": [] + }, + "WsFederation": { + "IsEnabled": "false", + "Authority": "", + "ClientId": "", + "Tenant": "", + "MetaDataAddress": "" + }, + "JwtBearer": { + "IsEnabled": "true", + "SecurityKey": "PhoneBook_XXXXXXXXXXXXXXXX", + "Issuer": "PhoneBook", + "Audience": "PhoneBook" + } +} +``` + +ASP.NET Zero enables and configures social and external login providers in the PostInitialize method of **{YourProjectName}WebHostModule.cs** class. Some parts of social and externa login code is close sourced for licensing purposes in [Abp.AspNetZeroCore](https://www.nuget.org/packages/Abp.AspNetZeroCore) and [Abp.AspNetZeroCore.Web](https://www.nuget.org/packages/Abp.AspNetZeroCore.Web) nuget packages. + +You can find many documents on the web to learn how to obtain authentication keys for social platforms. So, we will not go to details of creating apps on social media platforms. Once you get your keys, you can write +them into `appsettings.json`. When you enable it, social media logos are automatically shown on the login page as shown below: + +Social Login Icons + +Just note that, social media logins and external logins are only available on Tenant scope. So, a tenant must be selected on the login page to see those logos, otherwise there will be no logos on the login page. + +## OpenId Connect Login + +In addition to social logins, ASP.NET Zero includes OpenId Connect Login integrated. It's configuration can be changed in `appsettings.json` + +```json +"OpenId": { + "IsEnabled": "false", + "ClientId": "", + "Authority": "", + "LoginUrl": "", + "ValidateIssuer": "true", + "ResponseType": "id_token", + "ClaimsMapping": [] +} +``` + +In some cases, OpenId Connect provider doesn't return claims we want to use. For example, Azure AD doesn't return "nameidentifier" claim but ASP.NET Core Identity uses it to find id of the user. So, in such cases, we can use **ClaimsMapping** to map claims of provider to custom claims. AspNet Zero will find the claim with **key** and will map it to internal claim with **claim** value in the mapping. For the following configuration, external **objectidentifier** will be mapped to internal **nameidentifier** claim. + +````json +"ClaimsMapping": [ + { + "claim": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier", + "key": "id" + }, + { + "claim": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name", + "key": "name" + }, + { + "claim": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname", + "key": "given_name" + }, + { + "claim": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname", + "key": "family_name" + }, + { + "claim": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress", + "key": "email" + } +] +```` + +If you are using Azure AD for OpenID Connect and your app is multi-tenant on Azure side, then you need to disable issuer validation, so all Azure AD users can use your app. Note that, multi-tenant app here is the one you have created on Azure, not the multi-tenancy concept of ASP.NET Zero. Once social logins are properly configured in the backend appsettings.json, they are automatically shown in the login page UI. + +Note that currently "ValidateIssuer" setting is not affective because the used client side library doesn't support disabling issuer validation. + +## WsFederation (ADFS) + +ASP.NET Zero also includes ADFS login integrated. t's configuration can be changed in `appsettings.json` + +```json +"WsFederation": { + "IsEnabled": "false", + "Authority": "", + "ClientId": "", + "Tenant": "", + "MetaDataAddress": "" +} +``` + +## JwtBearer + +ASP.NET Zero uses JwtBearer authentication by defult. It is recommended to change SecurityKey configured in appsettings.json for your production environment + +## IExternalLoginInfoManager interface + +ASP.NET Zero allows to customize getting user's username, name and surname from claims when logging in via external login. By default there are two implementations of IExternalLoginInfoManager which are **DefaultExternalLoginInfoManager** and **WsFederationExternalLoginInfoManager**. + +You can implement this class for any external login manager you want and return it the external login provider you want in **ExternalLoginInfoManagerFactory.cs**. After that, ASP.NET Zero will use your implementation to get username, name and surname when creating a local user record for the externally logged in user. + +## React part + +All the above sections are related to the server side part of ASP.NET Zero. On the React side, social and external logins are handled in the account pages under `src/pages/account/`. Note that currently **Facebook**, **Google**, **OpenID Connect** and **ADFS** authentications are implemented for the React application. + +When you click a social login or external login icon on the login page, there are two main flows. Facebook, Google and ADFS options opens a popup window and ask user to login. In that case, callback function for the selected provider will be called right away. + +However, for OpenID Connect, clicking the icon will redirect you to the external website and you will login on the external website. After that, you will be redirected back to ASP.NET Zero website (to the login page). Then, the callback function for OpenID Connect will be called. + +All callback functions makes a request to server-side app to validate the information gathered from external or social login provider. If the information is validated, a local user record will be created (only for the first time) and user will be logged in to ASP.NET Zero website. + +## Next + +- [Two Factor Authentication](Features-React-Two-Factor-Authentication) + + diff --git a/docs/en/Features-React-Subscription-PayPal-Integration.md b/docs/en/Features-React-Subscription-PayPal-Integration.md new file mode 100644 index 00000000..1bbb75af --- /dev/null +++ b/docs/en/Features-React-Subscription-PayPal-Integration.md @@ -0,0 +1,19 @@ +# PayPal Integration + +In order to configure PayPal settings, open `appsettings.json` file in ***.Web.Host project** and fill the fields below: + +- **IsActive:** This setting can be used to enable/disable PayPal. If set to false, end users will not see PayPal option during the payment process. +- **Environment:** Determines PayPal environment. "**sandbox**" is used for testing environment and "**live**" is used for production environment. +- **BaseUrl:** URL for making API calls to PayPal. You can find correct URLs in your PayPal developer dashboard. +- **ClientId**: ClientId for the PayPal app. +- **ClientSecret:** ClientSecret for the PayPal app. +- **DemoUsername:** Username for a demo account to show users in Demo mode for testing purposes. +- **DemoPassword:** Password for a demo account to show users in Demo mode for testing purposes. + +Note that current implementation of PayPal doesn't support recurring payments. So, If a tenant wants to pay via PayPal, ASP.NET Zero will not charge Tenant's account automatically when the Tenant's subscription expires. In that case, Tenant needs to pay the subscription price on every subscription cycle by entering it to the system and clicking the **extend** button on the subscription page. + +## Next + +- [Stripe Integration](Features-React-Subscription-Stripe-Integration) + + diff --git a/docs/en/Features-React-Subscription-Stripe-Integration.md b/docs/en/Features-React-Subscription-Stripe-Integration.md new file mode 100644 index 00000000..a69266d8 --- /dev/null +++ b/docs/en/Features-React-Subscription-Stripe-Integration.md @@ -0,0 +1,75 @@ +# Stripe Integration + +In order to configure Stripe, open `appsettings.json` file in ***.Web.Host** project and fill the fields below: + +- **IsActive:** This setting can be used to enable/disable Stripe. If set to false, end users will not see Stripe option during the payment process. +- **BaseUrl:** URL for making API calls to Stripe. You can find correct URLs in your Stripe dashboard. +- **SecretKey:** Your Stripe SecretKey. +- **PublishableKey:** Your Stripe PublishableKey. +- **WebhookSecret:** Your Stripe WebHookSecret which is used to validate WebHook requests. +- **PaymentMethodTypes** (array containing strings): Supported payment method types, check [stripe payment method types](https://stripe.com/docs/payments/payment-methods) + +Stripe supports recurring payments. If a tenant wants to pay via Stripe and accepts automatically billing the account used for the initial payment, then Stripe charges the amount from Tenants account on each subscription cycle and notifies AspNet Zero. Then, AspNet Zero extends the subscription for the paid period (either monthly or annual). + +Stripe recurring payments + +If "Automatically bill my account" option is not selected on the payment page, tenants should login to the system and manually extend their subscription by clicking the "**Extend**" button on the subscription page and pay manually. + +##### Important Note + +AspNet Zero uses webhooks to get results from the stripe. That's why you must go to the https://dashboard.stripe.com/webhooks and add a new webhook with `https://[www.yoursite.com]/Stripe/WebHooks` endpoint. And subscribe to `invoice.paid` and `checkout.session.completed` events. + + + +## Testing Stripe WebHooks on Localhost + +##### Stripe-Cli + +You can use stripe-cli in order to get Stripe's WebHooks requests on your local environment. To download it, go to https://dashboard.stripe.com/webhooks page. And install stripe-cli + +![stripe-test-stripe-cli-download](images/stripe-test-stripe-cli-download.png) + +After you download it, login to stripe-cli https://github.com/stripe/stripe-cli/wiki/login-command + +Then you can forward webhooks to AspNet Zero with running listener. + +```powershell +stripe listen --forward-to https://localhost:44301/Stripe/WebHooks +``` + +That will forward all events to your local project. For more information check: https://github.com/stripe/stripe-cli/wiki/listen-command + +*After you connect successfully your device will be listed.* + +![stripe-test-stripe-cli-list](images/stripe-test-stripe-cli-list.png) + +##### Webhookrelay + +In order to get Stripe's webhook request on your local environment, you can also use external tools like [https://webhookrelay.com](https://webhookrelay.com).It is one of the best tools on the web at the moment. [How to receive Stripe webhooks on localhost](https://webhookrelay.com/blog/2017/12/26/receiving-stripe-webhooks-localhost/) can be used to test Stripe's webhook on the localhost. Basically, you need to create an account on [https://webhookrelay.com](https://webhookrelay.com), then need to download relay.exe to your development machine. + +Then, you need to run relay.exe like this; + +```powershell +./relay.exe forward --bucket stripe https://localhost:44301/Stripe/WebHooks +``` + +This will give you an url something like "https://my.webhookrelay.com/v1/webhooks/aa180d45-87d5-4e9c-8bfa-e535a91df3fc". You need to enter this url as an webhook endpoint on Stripe's webhook dashboard ([https://dashboard.stripe.com/account/webhooks](https://dashboard.stripe.com/account/webhooks)). + +Don't forget to enter your production app's URL as a webhook endpoint when you publish your app to production. + + + +**Note that;** + +- Tenants can disable or enable Stripe to charge their accounts automatically on the Subscription page. + +- When upgrading to an higher edition, AspNet Zero calculates the cost for upgrade and charges it from Tenants account(with using checkout). + +- When a tenant subscribes to an edition using Stripe and if admin user changes the edition of the Tenant on Tenant page to a higher edition, Tenant's account will be charged on stripe automatically. + + (https://stripe.com/docs/billing/subscriptions/prorations ) + +## Next + +- [Visual Settings](Features-React-Visual-Settings) + diff --git a/docs/en/Features-React-Subscription.md b/docs/en/Features-React-Subscription.md new file mode 100644 index 00000000..4d76783a --- /dev/null +++ b/docs/en/Features-React-Subscription.md @@ -0,0 +1,52 @@ +# Subscription + +Tenants can manage (see, extend or upgrade) their edition/plan subscriptions using this page: + +Subscription + +All payment records for purchasing/extending/upgrading the subscription are kept in the system. These records can be seen on "Payment History" tab on subscription page. + +Payment History + +Tenants can also create & print invoices for these payments by clicking the "**Show Invoice**" button. System will automatically generate an invoice number and show the generated invoice. In order to use this function, both Host and Tenant must set invoice information on host setting/tenant setting page. + +Tenant Invoice Settings + +After all, invoices for payments related to subscription can be generated. You can see a sample invoice below: + +Sample Invoice + +ASP.NET Zero's subscription system allows using two payment gateways, one is [PayPal](https://www.paypal.com) and the other one is [Stripe](https://stripe.com/). You can configure both payment gateways in the `appsettings.json` file in the ***.Web.Host** project. + +When subscription of a Tenant is about to expire, an email is sent to email address of the tenant to remind this expiration. This job is handled in **SubscriptionExpireEmailNotifierWorker.cs** background worker. This worker runs once in every day and sends email to tenants whose license will expire after N days later. The day count of N is stored in a setting named "**App.TenantManagement.SubscriptionExpireNotifyDayCount**" and its default value is 7 days. You can change it in **AppSettings.cs** class in *.Core project. + +When the subscription of a Tenant is expired, **SubscriptionExpirationCheckWorker.cs** (it is localed next to SubscriptionExpireEmailNotifierWorker.cs) comes into play and executes the logic below: + +* If **WaitingDayAfterExpire** is set for the Edition of the tenant, ASP.NET Zero waits for **WaitingDayAfterExpire** days to end Tenant's subscription. +* If **"assign to another edition"** option is selected for the Edition of the Tenant, tenant will be moved to the fallback edition. +* If "**deactive tenant**" option is selected for the Edition of the Tenant, tenant will be disabled and will not be able to use the system. +* If the subscription expires for a tenant who subscribed for trial usage, the tenant will be disabled and will not be able to use the system. + +#### Minimum Update Amount + +Since payment systems have accepted minimum payment amounts, you may need to configure the minimum payment amount based on your payment system. This setting is located in the backend project [`*.Core.Shared/AbpZeroTemplateConsts.cs`](https://github.com/aspnetzero/aspnet-zero-core/blob/dev/aspnet-core/src/MyCompanyName.AbpZeroTemplate.Core.Shared/AbpZeroTemplateConsts.cs#L24): + +```csharp +// Note: +// Minimum accepted payment amount. If a payment amount is less than that minimum value, payment progress will continue without charging. +// Even though we can use multiple payment methods, users always can choose to use the method with the highest accepted payment amount. +// For example, if you use Stripe and PayPal: Stripe accepts min $5 and PayPal accepts min $3. If your payment amount is $4, +// the user will prefer a payment method with the higher acceptance threshold (Stripe in this case). +public const decimal MinimumUpgradePaymentAmount = 1M; +``` + +Default value is **1**. + +Payment progress will continue without charging any amount if the payment amount is less than the configured value. + + + +## Next + +* [PayPal Integration](Features-React-Subscription-PayPal-Integration) +* [Stripe Integration](Features-React-Subscription-Stripe-Integration) diff --git a/docs/en/Features-React-Swagger-UI.md b/docs/en/Features-React-Swagger-UI.md new file mode 100644 index 00000000..9da38f72 --- /dev/null +++ b/docs/en/Features-React-Swagger-UI.md @@ -0,0 +1,13 @@ +# Swagger UI + +[Swagger UI](http://swagger.io/swagger-ui/) is **integrated** to ASP.NET Zero **by default**. You can browse **Swagger UI** from `https://localhost:44301/swagger/ui/` URL. + +Notice that this is working in the server-side Host project. + +The below page is the default page of server side API application where you can see all available API endpoints: + +Swagger UI + +**.Web.Host** project contains a basic login page. If you login on this page, you can execute actions on Swagger UI which requires authentication. + +In order to enable comments on Swagger UI, set ```Swagger:ShowSummaries``` to true in appsettings.json. After that, Swagger UI will show summaries written on your application services and controllers. diff --git a/docs/en/Features-React-Tenant-Dashboard.md b/docs/en/Features-React-Tenant-Dashboard.md new file mode 100644 index 00000000..286b6d55 --- /dev/null +++ b/docs/en/Features-React-Tenant-Dashboard.md @@ -0,0 +1,11 @@ +# Tenant Dashboard + +ASP.NET Zero startup project also includes a **sample** dashboard. It's just for demo purposes, you can make it starting point for your actual dashboard. It's implemented with app/main/**dashboard component** in **main module**. + +Dashboard + +Client gets all data from server, server generates random data. + +## Next + +- [Notifications](Features-React-Notifications) diff --git a/docs/en/Features-React-Tenant-Management.md b/docs/en/Features-React-Tenant-Management.md new file mode 100644 index 00000000..350e5fe5 --- /dev/null +++ b/docs/en/Features-React-Tenant-Management.md @@ -0,0 +1,38 @@ +# Tenant Management + +*If you're not developing a multi-tenant application, you can skip this section.* + +If this is a multi-tenant application and you logged in as a host user, then tenants page is shown: + +Tenant management page + +A tenant is represented by **Tenant** class. Tenant class [can be extended](Extending-Existing-Entities.md) by adding new properties. There is an only one tenant, named **Default** as initial. **Tenancy Name** (code name, which can be used as subdomain) is the **unique** name of a tenant. A tenant can be **active** or **passive**. If it's passive, no user of this tenant can login to the application. + +When we click the "**Create New Tenant**" button, a dialog is shown: + +Tenant Creation Modal + +**Tenancy name** should be unique and can not contain spaces or other special chars since it may be used as subdomain name (like tenancyname.mydomain.com). **Name** can be anything. **Admin email** is used as email address of the admin user of new tenant. Admin user is automatically created with the tenant. We can set a random password for admin and send activation email. When user first logins, they should change the password. We can uncheck this to enter a known password. + +When we create a new tenant, we should select/create a database to store new tenant's data. We can select '**Use host database**' to store tenant data in host database (can be used for single database approach) or we can specify a connection string to create/use a **dedicated database** for new tenant. ASP.NET Zero supports **hybrid** approach. That means you can use host database for some tenants and create dedicated databases for some other tenants. Even you can **group** some tenants in a separated database. + +Once you assign an edition to the tenant, you can select an expiration date (see [edition management](Features-React-Edition-Management) section to know what happens after subscription expiration). + +#### Tenant Edition and Features + +An **edition** can be **assigned** to a tenant (while creating or editing). Tenant will inherit all features of the assigned edition, but we can also override features and values for a tenant. Click **actions/change features** for a tenant to **customize** it's features: + +Tenant features + +#### Tenant User Impersonation + +As a host user, we may want to perform operations on behalf of a tenant. In this case, we can click the "**Login as this tenant**" button in the actions. When we click it, we see **a modal to select a user** of the +tenant. We can select any user and perform operations allowed that user. See [User Impersonation](Features-React-User-Management#user-impersonation) section of user management document for more information. + +#### Using Tenancy Name As Subdomain + +A multi-tenant application generally uses subdomain to identify current tenant. **tenant1**.mydomain.com, **tenant2**.mydomain.com and so on. ASP.NET Zero automatically identify and get tenant name from subdomain. See [Multi Tenancy](Overview-React#multi-tenancy) and [Configuration](Overview-React#configuration) sections of overview document. + +## Next + +- [Host Dashboard](Features-React-Host-Dashboard) diff --git a/docs/en/Features-React-Tenant-Settings.md b/docs/en/Features-React-Tenant-Settings.md new file mode 100644 index 00000000..0ca83e54 --- /dev/null +++ b/docs/en/Features-React-Tenant-Settings.md @@ -0,0 +1,63 @@ +# Tenant Settings + +Tenant settings page is used to configure tenant based settings. + +## General + +![General settings](images/tenant-settings-core-general.png) + +Under the general tab, each tenant can configure default time zone setting for users of that tenant. Each user of the tenant can also change this setting for their own account. + +If tenant doesn't make any change, time zone setting of host will be used. + +## Appearance + +![Appearance settings](images/tenant-settings-core-appearance.png) + +Under the appearance tab, each tenant can upload a logo file and upload a custom css file. In this way, each tenant can change the look of the application only for their account. Uploaded logo and css files can be easily removed by using the clear button. + +## User Management + +![User management settings](images/tenant-settings-core-user-management-3.png) + +Under the user management tab, each tenant can configure some user management settings related to their account. Each tenant can enable/disable user registration for their account. Tenants can also make newly registered users for their account active or passive by default. + +Each tenant can also enable/disable captcha on user registration, reset password, email activation and login page for their account. + +> Note: **Token Based Authentication** has `ReCaptchaIgnoreWhiteList` located in `WebConsts`. If you want a client app to be ignored for reCaptcha control during login, add a value to `ReCaptchaIgnoreWhiteList` and send the same value in the `User-Agent` request header for your login request from the client app. You can check the Xamarin mobile app in AspNet Zero to see how `ReCaptchaIgnoreWhiteList` works. + +Each tenant can also enable or disable email restriction for their own account. When enabled, it can specify the email domain allowed for email restriction. + +Each tenant can also enable/disable session timeout control for tenant users. If it is enable and the user does not provide any input to the site during the timeout period, a countdown modal will be displayed to user. If the user still does not provide any input to the site during the modal countdown period, user will be log out. + +Also, each tenant can enable/disable cookie consent so ASP.NET Zero shows a cookie consent bar for the users of that tenant to accept cookie policy of the application. + +Each tenant can force email confirmation for login. + +Each tenant can allow tenant users to use Gravatar profile picture or not. + +## Security + +![Security settings](images/tenant-settings-core-security.png) + +Security tab contains password complexity settings. Each tenant can define password complexity settings in this tab for their account. Each tenant can also configure user lock out settings. + +##### Password + +If MultiTenancyEnabled is set to false, the password expiration settings will apply globally to all active users across the application, rather than being configurable for each tenant. + +You can enable/disable password expiration on the settings page. If you enable it, users will have to change their password after defined days passed. + +You can also prevent user's new password from being same as any of last x passwords. If you enable it, you will need to define how many previous password you want to prevent. Users will not be able to use some of the previously used password as a new password. + +## Invoice + +![Invoice settings](images/tenant-settings-core-invoice.png) + +Under this tab, each tenant can configure their legal name, tax number and address which will be displayed on the generated invoices. Note that, tenants can only generate invoice if they made a payment for a subscription. + +If we disable multi-tenancy, some host settings are also shown in this page (since there is no host setting page). + +## Next + +* [Active Directory](Features-React-Active-Directory) diff --git a/docs/en/Features-React-Tenant-Sign-Up.md b/docs/en/Features-React-Tenant-Sign-Up.md new file mode 100644 index 00000000..9a17c307 --- /dev/null +++ b/docs/en/Features-React-Tenant-Sign-Up.md @@ -0,0 +1,21 @@ +# Tenant Sign Up + +Tenant sign up link is shown on the login form only if you are in the host context. When you click to the link, a registration form is shown like below if there are no Edition defined in the application: + +Tenant registration form + +If there is at least one Edition defined, then user will be redirected to edition selection page: + +Edition selection + +There are two type of editions, free and paid. Paid editions can have trial version. If an edition doesn't have trial version, "Free Trial" button will not be visible for that edition on the edition selection page. All selections on this page will redirect user to Tenant sign up page. If user selects "Buy Now" option, user will be redirected to payment page after the Tenant sign up page. For free and trial options, user will be logged in to system if "**New registered tenants are active by default**" option is enabled under the "**Tenant Management**" tab of Host settings page. If **New registered tenants are active by default** option is not selected, users will be redirected to tenant signup result page. + +If user selects "Buy Now" option and pays for the edition subscription, user will be logged in directly even if the **New registered tenants are active by default** option is not enabled. + +For more information about subscription system, you can check [Subscription](Features-React-Subscription) document. + +## Next + +- [Main Menu and Layout](Features-React-Main-Menu-Layout) + + diff --git a/docs/en/Features-React-Token-Based-Authentication.md b/docs/en/Features-React-Token-Based-Authentication.md new file mode 100644 index 00000000..4c2ab979 --- /dev/null +++ b/docs/en/Features-React-Token-Based-Authentication.md @@ -0,0 +1,33 @@ +# Token Based Authentication + +ASP.NET Zero React UI consumes the [host](Infrastructure-Core-Mvc-Token-Based-Authentication) via token based authentication. Any application can authenticate and use any functionality in the application as API. For instance, you can create a mobile application consumes the same API. In this section, we'll demonstrate usage of the API from [Postman](https://www.getpostman.com/docs/introduction) (a Google Chrome extension). + +## Authentication + +We suggest you to disable two factor authentication for the user which will be used for remote authentication. Otherwise, two factor authentication flow should be implemented by the client. You can check account module source code to understand the flow. We assume that you have disabled two factor authentication for the **admin** user of **default** tenant since we will use it in this sample. + +Following headers should be configured for all requests (`Abp.TenantId` is Id of the default tenant. This is not required for single tenant applications or if you want to work with host users): + +Postman auth headers + +Then we can send username and password as a **POST** request to https://localhost:44301/api/TokenAuth/Authenticate + +SPostman authentication + +In the response, `accessToken` field will be used to authorize for the API. + +## Using API + +After authenticate and get the access token, we can use it to call any +**authorized** actions. All **services** are available to be used +remotely. For example, we can use the **User service** to get a **list +of users**: + +Postman get user list + +We sent a POST request to **https://localhost:44301/api/services/app/User/GetUsers** and added +Authorization to the header as "**Bearer <accessToken>**". And the result is a JSON that contains the list of users. + +## Next + +- [Passwordless Login](Features-Passwordless-Login) diff --git a/docs/en/Features-React-Two-Factor-Authentication.md b/docs/en/Features-React-Two-Factor-Authentication.md new file mode 100644 index 00000000..a4945e86 --- /dev/null +++ b/docs/en/Features-React-Two-Factor-Authentication.md @@ -0,0 +1,80 @@ +# Two Factor Authentication + +ASP.NET Zero is ready to provide two factor login, but it's disabled as default. You can easily enable it in host settings page (in Security tab): + +![Two factor settings](images/lockout-two-factor-settings-2.png) + +Note: In a multi-tenant application, two factor authentication is available to tenants only if it's enabled in the host settings. Also, email verification and SMS verification settings are only available in the host side. This is by design. + +When it's enabled, user is asked to select a verification provider after entering user name and password: + +![Send security code](images/send-security-code-2.png) + +Then a **confirmation code** is sent to the selected provider and user enters the code in the next page: + +![Verify security code](images/verify-security-code-2.png) + +## Email Verification + +This is available if user has a confirmed email address. Since email sending is disabled in debug mode, you can see the code in logs. In release mode, email will be sent. You can change this behaviour in the PreInitialize method of **{YourProjectName}CoreModule.cs**. + +Here is the code block which configures ASP.NET Zero to use NullEmailSender in debug mode: + +```csharp +if (DebugHelper.IsDebug) +{ + //Disabling email sending in debug mode + Configuration.ReplaceService(DependencyLifeStyle.Transient); +} +``` + +## SMS Verification + +This is available if user has a confirmed phone number. In order to validate a phone number, a user should open my settings modal as explained in [here](Features-React-User-Menu#profile-settings). +When the user enters the phone number, a button will appear to validate to phone number. +When the validate button is clicked, an SMS message is sent to entered phone number including a validation code and another modal window is opened for entering the validation code. +When user enters this validation code, the phone number for the user will be set as validated. SMS sending is implemented in ASP.NET Zero using Twilio but an empty SmsSender class is used by default which writes SMS messages to log file. + +If you want to use Twilio for sending SMS on your app, please refer to next section. You can also implement `ISmsSender` interface and use your custom implementation for sending SMS. +In that case, you need to configure ASP.NET Zero to use your custom implementation like below in the PreInitialize method of **{YourProjectName}CoreModule.cs**: + +```csharp +Configuration.ReplaceService(); +``` + +## Authenticator Verification + +This is available if user has enabled two factor authentication in his/her profile. You can enable it in profile page: + +![Enable two factor authenticator app](images/enable-authenticator.png) + +Then user can scan the QR code with an authenticator app (like Google Authenticator) and enter the code in the next page: + +![Authenticator verification](images/authenticator-verification.png) + + +Then user can see his/her recovery codes: + +![Authenticator recovery codes](images/authenticator-recovery-codes.png) + +That's it. Now user use authenticator app to verify himself/herself. + +![Authenticator enabled](images/authenticator-enabled.png) + +You can also disable two factor authentication and view the recovery codes again: + +![Disable two factor authenticator app](images/disable-authenticator.png) + +### Twilio Integration + +In order to enable Twilio integration, just uncomment the following line in your **{YourProjectName}CoreModule** (in your Core project): + +```csharp +Configuration.ReplaceService(); +``` + +You also need to configure **AccountSid**, **AuthToken** and **SenderNumber** in `appsetting.json` file. + +## Next + +* [Forgot Password](Features-React-Forgot-Password) diff --git a/docs/en/Features-React-User-Delegation.md b/docs/en/Features-React-User-Delegation.md new file mode 100644 index 00000000..77322ca6 --- /dev/null +++ b/docs/en/Features-React-User-Delegation.md @@ -0,0 +1,25 @@ +# User Delegation + +ASP.NET ZERO provides a User Delegation feature. User Delegation feature provides users to delegate their accounts for a limited period of time to another user. This way, if a user will not be able to access the application for a period of time, this user can delegate his/her account to another user. + +This feature works like [Impersonation](Features-React-User-Management#user-impersonation) with a limited period. Audit Logs will be saved with Impersonator information, so you can see if an action is executed by actual account owner or by the delegated user. + +User Delegation feature is enabled by default and can be disabled in the constructor of `UserDelegationConfiguration.cs` or in PostInitialize method of `{YourProjectName}CoreModule.cs`. + +If the current user is logged in via user delegation, ASP.NET Zero checks the database to validate the **end time** of the user delegation record. This might be important for performance critic applications but notice that this check is only performed when User Delegation feature is enabled and current user is logged in via User Delegation feature. For other operations, there will be no such control. + +## Manage User Delegations + +User Delegations modal window can be accessed by clicking "Manage authority delegations" on user profile menu. + +User Delegations + + + +User Delegations modal lists currently delegated users. You can delete a user delegation on this modal. + +User Delegations Modal + +"Delegate New User" button opens a modal which creates a new user delegation. + +Audit logs diff --git a/docs/en/Features-React-User-Management.md b/docs/en/Features-React-User-Management.md new file mode 100644 index 00000000..56508ef9 --- /dev/null +++ b/docs/en/Features-React-User-Management.md @@ -0,0 +1,66 @@ +# User Management + +When we click Administration/Users menu, we enter the user management page: + +User management + +**Users** are people who can **login** to the application and perform some operations based on their **permissions**. + +**User** class represents a user. User class [can be extended](Extending-Existing-Entities.md) by adding new properties. + +**UserManager** is used to perform domain logic, **UserAppService** is used to perform application logic for users. + +A user can have zero or more **roles**. If a user has more than one role, the user inherits union of permissions of all these roles. Also, we can set **user-specific permission**. A user specific permission setting overrides role settings for this permission. For example if a user has Accountant role and Accountant role has "editing invoice" permission, we can take away "editing invoice" permission from a specific user if we want to. +The opposite is also true, so we can grant invoice editing permission for a specific user even if none of the roles of that user don't have "editing invoice" permission. + +A screenshot of user permission dialog: + +User Permissions + +*Note that not all permissions shown in the figure above.* + +A dialog is used to create/edit a user: + +Editing User + +We can change user's **password**, make the user **active/passive** and so on... A user can have a **profile picture**. It can be changed by the user (See User Menu section). **Admin** user can not be deleted as a business rule. If you don't want to use admin, just make it inactive. + +### Excel operations + +User list can be downloaded as an Excel file. Also, new users can be imported from an Excel file. In order to import users from an Excel file, ASP.NET Zero requires a specific excel file format. You can download a sample import template on the user list by clicking the "click here" link under the "Excel operations" dropdown button. + +Sample Excel import template link + +When you import users from Excel, if there are errors while importing users to ASP.NET Zero's database, invalid users will be saved to an Excel file with the reason (validation error message or exception message) and the user who made the import operation will be notified via ASP.NET Zero's notification system. So, the user can click the notification and see the result Excel file, fix the validation errors and import the failed users again. + +### User Unlock + +If a user is locked out (had a specific count of failed access), user can be unlocked on the user list page. You can click "Actions" button on the user page and click "**Unlock**" item on the opened dropdown list. + +Lockout options can be configured in the **IdentityRegistrar.cs** class under the "Identity" folder in the ***.Core** project. In the sample below, MaxFailedAccessAttempts is configured as 10, so if a user makes 10 failed login attempts in a row, the user will be locked out for a period of time. + +````csharp +return services.AddAbpIdentity(options => +{ + // other configurations + options.Lockout.MaxFailedAccessAttempts = 10; +}) +```` + +## User Impersonation + +As an admin user (or any allowed user), we may want to login as a user and perform operations on behalf of that user, without knowing the user's password. When we click "**Login as this user**" icon in the actions of a user, we +are automatically redirected and logged in as this user. This is called "**user impersonation**". When we impersonate a user, a "**back to my account**" option is added to the user profile menu: + +Back to my account link + +In an impersonated account, we can only perform operations allowed to that user. It means, everything **exactly** works as same as this user is logged in. The only difference is shown in audit logs which +indicates that operations are performed by somebody else on behalf of the user. + +Notice that also a **red 'back' icon** shown near to the user name to indicate that you are in an impersonated account. + +## Next + +- [Language Management](Features-React-Language-Management) + + diff --git a/docs/en/Features-React-User-Menu.md b/docs/en/Features-React-User-Menu.md new file mode 100644 index 00000000..585c4090 --- /dev/null +++ b/docs/en/Features-React-User-Menu.md @@ -0,0 +1,69 @@ +# User Menu + +A user can click his name at top right corner to open user menu: + +User menu + +## Linked Accounts + +Linked accounts are used to link multiple accounts to each other. In this way, a user can easily navigate through his accounts using this feature. + +User can link new accounts or delete already linked accounts by clicking the **Manage accounts** link. + +User menu + +In order to link a new account, user must enter login credentials of related account. + +link new account + +## Change Password + +User can change the password using this option. **ProfileAppService** is used to change password. + +## Login Attempts + +All login attempts (succeed of failed) are logged in the application. A user can see last login attempts for his/her account. + +Login attempts + +## Change Picture + +A user can change his/her own profile picture. Currently JPG, JPEG, GIF and PNG files are supported. + +A user can also use Gravatar image for profile picture using change profile picture modal window. + +Change profile picture + +## My Settings + +My settings modal is used to change user profile settings: + +User settings + +As shown here, **admin** user name can not be changed. It's considered a special user name since it's used in database migration seed. Other users can change their usernames. + +Users can also validate phone number on this page. If phone number validation is enabled, a "**Confirm**" button will be visible when user enters a phone number into the phone number textbox. When the confirm button is clicked, a confirmation code will be sent to user's phone number (if sms sending is implemented. You can use [Twilio](Features-React-Two-Factor-Authentication#twilio-integration) which is already integrated into ASP.NET Zero) and entering that code into the opened dialog will confirm user's phone number. + +## Download Collected Data + +A user can download his/her collected data using this menu item. + +Login attempts + +ASP.NET Zero includes an interface named **IUserCollectedDataProvider** and there are 3 different implementation for this interface. + +* **ChatUserCollectedDataProvider.cs** creates an excel file with the chat conversations of the user. +* **ProfilePictureUserCollectedDataProvider.cs** creates a image file with the user's profile picture. +* **ProfileUserCollectedDataProvider.cs** creates a text file including user's profile information. + +ASP.NET Zero compress those files into a new zip file and sends it to user as a notification so user can download his/her data. + +If you create a new implementation of **IUserCollectedDataProvider** interface, ASP.NET Zero will automatically include your file into generated zip file as well. + +## Logout + +**TokenAuthController** is used to logout the user. When the user clicks to **Logout** button, user's token is invalidated on the server side and removed from client side and the user is redirected to login page. + +## Next + +- [Setup Page](Features-React-Setup-Page) diff --git a/docs/en/Features-React-Visual-Settings.md b/docs/en/Features-React-Visual-Settings.md new file mode 100644 index 00000000..581927d0 --- /dev/null +++ b/docs/en/Features-React-Visual-Settings.md @@ -0,0 +1,19 @@ +# Visual Settings + +ASP.NET Zero's look of UI can be changed in visual settings page. This page is used to modify look of UI both for system default and personal user accounts. If a user doesn't have permission to see this page, then user will see an item named "Visual Settings" in his personal menu. + +User visual settings + +Users who have permission to see this page will see the same item in the application menu. + +In this page, users can change visual settings for Layout, Header, Menu and Footer of the application. + +Visual settings + +ASP.NET Zero contains all 12 demos of the Metronic theme implemented and users can select one of those themes using the visual settings page. After selecting a theme, some more configuration can be made for the selected theme. + +ASP.NET Zero also contains a quick theme selection icon next to the language selection dropdown on the layout of the application. By clicking this icon, users can easily switch between available themes by using the default settings of the selected theme. If you don't want to use this quick theme selection option in your app, you can login as host admin and disable it under the "other settings" tab of the settings page. + +## Next + +- [Host Settings](Features-React-Host-Settings) diff --git a/docs/en/Infrastructure-Core-React-Identity-Server4-Integration.md b/docs/en/Infrastructure-Core-React-Identity-Server4-Integration.md new file mode 100644 index 00000000..a8e5ac98 --- /dev/null +++ b/docs/en/Infrastructure-Core-React-Identity-Server4-Integration.md @@ -0,0 +1,118 @@ +# Identity Server 4 Integration + +**\*\*IMPORTANT NOTICE\*\*** +Identity Server 4 maintainance stopped on November 2022, see [official announcement](https://identityserver4.readthedocs.io/en/latest/). Because of that, it is removed from ASP.NET Zero. We suggest migrating to OpenIddict. Check out ASP.NET Zero's [OpenIddict integration document](Infrastructure-Core-React-OpenIddict-Integration.md). + +[IdentityServer4](http://identityserver.io/) is an OpenID Connect and OAuth 2.0 framework for ASP.NET Core. ASP.NET Zero is integrated to IdentityServer4. It's **disabled by default**. Its located in `*.Web.Host` project. + +## Configuration + +You can enable/disable or configure it from **appsettings.json** file + +```json +"IdentityServer": { + "IsEnabled": "false", + "Clients": [ + { + "ClientId": "client", + "AllowedGrantTypes": [ "password" ], + "ClientSecrets": [ + { + "Value": "def2edf7-5d42-4edc-a84a-30136c340e13" + } + ], + "AllowedScopes": [ "default-api" ] + }, + { + "ClientId": "demo", + "ClientName": "MVC Client Demo", + "AllowedGrantTypes": [ "hybrid", "client_credentials" ], + "RequireConsent": "true", + "ClientSecrets": [ + { + "Value": "def2edf7-5d42-4edc-a84a-30136c340e13" + } + ], + "RedirectUris": [ "http://openidclientdemo.com:8001/signin-oidc" ], + "PostLogoutRedirectUris": [ "http://openidclientdemo.com:8001/signout-callback-oidc" ], + "AllowedScopes": [ "openid", "profile", "email", "phone", "default-api" ], + "AllowOfflineAccess": "true" + } + ] +} +``` + +## Testing with Client + +ASP.NET Zero solution has a sample console application (ConsoleApiClient) that can connects to the application, authenticates through IdentityServer4 and calls an API. + + + +## Testing with MVC Client + +You can use [aspnet-zero-samples](https://github.com/aspnetzero/aspnet-zero-samples) -> `IdentityServerClient` project to test identity server with MVC client. + +Add a new client to `*.Web.Host` appsettings.json + +```json +... + { + "ClientId": "mvcdemo", + "ClientName": "MVC Client Demo 2", + "AllowedGrantTypes": [ "implicit", "client_credentials" ], + "RequireConsent": "true", + "ClientSecrets": [ + { + "Value": "mysecret" + } + ], + "RedirectUris": [ "http://localhost:62964/signin-oidc" ], + "PostLogoutRedirectUris": [ "http://localhost:62964/signout-callback-oidc" ], + "AllowedScopes": [ "openid", "profile", "email", "phone", "default-api" ], + "AllowOfflineAccess": "true" + } +... +``` + +Download the `IdentityServerClient` project and open it's `Startup.cs` and modify `AddOpenIdConnect` area as seen below + +```csharp +... +.AddOpenIdConnect("oidc", options => +{ + options.SignInScheme = "Cookies"; + + options.Authority = "https://localhost:44301";//change with your project url + options.RequireHttpsMetadata = false; + + options.ClientId = "mvcdemo"; + options.ClientSecret = "mysecret"; + + options.SaveTokens = true; +}); +... +``` + + + +That is all. Now you can test it. + +Run both projects. Go to `IdentityServerClient` project's secure. + +It will redirect you to the login page. + + + +After you successfully login, you will see the consent page. + +After you allow consents, you will redirect to the secure page and get user claims. + +## OpenId Connect Integration + +Once IdentityServer4 integration is enabled Web.Mvc application becomes an OpenId Connect server. That means another web application can use standard OpenId Connect protocol to authenticate users with your +application and get permission to share their information (a.k.a. consent screen). + +## More + +See [IdentityServer4's own documentation](https://identityserver4.readthedocs.io/en/latest/) to understand and configure IdentityServer4. + diff --git a/docs/en/Infrastructure-Core-React-OpenIddict-Integration.md b/docs/en/Infrastructure-Core-React-OpenIddict-Integration.md new file mode 100644 index 00000000..f41f451a --- /dev/null +++ b/docs/en/Infrastructure-Core-React-OpenIddict-Integration.md @@ -0,0 +1,115 @@ +# OpenIddict Integration + +[OpenIddict](https://documentation.openiddict.com/) aims at providing a versatile solution to implement OpenID Connect client, server and token validation support in any ASP.NET Core 2.1 (and higher) application. + +## Configuration + +You can enable/disable or configure it from **appsettings.json** file + +```json +"OpenIddict": { + "IsEnabled": "true", + "Applications": [{ + "ClientId": "client", + "ClientSecret": "def2edf7-5d42-4edc-a84a-30136c340e13", + "DisplayName": "AbpZeroTemplate_App", + "ConsentType": "Explicit", + "RedirectUris": ["https://oauthdebugger.com/debug"], + "PostLogoutRedirectUris": [], + "Scopes": [ + "default-api", + "profile" + ], + "Permissions": [ + "ept:token", + "ept:authorization", + "gt:password", + "gt:client_credentials", + "gt:authorization_code", + "rst:code", + "rst:code id_token" + ] + }] +} +``` + +* **IsEnabled**: Indicates if OpenIddict integration is enabled or not. +* **Applications**: List of OpenIddict applications. + * **ClientId**: The client identifier associated with the current application. + * **ClientSecret**: The client secret associated with the current application. + * **DisplayName**: The display name associated with the current application. + * **ConsentType**: The consent type associated with the current application (see [possible values](https://github.com/openiddict/openiddict-core/blob/dev/src/OpenIddict.Abstractions/OpenIddictConstants.cs#L178)). + * **RedirectUris**: The callback URLs associated with the current application, serialized as a JSON array. + * **PostLogoutRedirectUris**: The logout callback URLs associated with the current application, serialized as a JSON array. + * **Scopes**: The scopes associated with the current authorization, serialized as a JSON array (see [possible values](https://github.com/openiddict/openiddict-core/blob/dev/src/OpenIddict.Abstractions/OpenIddictConstants.cs#L402). You can also use custom values). + * **Permissions**: The permissions associated with the current application, serialized as a JSON array (see [possible values](https://github.com/openiddict/openiddict-core/blob/dev/src/OpenIddict.Abstractions/OpenIddictConstants.cs#L360)). + +## Testing with Client + +ASP.NET Zero solution has a sample console application (ConsoleApiClient) that can connects to the application, authenticates through OpenIddict and calls an API. + + +## Testing with Web Client + +You can use [https://oauthdebugger.com/](https://oauthdebugger.com/) website to test openIddict with a web client. + +Add a new Application to `*.Web.Host` appsettings.json + +```json +... +{ + "ClientId": "client", + "ClientSecret": "def2edf7-5d42-4edc-a84a-30136c340e13", + "DisplayName": "AbpZeroTemplate_App", + "ConsentType": "Explicit", + "RedirectUris": ["https://oauthdebugger.com/debug"], + "PostLogoutRedirectUris": [], + "Scopes": [ + "default-api", + "profile" + ], + "Permissions": [ + "ept:token", + "ept:authorization", + "gt:password", + "gt:client_credentials", + "gt:authorization_code", + "rst:code", + "rst:code id_token" + ] +} +... +``` + +Then, go to [https://oauthdebugger.com/](https://oauthdebugger.com/) and create a URL for authorization code flow. A sample URL should be something like this; + +```bash +https://localhost:44301/connect/authorize +?client_id=client +&redirect_uri=https://oauthdebugger.com/debug +&scope=default-api +&response_type=code +&response_mode=query +&state=krd0ddufuw +&nonce=fbhw5it86l6 +``` + +Visit this URL using a browser. If you are not logged into your ASP.NET Zero application, you will be redirected to Login page. If you are already logged in, you will be redirected back to [https://oauthdebugger.com/](https://oauthdebugger.com/). Here, you will see the result of the request as shown below; + +![oauthdebugger code](images/openiddict_oauthdebugger_code.png) + +You can use this code to request an access token. You need to send a request to [https://localhost:44301/connect/token](https://localhost:44301/connect/token) endpoint. Here is a sample request using Postman. + +![openiddict token endpoint](images/openiddict_token_endpoint.png) + +Using this token, you can get details of the user using [https://localhost:44301/connect/userinfo](https://localhost:44301/connect/userinfo) endpoint or you can make a request to any ASP.NET Zero API service (for example [https://localhost:44301/api/services/app/User/GetUsers](https://localhost:44301/api/services/app/User/GetUsers)). + +## OpenId Connect Integration + +Once OpenIddict integration is enabled, Web.Host application becomes an OpenId Connect server. That means another web application can use standard OpenId Connect protocol to authenticate users with your +application and get permission to share their information (a.k.a. consent screen). + +## More + +See [OpenIddict's own documentation](https://documentation.openiddict.com/) to understand and configure OpenIddict. + diff --git a/docs/en/Infrastructure-React-Authorization.md b/docs/en/Infrastructure-React-Authorization.md new file mode 100644 index 00000000..e67c4c41 --- /dev/null +++ b/docs/en/Infrastructure-React-Authorization.md @@ -0,0 +1,41 @@ +# Authorization + +You can use the `usePermissions` hook to check user permissions in React components. This hook is located at [src/hooks/usePermissions.ts](../src/hooks/usePermissions.ts). + +## Usage + +```tsx +import { usePermissions } from "@/hooks/usePermissions"; + +const MyComponent: React.FC = () => { + const { isGranted, isGrantedAny } = usePermissions(); + + return ( +
+ {isGranted("Pages.Administration.Users") && ( + + )} + + {isGrantedAny("Pages.Administration.Users.Create", "Pages.Administration.Users.Edit") && ( + + )} +
+ ); +}; +``` + +## Available Methods + +| Method | Description | +|--------|-------------| +| `isGranted(permissionName)` | Returns `true` if the user has the specified permission | +| `isGrantedAny(...permissionNames)` | Returns `true` if the user has any of the specified permissions | + +## Permissions Definition + +Permissions are defined on the backend in the `*.Core` project. See: +- [Role Management](Features-React-Role-Management) +- [User Management](Features-React-User-Management) +- [ASP.NET Boilerplate authorization](https://aspnetboilerplate.com/Pages/Documents/Authorization) documentation + + diff --git a/docs/en/Infrastructure-React-Bundling-Minifying-Compiling.md b/docs/en/Infrastructure-React-Bundling-Minifying-Compiling.md new file mode 100644 index 00000000..412f3776 --- /dev/null +++ b/docs/en/Infrastructure-React-Bundling-Minifying-Compiling.md @@ -0,0 +1,28 @@ +# Dynamic Asset Bundling and Minifying + +ASP.NET Zero uses [Vite](https://vitejs.dev/) to build the React application and it creates optimized style and script bundles automatically. But for some cases, an application might decide which style/script file to use at runtime and loads this style/script file dynamically. For such cases, ASP.NET Zero provides a bundling and minification system. + +ASP.NET Zero uses [Gulp](https://gulpjs.com/) for bundling & minifying such dynamic script and style files. + +Bundle definitions are stored in **bundles.json** file. Here is a sample screenshot of **bundles.json** file: + +bundles.json + +**bundles.json** file contains two sections, scripts and styles. + +* **scripts:** This section contains script bundle definitions. Each bundle definition contains two properties **output** and **input**. **output** property contains the file which the bundled script content will be written. **input** property contains the list of scripts which will be bundled. Script files are not minified in development time. +* **styles:** This section contains style bundle definitions. Each bundle definition contains two properties **output** and **input**. **output** property contains the file which the bundled style content will be written. **input** property contains the list of styles which will be bundled. Style files are always minified. You can also use **less** files in the input section of your style bundles. ASP.NET Zero converts the less file into css and adds it to bundle. + +All input sections in **bundles.json** support wildcard syntax. So, you can include all files under a folder (ex: *.js) or all files under a folder and its subfolders (ex: /**/*.css) or you can exclude some files (ex: !wwwroot/**/*.min.css) using wildcard syntax. + +ASP.NET Zero has command for bundling style and script files "**npm run create-dynamic-bundles**". + +* **npm run create-dynamic-bundles**: This command is introduced for development time usage. It automatically updates bundle(s). If you modify **bundles.json** file, you need to re-run this command. It also writes output to console about the bundling progress. Script and style bundles are not minified when using this command. + +For production usage, you can run "**npm run build**" and Vite will minify and optimize all assets for you. + +In the ***Web.Host project**, you can also run ```npm run create-bundles``` to handle bundling and minification of dynamic JavaScript and CSS files in production. + +If you need to make any change about ASP.NET Zero's bundling and minification process, you can modify **gulpfile.js**. + +> Note: Don't use the bundles.json unless you have a dynamic css/js file. Normally, Vite manages your css and js file bundling and optimization. diff --git a/docs/en/Infrastructure-React-Exception-Handling.md b/docs/en/Infrastructure-React-Exception-Handling.md new file mode 100644 index 00000000..c84856f5 --- /dev/null +++ b/docs/en/Infrastructure-React-Exception-Handling.md @@ -0,0 +1,5 @@ +# Exception Handling + +ASP.NET Zero uses ABP's [exception handling](https://aspnetboilerplate.com/Pages/Documents/AspNet-Core#exception-filter) system. Thus, you don't need to handle & care about exceptions in most time. All server side exceptions are gracefully handled and an appropriate message is shown to the user. + + diff --git a/docs/en/Infrastructure-React-Features.md b/docs/en/Infrastructure-React-Features.md new file mode 100644 index 00000000..62329b0d --- /dev/null +++ b/docs/en/Infrastructure-React-Features.md @@ -0,0 +1,31 @@ +# Features + +You can use the global `abp.features` object to check tenant features in your React components. + +## Usage + +```tsx +// Check if a feature is enabled +if (abp.features.isEnabled("App.ChatFeature")) { + // Feature is enabled +} + +// Get feature value +const maxUserCount = abp.features.getValue("App.MaxUserCount"); +``` + +## Using in Menu Items + +You can conditionally show menu items based on features: + +```typescript +{ + id: "Chat", + title: L("Chat"), + icon: "message-text-2", + route: "/app/chat", + featureDependency: () => abp.features.isEnabled("App.ChatFeature"), +} +``` + +Features are defined on the server side in the `*.Core` project. See feature management [documentation](https://aspnetboilerplate.com/Pages/Documents/Feature-Management) for more. diff --git a/docs/en/Infrastructure-React-Localization.md b/docs/en/Infrastructure-React-Localization.md new file mode 100644 index 00000000..2ada7d1b --- /dev/null +++ b/docs/en/Infrastructure-React-Localization.md @@ -0,0 +1,17 @@ +# Localization + +ASP.NET Zero **User Interface** is completely localized. ASP.NET Zero uses **dynamic, database based, per-tenant** localization. + +XML files are used as base translations for desired languages (defined in the server-side); for defining and adding new localization, refer to the [How to Add a New Language in an ASP.NET Zero React Application](Adding-New-Localization-React). + +Localization XML files + +PhoneBook will be your ProjectName. You can add more XML files by copying one XML file and translate to desired language. See [valid culture codes](http://www.csharp-examples.net/culture-names/). + +When you are adding a new localizable text, add it to the XML file of the default language then use in your application (Also, add translated +values to corresponding XML files). + +**Application languages** are defined in the **DefaultLanguagesCreator** class. This is used as seed data in the Entity Framework Migration. So, if you want to **add a new default language**, just add it into the **DefaultLanguagesCreator** class. This operation ensures that the new language is automatically seeded into the database during migration, making it available to the application at startup. This process occurs on the server side. You can refer to the documentation on [How to Add a New Language in an ASP.NET Zero React Application](Adding-New-Localization-React) for further guidance. + +See [localization](https://aspnetboilerplate.com/Pages/Documents/Localization) and [language management](https://aspnetboilerplate.com/Pages/Documents/Zero/Language-Management) documentations for more information on localization. + diff --git a/docs/en/Infrastructure-React-NSwag.md b/docs/en/Infrastructure-React-NSwag.md new file mode 100644 index 00000000..bcb35517 --- /dev/null +++ b/docs/en/Infrastructure-React-NSwag.md @@ -0,0 +1,41 @@ +# NSwag + +Since all communication with the server is made via AJAX requests, we use a client-side TypeScript layer to call server APIs. It's automatically generated by [NSwag](https://github.com/NSwag/NSwag) tool using [Swagger/OpenAPI](http://swagger.io/). ASP.NET Zero solution is properly configured for NSwag. + +## Regenerating Service Proxies + +When you change your server-side services, run the following command while the server-side (.Host project) is running: + +```bash +npm run nswag +``` + +Or alternatively, run **nswag/refresh.bat** (Windows) or the command inside it (non-Windows). + +## Generated Files + +Generated code is located in [src/api/generated/service-proxies.ts](../src/api/generated/service-proxies.ts). You should **not** make manual changes in this file since it will be overwritten on the next code generation. + +## Configuration + +NSwag configuration is in [nswag/service.config.nswag](../nswag/service.config.nswag). Post-processing is handled by [nswag/postprocess.cjs](../nswag/postprocess.cjs). + +## Using Service Proxies + +Import and use service proxies with the `useServiceProxy` hook: + +```tsx +import { useServiceProxy } from "@/api/service-proxy-factory"; +import { UserServiceProxy } from "@api/generated/service-proxies"; + +const MyComponent: React.FC = () => { + const userService = useServiceProxy(UserServiceProxy, []); + + const loadUsers = async () => { + const result = await userService.getUsers(input); + // Handle result + }; + + // ... +}; +``` diff --git a/docs/en/Infrastructure-React-Npm-Packages.md b/docs/en/Infrastructure-React-Npm-Packages.md new file mode 100644 index 00000000..bbfc8d05 --- /dev/null +++ b/docs/en/Infrastructure-React-Npm-Packages.md @@ -0,0 +1,26 @@ +# NPM Packages + +ASP.NET Zero React UI uses [NPM](https://www.npmjs.com/) to manage front-end library dependencies (like React and Ant Design). You can easily add, update, or remove packages using npm's command line interface. + +## Common Commands + +```bash +# Install all dependencies +npm install + +# Add a new package +npm install package-name + +# Add a dev dependency +npm install package-name --save-dev + +# Update packages +npm update + +# Remove a package +npm uninstall package-name +``` + +## Package.json + +Dependencies are defined in [package.json](../package.json). After modifying dependencies, run `npm install` to update your `node_modules` folder. diff --git a/docs/en/Infrastructure-React-React-CLI-WebPack.md b/docs/en/Infrastructure-React-React-CLI-WebPack.md new file mode 100644 index 00000000..2d92eab5 --- /dev/null +++ b/docs/en/Infrastructure-React-React-CLI-WebPack.md @@ -0,0 +1,36 @@ +# Vite Build Tool + +ASP.NET Zero React UI uses [Vite](https://vitejs.dev/) for development and production builds. Vite provides fast development server with Hot Module Replacement (HMR) and optimized production builds. + +## Development + +To run the application in development mode, open a terminal and run: + +```bash +npm run dev +``` + +Once compiled and ready, you can open the application in your browser at . + +## Production Build + +To create a production build: + +```bash +npm run build +``` + +This creates an optimized build in the `dist/` folder. + +## Preview Production Build + +To preview the production build locally: + +```bash +npm run preview +``` + +## Configuration + +Vite configuration is in [vite.config.ts](../vite.config.ts). See the [Vite documentation](https://vitejs.dev/) for more configuration options. + diff --git a/docs/en/Infrastructure-React-Setting.md b/docs/en/Infrastructure-React-Setting.md new file mode 100644 index 00000000..c102c9f1 --- /dev/null +++ b/docs/en/Infrastructure-React-Setting.md @@ -0,0 +1,25 @@ +# Setting + +You can use the global `abp.setting` object to get setting values in your React components. + +## Usage + +```tsx +// Get a setting value +const settingValue = abp.setting.get("SettingName"); + +// Get a boolean setting +const boolValue = abp.setting.getBoolean("SettingName"); + +// Get an integer setting +const intValue = abp.setting.getInt("SettingName"); +``` + +Settings are defined on the server: + +* [Host Settings](Features-React-Host-Settings) +* [Tenant Settings](Features-React-Tenant-Settings) + +See setting management [documentation](https://aspnetboilerplate.com/Pages/Documents/Setting-Management) for more. + + diff --git a/docs/en/Infrastructure-React-SignalR-Integration.md b/docs/en/Infrastructure-React-SignalR-Integration.md new file mode 100644 index 00000000..27809c1e --- /dev/null +++ b/docs/en/Infrastructure-React-SignalR-Integration.md @@ -0,0 +1,10 @@ +# SignalR Integration + +SignalR is properly configured and integrated to the startup project. +Real time notification and chat systems use it. You can also direcly use +SignalR in your applications. + +See [SignalR +integration](https://aspnetboilerplate.com/Pages/Documents/SignalR-AspNetCore-Integration) +document for more information. + diff --git a/docs/en/Infrastructure-React-Spinner.md b/docs/en/Infrastructure-React-Spinner.md new file mode 100644 index 00000000..b4f695a8 --- /dev/null +++ b/docs/en/Infrastructure-React-Spinner.md @@ -0,0 +1,89 @@ +# Spinner / Loading States + +ASP.NET Zero React UI uses [Ant Design's Spin component](https://ant.design/components/spin) for showing loading states. + +## Route Loading Spinner + +When lazy-loaded routes are being loaded, a centered spinner is shown. This is implemented in [AppRouter.tsx](../src/routes/AppRouter.tsx): + +```tsx +import { Spin } from "antd"; + +const LoadingSpinner = () => ( +
+ +
+); + +// Used with React.Suspense +}> + + {/* ... */} + + +``` + +## Table Loading States + +Ant Design's Table component has a built-in `loading` prop: + +```tsx +import { Table } from "antd"; + +const MyTable: React.FC = () => { + const [loading, setLoading] = useState(false); + const [data, setData] = useState([]); + + const fetchData = async () => { + setLoading(true); + try { + const result = await service.getData(); + setData(result); + } finally { + setLoading(false); + } + }; + + return ; +}; +``` + +## Element-Level Spinners + +For wrapping specific elements with a loading overlay: + +```tsx +import { Spin } from "antd"; + +const MyComponent: React.FC = () => { + const [loading, setLoading] = useState(true); + + return ( + +
+ {/* Content that will be covered by spinner */} +
+
+ ); +}; +``` + +## Customizing Spinners + +You can customize the Spin component appearance using Ant Design's props: + +```tsx + +``` + +Check [Ant Design Spin documentation](https://ant.design/components/spin) for more customization options. diff --git a/docs/en/Infrastructure-React-Tenancy-Resolvers.md b/docs/en/Infrastructure-React-Tenancy-Resolvers.md new file mode 100644 index 00000000..f1cf35ef --- /dev/null +++ b/docs/en/Infrastructure-React-Tenancy-Resolvers.md @@ -0,0 +1,27 @@ +# Tenancy Resolvers + +ASP.NET Boilerplate has built in tenancy resolvers for server side, see [documentation](https://aspnetboilerplate.com/Pages/Documents/Multi-Tenancy#determining-current-tenant) for more information. But, in some scenarios, you may want to determine the current tenant on the client application. For example, ASP.NET Zero's React template is deployed separately from its ASP.NET Core API app. + +Because of this, ASP.NET Zero provides built-in tenancy resolvers for its React application. These are; + +* SubdomainTenantResolver +* QueryStringTenantResolver +* CookieTenantResolver + +By default, these resolvers are executed in the given order. If a tenant resolver finds the tenant, it then sets `Abp.TenantId` cookie value, so the server side app can use this to determine the current tenant as well. + +## SubdomainTenantResolver + +Finds the tenancy name from subdomain by using the app's configured URL. For example, if the app's URL is configured as `https://{TENANCY_NAME}.mywebsite.com` and visited url is `https://tenant1.mywebsite.com`, then tenant1 will be returned as tenancy name. + +## QueryStringTenantResolver + +Looks for `abp_tenancy_name` query string parameter and returns its value for tenancy name if `abp_tenancy_name` parameter is present in current query string. + +## CookieTenantResolver + +Looks for `abp_tenancy_name` cookie value and returns its value for tenancy name if `abp_tenancy_name` cookie value is present. + +You can also implement similar classes and use them in the bootstrap process of [AppPreBootstrap.ts](../src/app/bootstrap/AppPreBootstrap.ts) to determine the current tenant. + + diff --git a/docs/en/Infrastructure-React-Used-Libraries-Frameworks.md b/docs/en/Infrastructure-React-Used-Libraries-Frameworks.md new file mode 100644 index 00000000..63995194 --- /dev/null +++ b/docs/en/Infrastructure-React-Used-Libraries-Frameworks.md @@ -0,0 +1,56 @@ +## Used Libraries & Frameworks + +Many open source frameworks and libraries are used to build the ASP.NET Zero React UI project. Here's the list of main libraries: + +### Core Framework +- [React](https://react.dev/) - UI library +- [Vite](https://vitejs.dev/) - Build tool and dev server +- [TypeScript](http://typescriptlang.org/) - Type-safe JavaScript + +### UI Components & Styling +- [Ant Design](https://ant.design/) - UI component library +- [Metronic Theme](http://keenthemes.com/metronic/) - Admin theme +- [Bootstrap](http://getbootstrap.com/) - CSS framework +- [React Bootstrap](https://react-bootstrap.github.io/) - Bootstrap React components + +### State Management & Routing +- [Redux Toolkit](https://redux-toolkit.js.org/) - State management +- [React Redux](https://react-redux.js.org/) - React bindings for Redux +- [React Router](https://reactrouter.com/) - Client-side routing + +### Forms & Validation +- [React Hook Form](https://react-hook-form.com/) - Form handling + +### Data & API +- [Axios](https://axios-http.com/) - HTTP client +- [NSwag](https://github.com/NSwag/NSwag) - API client generation +- [SignalR](https://docs.microsoft.com/en-us/aspnet/core/signalr/) - Real-time communication + +### Internationalization +- [i18next](https://www.i18next.com/) - Internationalization +- [React-i18next](https://react.i18next.com/) - React i18n integration +- [React Intl](https://formatjs.io/docs/react-intl/) - Internationalization + +### Date & Time +- [Day.js](https://day.js.org/) - Date manipulation +- [Flatpickr](https://flatpickr.js.org/) - Date picker + +### Charts & Visualization +- [Ant Design Plots](https://charts.ant.design/) - Charting library + +### Utilities +- [Lodash](https://lodash.com/) - Utility functions +- [LocalForage](https://github.com/localForage/localForage) - Offline storage +- [SweetAlert2](https://sweetalert2.github.io/) - Alerts and modals +- [Push.js](https://github.com/Nickersoft/push.js) - Desktop notifications +- [React Grid Layout](https://github.com/react-grid-layout/react-grid-layout) - Draggable grid + +### Icons +- [Font Awesome](http://fontawesome.io/) - Icon library +- [Bootstrap Icons](https://icons.getbootstrap.com/) - Icon library +- [Line Awesome](https://icons8.com/line-awesome) - Icon library +- [Famfamfam Flags](https://github.com/legacy-icons/famfamfam-flags/) - Flag icons + +### Image & Media +- [React Easy Crop](https://github.com/ValentinH/react-easy-crop) - Image cropping +- [React Quill](https://github.com/zenoamaro/react-quill) - Rich text editor diff --git a/docs/en/Migration-from-Boilerplate-to-AspNet-Zero-React.md b/docs/en/Migration-from-Boilerplate-to-AspNet-Zero-React.md new file mode 100644 index 00000000..3cd9e370 --- /dev/null +++ b/docs/en/Migration-from-Boilerplate-to-AspNet-Zero-React.md @@ -0,0 +1,478 @@ +# Migration Guide: Transitioning from ASP.NET Boilerplate to ASP.NET Zero + +This is a step-by-step guide to migrate your React application from ASP.NET Boilerplate to ASP.NET Zero. + +## Introduction + +The purpose of this migration guide is to provide a comprehensive, step-by-step process for migrating from ASP.NET Boilerplate to ASP.NET Zero in the context of an React application. This guide aims to help developers understand the differences between the two applications, ensure a smooth migration, and leverage the advanced features of ASP.NET Zero to improve application performance and maintainability. + +## Creating and Migrating Entities + +Let's consider an entity called `Book` that was previously created in an ASP.NET Boilerplate application as follows: + +```c# +public class Book : Entity, IHasCreationTime +{ + public string Name { get; set; } + public string Description { get; set; } + public DateTime CreationTime { get; set; } + + public Book() + { + CreationTime = Clock.Now; + } +} +``` + +### Transitioning to ASP.NET Zero + +When migrating this entity to ASP.NET Zero, you can create a new class within the `*.Core` project, just like you did in your ASP.NET Boilerplate application. This ensures consistency and simplifies integration into your ASP.NET Zero solution. + +## Configuring DbContext + +To integrate the `*DbContext` added entity in ASP.NET Boilerplate into ASP.NET Zero, add it to the `*DbContext` class in the `*.EntityFrameworkCore` project as shown below: + +```c# +public class AngularProjectDemoDbContext : AbpZeroDbContext, IOpenIddictDbContext +{ + /* Define an DbSet for each entity of the application */ + + public virtual DbSet Books { get; } + + ... + + public AngularProjectDemoDbContext(DbContextOptions options) + : base(options) + { + } + + ... +} +``` + +## Database Migrations + +To add the entity added to the `*DbContext` to the database table, first, we need to create a migration. You can follow the exact same steps applied in ASP.NET Boilerplate using the **Package Manager Console**. After opening it, select the project to be applied in the Default Project section and create a migration with the specified command. + +![Adding Migration in Package Manager Console](images/adding-migration.png) + +After applying this command, it is sufficient to apply the following command to reflect the created migration to the database. + +![Update Database in Package Manager Console](images/updating-database.png) + +### Migrating Databases Between Applications + +When transitioning from an ASP.NET Boilerplate application to an ASP.NET Zero application, it is essential to consider how the database migration will be handled. Since both frameworks typically use Microsoft SQL Server (MSSQL) as the database management system, the migration process can be streamlined by leveraging existing migration tools and practices. Given that both frameworks utilize the ABP framework, many of the tables will have similar characteristics, which simplifies the migration process. + +Here are the key steps to ensure a smooth database migration: + +- Analyze Existing Database Schema: Start by analyzing the existing database schema in the ASP.NET Boilerplate application. Identify the entities, relationships, and constraints that need to be migrated. + +- Generate Migrations in ASP.NET Zero: Use the Entity Framework Core migration tools to generate the necessary migrations in the ASP.NET Zero application. This involves creating an initial migration that mirrors the existing database schema. + +- Data Migration: Ensure that data is accurately migrated from the ASP.NET Boilerplate database to the ASP.NET Zero database. + +- Test the Migration: Thoroughly test the migration process in a staging environment. Verify that all entities and data have been correctly migrated and that the application functions as expected. + +For more detailed information and best practices on using Entity Framework Core with MSSQL, refer to the official Microsoft documentation on [SQL Server migration](https://learn.microsoft.com/en-us/sql/sql-server/migrate/?view=sql-server-ver16) and [copying databases](https://learn.microsoft.com/en-us/sql/relational-databases/databases/copy-databases-to-other-servers?view=sql-server-ver16). Additionally, [this guide](https://www.sqlshack.com/six-different-methods-to-copy-tables-between-databases-in-sql-server/) provides a comprehensive overview of different methods to copy tables between databases in SQL Server, offering practical examples and steps. + +> When migrating data from an ASP.NET Boilerplate database to an ASP.NET Zero database, ensure that all existing data is accurately transferred. Testing the migration process in a staging environment is crucial to ensure that the migration is successful and that the application functions as expected after the migration. + +## Implementing Application Services + +This chapter covers the implementation of application services in ASP.NET Zero, focusing on creating service interfaces, DTOs (Data Transfer Objects), and their associated services class. + +### Creating DTOs and Defining AppService Interfaces + +In this section, creating application service classes requires defining necessary interfaces and DTOs. Similar to ASP.NET Boilerplate, however, interfaces and DTOs created here will be managed in the `*.Application.Shared` project instead of `*.Application` for easier organization and accessibility. + +**IBookAppService** manages general CRUD operations for the **Book** entity, defined with the following method signatures: + +```c# +public interface IBookAppService: IApplicationService +{ + Task> GetBooks(GetBooksInput input); + + Task GetBookForEdit(NullableIdDto input); + + Task CreateOrUpdateBook(CreateOrUpdateBookInput input); + + Task DeleteBook(EntityDto input); +} +``` + +In ASP.NET Zero, DTOs like `CreateOrUpdateBookInput`, `GetBooksInput`, `BookListDto`, and `GetBookForEditOutput` are typically organized under the `*.Application.Shared` project, specifically under a folder structure such as **Books/Dto**. + +You can also use the same dto structure that you created in ASP.NET Boilerplate in the ASP.NET Zero application. + +Like ASP.NET Boilerplate, you can create interfaces in ASP.NET Zero in a similar way. + +## Managing Permissions + +In ASP.NET Zero applications, it is important to manage permissions, as in ASP.NET Boilerplate, to control access to various features and functions within the application. Permissions are typically defined as constants in a central static class called AppPermissions. + +```c# +public static class AppPermissions +{ + ... + + public const string Pages_Administration_Books = "Pages.Administration.Books"; + public const string Pages_Administration_Books_Create = "Pages.Administration.Books.Create"; + public const string Pages_Administration_Books_Edit = "Pages.Administration.Books.Edit"; + public const string Pages_Administration_Books_Delete = "Pages.Administration.Books.Delete"; + + ... +} +``` + +In ASP.NET Zero applications, one distinction from ASP.NET Boilerplate in app service classes lies in CRUD operations where permission control is managed. Here, to access general app service functionality, a permission named `Pages_Administration_Books` is created, encompassing permissions for **Create**, **Edit**, and **Delete** operations. For these methods, separate permission checks can optionally be applied in ASP.NET Boilerplate applications as well. This approach aims to facilitate easier management and controlled access, emphasizing structured handling of permissions. + +### Implementing App Service Classes + +Create an app service class created in an `*.Application` project, as created in ASP.NET Boilerplate, that will implement the **IBookAppService** interface. + +```c# + [AbpAuthorize(AppPermissions.Pages_Administration_Books)] + public class BookAppService : AngularProjectDemoAppServiceBase, IBookAppService + { + private readonly IRepository _bookRepository; + + public BookAppService(IRepository bookRepository) + { + _bookRepository = bookRepository; + } + + public async Task CreateOrUpdateBook(CreateOrUpdateBookInput input) + { + if (input.Book.Id.HasValue) + { + await UpdateBookAsync(input); + } + else + { + await CreateBookAsync(input); + } + } + + + [AbpAuthorize(AppPermissions.Pages_Administration_Books_Delete)] + public async Task DeleteBook(EntityDto input) + { + var book = await _bookRepository.GetAsync(input.Id); + + await _bookRepository.DeleteAsync(book); + } + + + [AbpAuthorize(AppPermissions.Pages_Administration_Books_Create, AppPermissions.Pages_Administration_Books_Edit)] + public async Task GetBookForEdit(NullableIdDto input) + { + BookEditDto bookListDto; + + if (input.Id.HasValue) + { + var book = await _bookRepository.GetAsync(input.Id.Value); + bookListDto = ObjectMapper.Map(book); + } + else + { + bookListDto = new BookEditDto(); + } + + return new GetBookForEditOutput + { + Book = bookListDto + }; + } + + [HttpPost] + public async Task> GetBooks(GetBooksInput input) + { + var query = _bookRepository.GetAll(); + + query = query + .WhereIf(!input.Filter.IsNullOrEmpty(), + b => b.Name.Contains(input.Filter) + ); + + var books = await query.ToListAsync(); + + return new ListResultDto(ObjectMapper.Map>(books)); + } + + [AbpAuthorize(AppPermissions.Pages_Administration_Books_Edit)] + protected virtual async Task UpdateBookAsync(CreateOrUpdateBookInput input) + { + var book = await _bookRepository.GetAsync(input.Book.Id.Value); + book.Name = input.Book.Name; + book.Description = input.Book.Description; + } + + [AbpAuthorize(AppPermissions.Pages_Administration_Books_Create)] + protected virtual async Task CreateBookAsync(CreateOrUpdateBookInput input) + { + var book = ObjectMapper.Map(input.Book); + + await _bookRepository.InsertAsync(book); + } + } +``` + +This **BookAppService** class demonstrates how CRUD operations for books are implemented in an ASP.NET Zero application, following a straightforward approach similar to ASP.NET Boilerplate. + +## Navigation and Page Management + +In ASP.NET Zero React, routes are defined in [src/routes/AppRouter.tsx](../src/routes/AppRouter.tsx). This file uses React Router to define all application routes with lazy loading for better performance. + +**Adding a New Route (ASP.NET Zero React)** + +```tsx +// In src/routes/AppRouter.tsx + +// Import your component with lazy loading +const BooksPage = React.lazy( + () => import("@/pages/admin/books/index") +); + +// Add the route inside the Routes component + + + + } +/> +``` + +### Adding Navigation Menu Items + +To add navigation items, modify the `buildRawMenu` function in [src/lib/navigation/appNavigation.tsx](../src/lib/navigation/appNavigation.tsx): + +**appNavigation.tsx (ASP.NET Zero React)** + +```tsx +export const buildRawMenu = (): AppMenuItem[] => [ + // ... existing items + { + id: "Books", + title: L("Books"), + permissionName: "Pages.Administration.Books", + icon: "book", + route: "/app/admin/books", + }, + // ... more items +]; +``` + +The `AppMenuItem` interface includes: +- `id`: Unique identifier +- `title`: Display name (use `L()` for localization) +- `permissionName`: Required permission to show this menu item +- `icon`: Keenicons icon name +- `route`: React Router path + + +## Building the User Interface + +The pages and modals created here show similar features. Since ASP.NET Zero uses the Metronic theme, it is recommended that you review [Metronic Theme](https://preview.keenthemes.com/metronic8/demo1/index.html?mode=light) to adapt it. + +### Creating Components + +In ASP.NET Zero React, you create pages as React functional components. Unlike ASP.NET Boilerplate which uses Angular modules, React uses a simple file-based structure with components organized in folders. + +**Creating a Books Page (ASP.NET Zero React)** + +Create your page component at `src/pages/admin/books/index.tsx`: + +```tsx +import { useState, useEffect } from "react"; +import { Button, Table, Modal, message } from "antd"; +import { useServiceProxy } from "@/api/service-proxy-factory"; +import { usePermissions } from "@/hooks/usePermissions"; +import { L } from "@/lib/L"; +import CreateOrEditBookModal from "./components/CreateOrEditBookModal"; + +export default function BooksPage() { + const [loading, setLoading] = useState(false); + const [data, setData] = useState([]); + const [isModalOpen, setIsModalOpen] = useState(false); + const [selectedBookId, setSelectedBookId] = useState(); + + const { bookService } = useServiceProxy(); + const { isGranted } = usePermissions(); + + const fetchBooks = async () => { + setLoading(true); + try { + const result = await bookService.getAll({}); + setData(result.items || []); + } finally { + setLoading(false); + } + }; + + useEffect(() => { + fetchBooks(); + }, []); + + const handleDelete = async (id: number) => { + Modal.confirm({ + title: L("AreYouSure"), + onOk: async () => { + await bookService.delete({ id }); + message.success(L("SuccessfullyDeleted")); + fetchBooks(); + }, + }); + }; + + return ( +
+
+

{L("Books")}

+ {isGranted("Pages.Administration.Books.Create") && ( + + )} +
+
+
+ {/* Define your columns */} +
+ + + { + setIsModalOpen(false); + setSelectedBookId(undefined); + }} + onSaved={() => { + setIsModalOpen(false); + setSelectedBookId(undefined); + fetchBooks(); + }} + /> + + ); +} +``` + +### Key Differences from ASP.NET Boilerplate + +| Feature | ASP.NET Boilerplate (Angular) | ASP.NET Zero React | +|---------|-------------------------------|-------------------| +| UI Library | Bootstrap, ngx-bootstrap | Ant Design | +| Modals | BsModalService | Ant Design Modal component | +| Tables | HTML tables | Ant Design Table with built-in pagination | +| Notifications | abp.message, abp.notify | Ant Design message, Modal.confirm | +| Permissions | Angular guards | `usePermissions()` hook | +| State Management | Component state | React hooks + Redux Toolkit | +| File Structure | Modules with routing | File-based pages in `src/pages/` | + +### Creating Modal Components + +In ASP.NET Zero React, create and edit operations are typically handled in a single modal component: + +**CreateOrEditBookModal.tsx** + +```tsx +import { useEffect } from "react"; +import { Modal, Form, Input, message } from "antd"; +import { useServiceProxy } from "@/api/service-proxy-factory"; +import { L } from "@/lib/L"; + +interface Props { + bookId?: number; + open: boolean; + onClose: () => void; + onSaved: () => void; +} + +export default function CreateOrEditBookModal({ bookId, open, onClose, onSaved }: Props) { + const [form] = Form.useForm(); + const { bookService } = useServiceProxy(); + const isEditMode = !!bookId; + + useEffect(() => { + if (open && bookId) { + // Fetch and populate form for editing + bookService.getForEdit({ id: bookId }).then((result) => { + form.setFieldsValue(result); + }); + } else { + form.resetFields(); + } + }, [open, bookId]); + + const handleSave = async () => { + const values = await form.validateFields(); + if (isEditMode) { + await bookService.update({ ...values, id: bookId }); + } else { + await bookService.create(values); + } + message.success(L("SavedSuccessfully")); + onSaved(); + }; + + return ( + +
+ + + + {/* Add more form fields */} +
+
+ ); +} +``` + +## Adding Localization + +As in ASP.NET Boilerplate, localization resources in ASP.NET Zero are created under the *.Core project within the Localization folder. In ASP.NET Zero, the source files are located in a folder named after your project, such as AngularProjectDemo. You can add entries to the AngularProjectDemo.xml file or any other language file as shown below: + +```xml + + + ... + Books + Books Header Info + Create New Book + + +``` + + +## Implementing Unit Tests + +Unit tests implemented in ASP.NET Boilerplate show minor changes in ASP.NET Zero. + +**Main Differences:** + +**ASP.NET Boilerplate:** More manual setup with Effort and custom fixtures. +**ASP.NET Zero:** Uses ASP.NET Core’s DI and in-memory database providers for simpler and more consistent setup. + +**ASP.NET Boilerplate:** Uses Effort for simulating an in-memory database. +**ASP.NET Zero:** Uses SQL Server or Entity Framework Core’s in-memory database for easier configuration. + +**Transition Steps:** + +- Transition from base class TestBase to AppTestBase provided by ASP.NET Zero. +- Replace Effort with Entity Framework Core’s in-memory database provider. +- Ensure dependencies are resolved using ASP.NET Core’s DI framework. +- Adapt test methods to utilize ASP.NET Zero’s testing utilities and patterns. + + +## Conclusion + +The migration from ASP.NET Boilerplate to ASP.NET Zero involves several important transitions in various aspects of application development. This process includes changes in the management of models, configuration of app services and unit tests. ASP.NET Zero offers advanced features that increase efficiency and scalability by providing a more integrated and streamlined approach to these elements. diff --git a/docs/en/Playwright-UI-Testing-React.md b/docs/en/Playwright-UI-Testing-React.md new file mode 100644 index 00000000..6b2a03d2 --- /dev/null +++ b/docs/en/Playwright-UI-Testing-React.md @@ -0,0 +1,105 @@ +# UI Tests + +ASP.NET Zero provides an infrastructure for UI tests using [Playwright](https://playwright.dev/). You can check Playwright documentation to learn more about its features. + +## Project Setup & Structure + +You should have the following installed in your system: + +- [Node.js](https://nodejs.org/en/) - Please make sure you have v18+ (LTS recommended) in your system before you start. + +After making sure Node.js is installed, follow the steps below: + +1. Open your terminal. +2. Navigate to the `ui-tests-playwright` folder of this project. +3. Run `npm install`. + +There are two parts of the project that requires your close attention. + +- The `playwright.config.ts` is the configuration file for Playwright. You can check [https://playwright.dev/docs/test-configuration](https://playwright.dev/docs/test-configuration) to learn its options. +- The `tests` folder includes the actual tests and utilities to create them with less effort. **This is where you will place your test suites.** + +Another important file you need to know about is the `.env` file. This file contains website URL to test, a username and a password to login. + +After installing dependencies, the project will be ready-to-run. + +### Ruuning the tests + +Before running the UI tests, be sure that your Host and React apps are running with a brand new database. + +When you run the UI tests, a screenshot will be created for each UI test and this screenshot will be compared to new ones for the following executions of the same UI test. + +Because of that, if you are running the UI tests for the first time, you must add `--update-snapshots` parameter to your test execution command. + +```shell +npx playwright test --update-snapshots +``` + +For the following executions, you need to run same command without `--update-snapshots` parameter; + +```shell +npx playwright test +``` + +This command runs all tests, but sometimes you may want to run a single test or a test group. For such cases, insert the relative file path to the end of the same command for that. + +```shell +npx playwright test React/editions +``` + +If you want to run all specs for one project + +```shell +npx playwright test React +``` + +In your own repository, you can store screenshots in your repository and this will allow you to easily run UI tests for your app. + +## How to Create Tests + +Please examine `tests/React/editions.spec.ts` to see an example spec. + +There are two ways to create a test. + +- Write directives to Playwright manually. +- Use the Playwright CLI to generate code. + +ASP.NET Zero's default UI tests are executed in serial. So, tests are executed one by one and next test uses the state of previous test. We suggest using this approach when you are writing a UI test in your project as well. + +Also, ASP.NET ZEro's default UI tests removes all data created during the UI tests. For example, this UI test creates a new Role with the name 'test' (Input fields are filled in a previous step which is not shown here). + +````typescript +/* Step 5 */ +test('should save record when "Save" button is clicked', async () => { + await rolesPage.saveForm(); + await rolesPage.waitForResponse(); + await rolesPage.replaceLastColoumnOfTable(); + + await expect(page).toHaveScreenshot(ROLES_CRUD_NEW_SAVE); +}); +```` + +And after that, this final step in Role UI test group removes the created record. + +````typescript +/* Step 12 */ +test('should delete record on click to "Yes" button', async () => { + await rolesPage.openActionsDropdown(2); + await rolesPage.waitForDropdownMenu(); + await rolesPage.triggerDropdownAction('Delete'); + await rolesPage.waitForConfirmationDialog(); + await rolesPage.confirmConfirmation(); + await rolesPage.waitForResponse(); + await rolesPage.replaceLastColoumnOfTable(); + + await expect(page).toHaveScreenshot(ROLES_CRUD_DELETE_CONFIRM); +}); +```` + +In this way, you can run your UI test during development. + +**Tip:** If you want to see the browser when tests are running, set `headless: true` in **playwright.config.ts**. + +**Tip:** If you use VSCode to write your tests you can use debugging. + + diff --git a/docs/en/Security-Test-React.md b/docs/en/Security-Test-React.md new file mode 100644 index 00000000..38da54c9 --- /dev/null +++ b/docs/en/Security-Test-React.md @@ -0,0 +1,121 @@ +# Security Test with OWASP ZAP + +Security is an important aspect for software applications. ASP.NET Zero includes ready to use files for testing the security of its web application using [OWASP ZAP](https://www.zaproxy.org/) Tool. + +To test the web application, you need to install following tools; + +* [Docker](https://www.docker.com/) +* [Helm](https://helm.sh/) +* [NGINX ingress](https://kubernetes.github.io/ingress-nginx/deploy/) for k8s +* [Owasp ZAP Tool](https://www.zaproxy.org/) + +After installing the Docker, be sure that you enabled the Kubernetes on Docker by following [https://docs.docker.com/desktop/kubernetes/](https://docs.docker.com/desktop/kubernetes/). + +## Deploying App to Kubernetes + +The security test will be runned on an instance of the app which works in Kubernetes. To run our application on Kubernetes, ASP.NET Zero includes Helm charts out of the box. To run our web application on Kubernetes, we must create 3 Docker images. + +To do that, go to folder which contains *.Web.sln file and execute the command below (Don't forget to replace MyCompanyName.AbpZeroTemplate with your project name); + +```bash +docker build -t abpzerotemplate-host -f src\MyCompanyName.AbpZeroTemplate.Web.Host\Dockerfile . +``` + +This command will create a Docker image for the Host API application. Our application also needs to connect to a database. We will be using the *.Migrator project to create and seed our app's database. So, we need to create a docker image for our Migrator app as well. To do that, run the command below in the same directory (Don't forget to replace MyCompanyName.AbpZeroTemplate with your project name); + +```bash +docker build -t abpzerotemplate-migrator -f src\MyCompanyName.AbpZeroTemplate.Migrator\Dockerfile . +``` + +We will not run a security test on the client side React application, we will only run security test on API application but our Helm chart includes the React app as well. Because of that, we need to create an image for the React app as well. To do that, first build your React app using the command below; + +```` +npm run build +```` + +When the build is completed, run the following command in the React project folder; + +```` +docker build -t abpzerotemplate-react -f Dockerfile . +```` + +Now, we have all Docker images we need to run our application. + +Open a command prompt and go to "aspnet-core\etc\k8s\helm-chart" under your project directory and execute the Helm command below; + +``` +helm upgrade --install anz abpzerotemplate-React +``` + +This command will create 4 pods on Kubernetes, 1 for SQL Server, 1 for Host API application, 1 for React application and 1 for the migrator app. The default Helm chart uses SQL Server but you can change it if your app uses a different database provider. + +To access your app from your local computer, go to your Host file and add the entry below. You can use a different URL if you wish but don't forget to use that URL in the following steps. + +```bash +127.0.0.1 abpzerotemplate-host-www +127.0.0.1 abpzerotemplate-React-www +``` + +If you are not using Windows OS, you need to do the same for your OS. + +Now, if you go to a browser and type [https://abpzerotemplate-host-www/swagger](https://abpzerotemplate-host-www/swagger), you can visit the Host API application. You can visit the React app on [https://abpzerotemplate-React-www](https://abpzerotemplate-React-www). + +### About Nginx Ingress Controller + +By default, nginx doesn't allow "." character in request headers. We need to change ```ignore_invalid_headers``` setting for nginx. To do that, follow the steps below: + +- Run ```kubectl get pods -A``` command and find the ingress-nginx pod name. It should have a name similar to ```ingress-nginx-controller-756f546d89-52r5t```. + +- Then run the command below to enter to this pod: + + ```bash + kubectl exec -it ingress-nginx-controller-756f546d89-52r5t -n ingress-nginx sh + ``` + +- In this pod, run ```vi nginx.conf```. This will print content of nginx.conf and will allow you to edit its content. + +- Go to the line which contains **ignore_invalid_headers** setting by using arrow keys. Click ```i``` key to enter to edit mode and change **on** value to **off**. Now, the setting should be like this: + + + +Nginx ignore-invalid-headers. + +* press ```:w``` to save the changes and then press ```:q!``` to exit from edit mode. +* Finally run ```nginx -s reload``` to restart the Nginx to load this change. + +After all, Nginx ingress controller will allow AspNet Zero app tp use "." in request headers. + + +## Preparing ZAP for Testing + +Open the OWASP ZAP application and load the script from "aspnet-core\etc\owasp\abpzerotemplate-React\abpzerotemplate-React-http-sender-script.js" by clicking the scripts area and then load icon as shown below; + +Load Zest Script + +When the script load window appears, select the properties as shown below; + +Load Zest Script Properties + +This script will be used to automatically add "authorization" header to each request. The script itself contains "ACCESS_TOKEN_HERE" placeholder for the token value. So, you need to go to [https://abpzerotemplate-host-www/swagger/](https://abpzerotemplate-host-www/swagger/) endpoint and get a token from "TokenAuth/Authenticate" endpoint and replace this placeholder. + +Don't forget to enable the HTTP Sender script after importing it. Otherwise, OWASP ZAP can't make authorized requests to your API endpoints. + +Load OWASP Context + + + + + +## Include OpenAPI Definitions + +OWASP ZAP can import OpenAPI definition from an OpenAPI json file. To do that, select "Import > Import an OpenAPI definition from a URL" and enter "https://abpzerotemplate-host-www/swagger/v1/swagger.json" as Definition and "https://abpzerotemplate-host-www" as the URL. + +## Testing the App + +Finally, we can start testing the app. To do that, right click on the your site and Click Attack > Active Scan and select the context we imported before and click "Start Scan" button. + +### Report + +The test process might take a long time depending on the API endpoint count. When the test finishes, you can go to Report menu on ZAP Tool and generate a report as you wish. + + diff --git a/docs/en/Step-by-step-publish-to-azure-React-staticsite.md b/docs/en/Step-by-step-publish-to-azure-React-staticsite.md new file mode 100644 index 00000000..430a1193 --- /dev/null +++ b/docs/en/Step-by-step-publish-to-azure-React-staticsite.md @@ -0,0 +1,122 @@ +# Introduction + +In this article we will use [Azure Storage Static site](https://docs.microsoft.com/en-us/azure/storage/blobs/storage-blob-static-website) feature to deploy the **React UI** site. + +## Why use Azure Storage + +**React UI** is a static website, therefore it can be deployed in Azure storage which provides features specifically designed for static websites, such as Custom Domains and SSL. +Also, using Azure Storage is much [cheaper](https://azure.microsoft.com/en-us/pricing/details/storage/) than deploying an App Service on Azure. + +## Steps + +- Create Storage Account +- Enable Static Website feature +- **OPTIONAL** Enable Custom Domain +- Publish files to Azure Storage + +### Create Storage Account + +Follow this article: [Create a storage account](https://docs.microsoft.com/en-us/azure/storage/common/storage-quickstart-create-account) +and make sure that you are using at least **StorageV2 (general purpose v2)** account kind. + +### Enable Static Site feature + +Go to the newly created storage account and navigate to **Static website** from the side menu. + +*if you do not see this option: make sure that you are using at least **StorageV2 (general purpose v2)** account kind.* + +- Set **Static website**: Enabled +- Set **Index document name**: index.html +- Set **Error document path**: index.html + +### OPTIONAL: Custom Domain + +You can use Azure Storage Static website feature to have your Custom domain redirected to it with SSL enabled. + +For that to work, you must serve the contents of the static website from Azure CDN. + +Follow [this article](https://docs.microsoft.com/en-us/azure/storage/blobs/storage-https-custom-domain-cdn) to create Azure CDN end point that will serve the contents over HTTPS on a custom domain. + +Remember to update the settings in `.env.production` file below to reflect the new URL for your static site, as now they will be served from the CDN over a custom domain. + +Then add the task as described in the RELEASE pipeline below, to purge the cache on every release. + +#### Update Configuration + +Update the **HOST** appsettings.json with the following: + +- ClientRootAddress: replaced with the static website primary end point ex: https://xxxxxx.z33.web.core.windows.net +- CorsOrigins: add the static website primary end point + +Update the **React UI** `.env.production` with the following: + +```env +VITE_API_URL=https://your-api-host.azurewebsites.net +``` + +### Publish files to Azure Storage + +Once you enable the static website feature on Azure storage, it will automatically create a special container with the name **$web**. + +*note you cannot change this name.* + +It is assumed that you already have a *dist* folder built and ready for publishing. + +#### Manual Publishing + +You can manually upload your *dist* files using [Azure Storage Explorer](https://azure.microsoft.com/en-us/features/storage-explorer/) to the *$web* container. + +#### Automated publishing using Azure Pipelines + +We will create a **RELEASE** pipeline here to do the following: + +- Pick up the *drop* folder from the **BUILD** pipeline or any other source +- Delete everything that exists in **$web** *(always clean the storage container before we upload a new version)* +- **OPTIONAL (with custom domain)** purge the Azure CDN cache if using a custom domain +- Publish the files to the **$web** container + +#### Steps to create a Release Pipeline + +- Go to [Azure DevOps](https://dev.azure.com) +- Click ````Pipelines```` from the side menu +- Click ````Releases```` +- Click ````New```` > ````New release pipeline```` +- Click ````Empty job```` +- Click ````Add an artifact```` and select the source of the **React UI** dist folder +- Click on ````Stage 1```` jobs link +- Click ````+```` to add a new Task to the Agent job +- Add the following tasks *(in this order)* + - Azure CLI + - Azure CLI (optional with custom domain) + - Azure File Copy +- configure the tasks settings as below + +##### Azure CLI: Settings + +- Name: Delete all $web container files +- Azure Subscription: ````(select your subscription)```` +- Script Location: ````Inline Script```` +- Inline Script + - ````az storage blob delete-batch --account-name [STORAGE-ACCOUNT-NAME] --source '$web'```` + +##### OPTIONAL (with custom domain): Azure CLI: Settings + +- Name: Purge the Azure CDN cache end point +- Azure Subscription: ````(select your subscription)```` +- Script Location: ````Inline Script```` +- Inline Script + - ````az cdn endpoint purge -n [AZURE-CDN-END-POINT-NAME] -g [AZURE-CDN-RESOURCE-GROUP-NAME] --profile-name [AZURE-CDN-PROFILE-NAME] --content-paths "/*"```` + +##### Azure File Copy: Settings + +*note: (switch to Task Version 3 if it is not the default).* + +- Name: Copy new files to $web container +- Source: ````(select the source of the drop folder)```` +- Azure Subscription: ````(select your subscription)```` +- Destination Type: ````Azure Blob```` +- RM Storage Account: ````(storage account name)```` +- Container Name: ````$web```` + +That's it, now you can queue a release. +