Compare commits
7 Commits
892b2183e0
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 7f728b821e | |||
| 0ad0534b22 | |||
| c4071786ea | |||
| 72b7902b55 | |||
| 7b1c4c27fe | |||
| 9b45752d1a | |||
| be2599218d |
13
.idea/.idea.ASPReactDemo/.idea/.gitignore
generated
vendored
Normal file
13
.idea/.idea.ASPReactDemo/.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Rider ignored files
|
||||
/projectSettingsUpdater.xml
|
||||
/contentModel.xml
|
||||
/modules.xml
|
||||
/.idea.ASPReactDemo.iml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
||||
4
.idea/.idea.ASPReactDemo/.idea/encodings.xml
generated
Normal file
4
.idea/.idea.ASPReactDemo/.idea/encodings.xml
generated
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Encoding" addBOMForNewFiles="with BOM under Windows, with no BOM otherwise" />
|
||||
</project>
|
||||
6
.idea/.idea.ASPReactDemo/.idea/git_toolbox_blame.xml
generated
Normal file
6
.idea/.idea.ASPReactDemo/.idea/git_toolbox_blame.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="GitToolBoxBlameSettings">
|
||||
<option name="version" value="2" />
|
||||
</component>
|
||||
</project>
|
||||
15
.idea/.idea.ASPReactDemo/.idea/git_toolbox_prj.xml
generated
Normal file
15
.idea/.idea.ASPReactDemo/.idea/git_toolbox_prj.xml
generated
Normal file
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="GitToolBoxProjectSettings">
|
||||
<option name="commitMessageIssueKeyValidationOverride">
|
||||
<BoolValueOverride>
|
||||
<option name="enabled" value="true" />
|
||||
</BoolValueOverride>
|
||||
</option>
|
||||
<option name="commitMessageValidationEnabledOverride">
|
||||
<BoolValueOverride>
|
||||
<option name="enabled" value="true" />
|
||||
</BoolValueOverride>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
8
.idea/.idea.ASPReactDemo/.idea/indexLayout.xml
generated
Normal file
8
.idea/.idea.ASPReactDemo/.idea/indexLayout.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="UserContentModel">
|
||||
<attachedFolders />
|
||||
<explicitIncludes />
|
||||
<explicitExcludes />
|
||||
</component>
|
||||
</project>
|
||||
6
.idea/.idea.ASPReactDemo/.idea/vcs.xml
generated
Normal file
6
.idea/.idea.ASPReactDemo/.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.SignalR" Version="1.2.0" />
|
||||
<PackageReference Include="NSwag.AspNetCore" Version="14.2.0" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -1,20 +1,24 @@
|
||||
using Api.SignalR;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
// Add services to the container.
|
||||
// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi
|
||||
builder.Services.AddEndpointsApiExplorer();
|
||||
builder.Services.AddOpenApi();
|
||||
builder.Services.AddOpenApiDocument(config =>
|
||||
{
|
||||
config.Title = "NSwag Demo API";
|
||||
});
|
||||
builder.Services.AddSignalR();
|
||||
builder.Services.AddHostedService<SignalRSendService>();
|
||||
builder.Services.AddSingleton<IUserIdProvider, UserProvider>();
|
||||
builder.Services.AddOpenApiDocument(config => { config.Title = "NSwag Demo API"; });
|
||||
builder.Services.AddCors(options =>
|
||||
{
|
||||
options.AddDefaultPolicy(policy =>
|
||||
{
|
||||
policy.AllowAnyOrigin()
|
||||
.AllowAnyHeader()
|
||||
.AllowAnyMethod();
|
||||
policy.WithOrigins("http://localhost:5173")
|
||||
.AllowAnyHeader()
|
||||
.AllowAnyMethod()
|
||||
.AllowCredentials();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -24,8 +28,8 @@ var app = builder.Build();
|
||||
if (app.Environment.IsDevelopment())
|
||||
{
|
||||
app.MapOpenApi();
|
||||
app.UseOpenApi();
|
||||
app.UseSwaggerUi();
|
||||
app.UseOpenApi();
|
||||
app.UseSwaggerUi();
|
||||
}
|
||||
|
||||
app.UseHttpsRedirection();
|
||||
@@ -36,23 +40,36 @@ var summaries = new[]
|
||||
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
|
||||
};
|
||||
|
||||
app.MapGet("/weatherforecast", () =>
|
||||
{
|
||||
var forecast = Enumerable.Range(1, 5).Select(index =>
|
||||
new WeatherForecast
|
||||
(
|
||||
DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
|
||||
Random.Shared.Next(-20, 55),
|
||||
summaries[Random.Shared.Next(summaries.Length)]
|
||||
))
|
||||
.ToArray();
|
||||
return forecast;
|
||||
})
|
||||
.WithName("GetWeatherForecast");
|
||||
app.MapGet("/weatherforecast", async () =>
|
||||
{
|
||||
var hub = app.Services.GetRequiredService<IHubContext<WeatherUpdateHub>>();
|
||||
for (var i = 0; i < 100; i++)
|
||||
{
|
||||
await hub.Clients.All.SendAsync("ProgressUpdate", "None", $$"""{"percentage": "{{i}}", "message": "{{DateTime.Now.Millisecond}}"}""");
|
||||
await Task.Delay(100);
|
||||
}
|
||||
|
||||
|
||||
var forecast = Enumerable.Range(1, 2500).Select(index =>
|
||||
new WeatherForecast
|
||||
(
|
||||
DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
|
||||
Random.Shared.Next(-20, 55),
|
||||
summaries[Random.Shared.Next(summaries.Length)]
|
||||
))
|
||||
.ToArray();
|
||||
|
||||
return forecast;
|
||||
})
|
||||
.WithName("GetWeatherForecast");
|
||||
|
||||
|
||||
// Add SignalR hub
|
||||
app.MapHub<WeatherUpdateHub>("/WeatherUpdateHub");
|
||||
|
||||
app.Run();
|
||||
|
||||
record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary)
|
||||
{
|
||||
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
|
||||
}
|
||||
}
|
||||
31
Api/SignalR/SignalRSendService.cs
Normal file
31
Api/SignalR/SignalRSendService.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
|
||||
namespace Api.SignalR
|
||||
{
|
||||
public class SignalRSendService(IHubContext<WeatherUpdateHub> hubContext, ILogger<SignalRSendService> logger) : BackgroundService
|
||||
{
|
||||
|
||||
#region Override ExecuteAsync
|
||||
|
||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||
{
|
||||
while (!stoppingToken.IsCancellationRequested)
|
||||
{
|
||||
// For example, wait 10 seconds between messages.
|
||||
await Task.Delay(TimeSpan.FromSeconds(2), stoppingToken);
|
||||
|
||||
// Log or do any work here.
|
||||
logger.LogInformation("Background service sending message at: {Time}", DateTime.Now);
|
||||
|
||||
// Send a message to all connected clients.
|
||||
await hubContext.Clients.All.SendAsync(
|
||||
"ReceiveMessage", // This is the client method name.
|
||||
"Background Service", // Example sender.
|
||||
$"Hello from the background task at {DateTime.Now:F}", // The message.
|
||||
stoppingToken);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
14
Api/SignalR/UserProvider.cs
Normal file
14
Api/SignalR/UserProvider.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
|
||||
namespace Api.SignalR
|
||||
{
|
||||
public class UserProvider : IUserIdProvider
|
||||
{
|
||||
public string GetUserId(HubConnectionContext connection)
|
||||
{
|
||||
// Extract the "userId" query parameter from the HTTP context.
|
||||
var httpContext = connection.GetHttpContext();
|
||||
return httpContext?.Request.Query["userId"].FirstOrDefault();
|
||||
}
|
||||
}
|
||||
}
|
||||
20
Api/SignalR/WeatherUpdateHub.cs
Normal file
20
Api/SignalR/WeatherUpdateHub.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
|
||||
namespace Api.SignalR
|
||||
{
|
||||
public class WeatherUpdateHub: Hub
|
||||
{
|
||||
// This method can be called by connected clients.
|
||||
public async Task SendMessage(string user, string message)
|
||||
{
|
||||
// Broadcast the message to all connected clients.
|
||||
await Clients.All.SendAsync("ReceiveMessage", user, message);
|
||||
}
|
||||
|
||||
public async Task SendProgressUpdate(string message)
|
||||
{
|
||||
// Send a message to the caller.
|
||||
await Clients.Caller.SendAsync("ProgressUpdate", message);
|
||||
}
|
||||
}
|
||||
}
|
||||
111
README.md
111
README.md
@@ -1,59 +1,92 @@
|
||||
# ASPReactDemo
|
||||
<h1> ASP Net Core, Nswag, SignalR, React, Syncfusion </h1>
|
||||
|
||||
Eine Demo für:
|
||||
-ASP Net Core
|
||||
-Nswag
|
||||
-SignalR
|
||||
-React
|
||||
-Syncfusion
|
||||
- [Installation React with Vite and TypeScript](#installation-react-with-vite-and-typescript)
|
||||
- [Routing](#routing)
|
||||
- [Install react-router-dom](#install-react-router-dom)
|
||||
- [Update App.tsx](#update-apptsx)
|
||||
- [Update configuration files](#update-configuration-files)
|
||||
- [Update css file](#update-css-file)
|
||||
- [DaisyUI 5](#daisyui-5)
|
||||
- [Install daisyUI](#install-daisyui)
|
||||
- [Update css file](#update-css-file-1)
|
||||
|
||||
## Installation React
|
||||
|
||||
### With Yarn
|
||||
|
||||
# Installation React with Vite and TypeScript
|
||||
|
||||
```bash
|
||||
yarn create react-app react-client --template typescript
|
||||
# Create a new project using the "react-ts" template
|
||||
yarn create vite my-react-app --template react-ts
|
||||
|
||||
cd react-client
|
||||
# Navigate to the project folder
|
||||
cd my-react-app
|
||||
|
||||
yarn build
|
||||
# Install dependencies
|
||||
yarn install
|
||||
|
||||
yarn start
|
||||
```
|
||||
## Installation tailwindcss
|
||||
|
||||
### Install and Init
|
||||
# Routing
|
||||
|
||||
## Install react-router-dom
|
||||
```bash
|
||||
yarn add tailwindcss postcss autoprefixer
|
||||
yarn add react-router-dom
|
||||
```
|
||||
|
||||
npx tailwindcss init -p
|
||||
## Update App.tsx
|
||||
```tsx
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# Optional Tools
|
||||
## tailwindcss
|
||||
|
||||
### Install tailwindcss
|
||||
```bash
|
||||
# Install tailwindcss
|
||||
yarn add tailwindcss@latest postcss@latest autoprefixer@latest
|
||||
```
|
||||
|
||||
### Update configuration files
|
||||
```typescript
|
||||
# vite.config.ts
|
||||
|
||||
import { defineConfig } from 'vite'
|
||||
import tailwindcss from '@tailwindcss/vite' // Add this line
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
tailwindcss(), //Add this line
|
||||
],
|
||||
})
|
||||
|
||||
```
|
||||
|
||||
### Configure the tailwind.config.js File
|
||||
|
||||
Update the tailwind.config.js file to specify the paths to all of your template files. Replace the default content with the following (if not already present):
|
||||
|
||||
```javascript
|
||||
module.exports = {
|
||||
purge: ['./src/**/*.{js,jsx,ts,tsx}', './public/index.html'],
|
||||
darkMode: false, // or 'media' or 'class'
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
variants: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [],
|
||||
}
|
||||
```
|
||||
|
||||
### Add Tailwind directives to your CSS:
|
||||
|
||||
### Update css file
|
||||
|
||||
```css
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
/* eg. src/index.css */
|
||||
|
||||
@import "tailwindcss";
|
||||
|
||||
/* Add your custom styles here */
|
||||
```
|
||||
|
||||
|
||||
## DaisyUI 5
|
||||
|
||||
### Install daisyUI
|
||||
```bash
|
||||
# Install daisyUI
|
||||
yarn add -D daisyui@beta
|
||||
```
|
||||
### Update css file
|
||||
|
||||
```css
|
||||
/* eg. src/index.css */
|
||||
@import "tailwindcss"; /* Existing line */
|
||||
@plugin "daisyui";
|
||||
/* Add your custom styles here */
|
||||
```
|
||||
|
||||
8
react-client/.idea/.gitignore
generated
vendored
Normal file
8
react-client/.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
||||
4
react-client/.idea/encodings.xml
generated
Normal file
4
react-client/.idea/encodings.xml
generated
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Encoding" addBOMForNewFiles="with BOM under Windows, with no BOM otherwise" />
|
||||
</project>
|
||||
6
react-client/.idea/git_toolbox_blame.xml
generated
Normal file
6
react-client/.idea/git_toolbox_blame.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="GitToolBoxBlameSettings">
|
||||
<option name="version" value="2" />
|
||||
</component>
|
||||
</project>
|
||||
6
react-client/.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
6
react-client/.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
</profile>
|
||||
</component>
|
||||
8
react-client/.idea/modules.xml
generated
Normal file
8
react-client/.idea/modules.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/react-client.iml" filepath="$PROJECT_DIR$/.idea/react-client.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
12
react-client/.idea/react-client.iml
generated
Normal file
12
react-client/.idea/react-client.iml
generated
Normal file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="WEB_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/temp" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/tmp" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
15
react-client/.idea/runConfigurations/start.xml
generated
Normal file
15
react-client/.idea/runConfigurations/start.xml
generated
Normal file
@@ -0,0 +1,15 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="start" type="js.build_tools.npm" nameIsGenerated="true">
|
||||
<package-json value="$PROJECT_DIR$/package.json" />
|
||||
<command value="run" />
|
||||
<scripts>
|
||||
<script value="start" />
|
||||
</scripts>
|
||||
<node-interpreter value="project" />
|
||||
<envs />
|
||||
<EXTENSION ID="com.intellij.lang.javascript.buildTools.npm.rc.StartBrowserRunConfigurationExtension">
|
||||
<browser name="37cae5b9-e8b2-4949-9172-aafa37fbc09c" start="true" with-js-debugger="true" url="http://localhost:3000/" />
|
||||
</EXTENSION>
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
||||
6
react-client/.idea/vcs.xml
generated
Normal file
6
react-client/.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
@@ -3,6 +3,7 @@
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@microsoft/signalr": "^8.0.7",
|
||||
"@testing-library/jest-dom": "^5.14.1",
|
||||
"@testing-library/react": "^13.0.0",
|
||||
"@testing-library/user-event": "^13.2.1",
|
||||
|
||||
@@ -2,32 +2,61 @@ import React, { useEffect, useState } from 'react';
|
||||
import logo from './logo.svg';
|
||||
import './App.css';
|
||||
import { Client, WeatherForecast } from './ApiCient';
|
||||
import { HubConnection, HubConnectionBuilder } from '@microsoft/signalr';
|
||||
|
||||
function App() {
|
||||
|
||||
const [forecast, setForecast] = useState<WeatherForecast[] | undefined>([]);
|
||||
|
||||
const [connection, setConnection] = useState<HubConnection | null>(null);
|
||||
const [message, setMessage] = useState<string>('');
|
||||
|
||||
useEffect(() => {
|
||||
const client = new Client();
|
||||
const newConnection = new HubConnectionBuilder()
|
||||
.withUrl('http://localhost:5175/WeatherUpdateHub?userId=user1234')
|
||||
.withAutomaticReconnect()
|
||||
.build();
|
||||
|
||||
const fetchForecast = async () => {
|
||||
const forecast = await client.getWeatherForecast();
|
||||
setForecast(forecast);
|
||||
};
|
||||
|
||||
fetchForecast();
|
||||
const intervalId = setInterval(fetchForecast, 1000);
|
||||
|
||||
return () => clearInterval(intervalId);
|
||||
setConnection(newConnection);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (connection) {
|
||||
connection
|
||||
.start()
|
||||
.then(() => {
|
||||
console.log('Connected to SignalR hub!');
|
||||
connection.on('ReceiveMessage', (user, message) => {
|
||||
setMessage(message);
|
||||
console.log('Received message:', user, message);
|
||||
});
|
||||
})
|
||||
.catch(error => console.error('SignalR Connection Error: ', error));
|
||||
}
|
||||
}, [connection]);
|
||||
|
||||
|
||||
|
||||
|
||||
// useEffect(() => {
|
||||
// const client = new Client();
|
||||
|
||||
// const fetchForecast = async () => {
|
||||
// const forecast = await client.getWeatherForecast();
|
||||
// setForecast(forecast);
|
||||
// };
|
||||
|
||||
// fetchForecast();
|
||||
// const intervalId = setInterval(fetchForecast, 1000);
|
||||
|
||||
// return () => clearInterval(intervalId);
|
||||
// }, []);
|
||||
|
||||
|
||||
return (
|
||||
<div className="App">
|
||||
{/* <header className="App-header">
|
||||
<img src={logo} className="App-logo" alt="logo" />
|
||||
<p>
|
||||
</div> <p>
|
||||
Edit <code>src/App.tsx</code> and save to reload.
|
||||
</p>
|
||||
<a
|
||||
@@ -49,6 +78,10 @@ function App() {
|
||||
setForecast(forecast);
|
||||
}} > Call API</button>
|
||||
|
||||
<div className="text-red-50">
|
||||
{message}
|
||||
</div>
|
||||
|
||||
|
||||
{forecast?.map((item: WeatherForecast) => {
|
||||
return (
|
||||
|
||||
@@ -1557,6 +1557,17 @@
|
||||
resolved "https://registry.yarnpkg.com/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz#4fc56c15c580b9adb7dc3c333a134e540b44bfb1"
|
||||
integrity sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==
|
||||
|
||||
"@microsoft/signalr@^8.0.7":
|
||||
version "8.0.7"
|
||||
resolved "https://registry.yarnpkg.com/@microsoft/signalr/-/signalr-8.0.7.tgz#94419ddbf9418753e493f4ae4c13990316ec2ea5"
|
||||
integrity sha512-PHcdMv8v5hJlBkRHAuKG5trGViQEkPYee36LnJQx4xHOQ5LL4X0nEWIxOp5cCtZ7tu+30quz5V3k0b1YNuc6lw==
|
||||
dependencies:
|
||||
abort-controller "^3.0.0"
|
||||
eventsource "^2.0.2"
|
||||
fetch-cookie "^2.0.3"
|
||||
node-fetch "^2.6.7"
|
||||
ws "^7.4.5"
|
||||
|
||||
"@nicolo-ribaudo/eslint-scope-5-internals@5.1.1-v1":
|
||||
version "5.1.1-v1"
|
||||
resolved "https://registry.yarnpkg.com/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz#dbf733a965ca47b1973177dc0bb6c889edcfb129"
|
||||
@@ -2639,6 +2650,13 @@ abab@^2.0.3, abab@^2.0.5:
|
||||
resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.6.tgz#41b80f2c871d19686216b82309231cfd3cb3d291"
|
||||
integrity sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==
|
||||
|
||||
abort-controller@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392"
|
||||
integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==
|
||||
dependencies:
|
||||
event-target-shim "^5.0.0"
|
||||
|
||||
accepts@~1.3.4, accepts@~1.3.8:
|
||||
version "1.3.8"
|
||||
resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e"
|
||||
@@ -4712,6 +4730,11 @@ etag@~1.8.1:
|
||||
resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
|
||||
integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==
|
||||
|
||||
event-target-shim@^5.0.0:
|
||||
version "5.0.1"
|
||||
resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789"
|
||||
integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==
|
||||
|
||||
eventemitter3@^4.0.0:
|
||||
version "4.0.7"
|
||||
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f"
|
||||
@@ -4722,6 +4745,11 @@ events@^3.2.0:
|
||||
resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400"
|
||||
integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==
|
||||
|
||||
eventsource@^2.0.2:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/eventsource/-/eventsource-2.0.2.tgz#76dfcc02930fb2ff339520b6d290da573a9e8508"
|
||||
integrity sha512-IzUmBGPR3+oUG9dUeXynyNmf91/3zUSJg1lCktzKw47OXuhco54U3r9B7O4XX+Rb1Itm9OZ2b0RkTs10bICOxA==
|
||||
|
||||
execa@^5.0.0:
|
||||
version "5.1.1"
|
||||
resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd"
|
||||
@@ -4852,6 +4880,14 @@ fb-watchman@^2.0.0:
|
||||
dependencies:
|
||||
bser "2.1.1"
|
||||
|
||||
fetch-cookie@^2.0.3:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/fetch-cookie/-/fetch-cookie-2.2.0.tgz#01086b6b5b1c3e08f15ffd8647b02ca100377365"
|
||||
integrity sha512-h9AgfjURuCgA2+2ISl8GbavpUdR+WGAM2McW/ovn4tVccegp8ZqCKWSBR8uRdM8dDNlx5WdKRWxBYUwteLDCNQ==
|
||||
dependencies:
|
||||
set-cookie-parser "^2.4.8"
|
||||
tough-cookie "^4.0.0"
|
||||
|
||||
file-entry-cache@^6.0.1:
|
||||
version "6.0.1"
|
||||
resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027"
|
||||
@@ -7069,6 +7105,13 @@ node-addon-api@^7.0.0:
|
||||
resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-7.1.1.tgz#1aba6693b0f255258a049d621329329322aad558"
|
||||
integrity sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==
|
||||
|
||||
node-fetch@^2.6.7:
|
||||
version "2.7.0"
|
||||
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d"
|
||||
integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==
|
||||
dependencies:
|
||||
whatwg-url "^5.0.0"
|
||||
|
||||
node-forge@^1:
|
||||
version "1.3.1"
|
||||
resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3"
|
||||
@@ -8769,6 +8812,11 @@ serve-static@1.16.2:
|
||||
parseurl "~1.3.3"
|
||||
send "0.19.0"
|
||||
|
||||
set-cookie-parser@^2.4.8:
|
||||
version "2.7.1"
|
||||
resolved "https://registry.yarnpkg.com/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz#3016f150072202dfbe90fadee053573cc89d2943"
|
||||
integrity sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==
|
||||
|
||||
set-function-length@^1.2.2:
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449"
|
||||
@@ -9480,6 +9528,11 @@ tr46@^2.1.0:
|
||||
dependencies:
|
||||
punycode "^2.1.1"
|
||||
|
||||
tr46@~0.0.3:
|
||||
version "0.0.3"
|
||||
resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"
|
||||
integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==
|
||||
|
||||
tryer@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/tryer/-/tryer-1.0.1.tgz#f2c85406800b9b0f74c9f7465b81eaad241252f8"
|
||||
@@ -9799,6 +9852,11 @@ web-vitals@^2.1.0:
|
||||
resolved "https://registry.yarnpkg.com/web-vitals/-/web-vitals-2.1.4.tgz#76563175a475a5e835264d373704f9dde718290c"
|
||||
integrity sha512-sVWcwhU5mX6crfI5Vd2dC4qchyTqxV8URinzt25XqVh+bHEPGH4C3NPrNionCP7Obx59wrYEbNlw4Z8sjALzZg==
|
||||
|
||||
webidl-conversions@^3.0.0:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"
|
||||
integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==
|
||||
|
||||
webidl-conversions@^4.0.2:
|
||||
version "4.0.2"
|
||||
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad"
|
||||
@@ -9950,6 +10008,14 @@ whatwg-mimetype@^2.3.0:
|
||||
resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf"
|
||||
integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==
|
||||
|
||||
whatwg-url@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d"
|
||||
integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==
|
||||
dependencies:
|
||||
tr46 "~0.0.3"
|
||||
webidl-conversions "^3.0.0"
|
||||
|
||||
whatwg-url@^7.0.0:
|
||||
version "7.1.0"
|
||||
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-7.1.0.tgz#c2c492f1eca612988efd3d2266be1b9fc6170d06"
|
||||
@@ -10250,7 +10316,7 @@ write-file-atomic@^3.0.0:
|
||||
signal-exit "^3.0.2"
|
||||
typedarray-to-buffer "^3.1.5"
|
||||
|
||||
ws@^7.4.6:
|
||||
ws@^7.4.5, ws@^7.4.6:
|
||||
version "7.5.10"
|
||||
resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.10.tgz#58b5c20dc281633f6c19113f39b349bd8bd558d9"
|
||||
integrity sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==
|
||||
|
||||
24
react-vite-client/.gitignore
vendored
Normal file
24
react-vite-client/.gitignore
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
12
react-vite-client/.run/build.run.xml
Normal file
12
react-vite-client/.run/build.run.xml
Normal file
@@ -0,0 +1,12 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="build" type="js.build_tools.npm" nameIsGenerated="true">
|
||||
<package-json value="$PROJECT_DIR$/package.json" />
|
||||
<command value="run" />
|
||||
<scripts>
|
||||
<script value="build" />
|
||||
</scripts>
|
||||
<node-interpreter value="project" />
|
||||
<envs />
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
||||
17
react-vite-client/.run/dev.run.xml
Normal file
17
react-vite-client/.run/dev.run.xml
Normal file
@@ -0,0 +1,17 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="dev" type="js.build_tools.npm" nameIsGenerated="true">
|
||||
<package-json value="$PROJECT_DIR$/package.json" />
|
||||
<command value="run" />
|
||||
<scripts>
|
||||
<script value="dev" />
|
||||
</scripts>
|
||||
<node-interpreter value="project" />
|
||||
<envs />
|
||||
<EXTENSION ID="com.intellij.lang.javascript.buildTools.npm.rc.StartBrowserRunConfigurationExtension">
|
||||
<browser name="37cae5b9-e8b2-4949-9172-aafa37fbc09c" start="true" with-js-debugger="true" url="http://localhost:5173/" />
|
||||
</EXTENSION>
|
||||
<method v="2">
|
||||
<option name="RunConfigurationTask" enabled="true" run_configuration_name="build" run_configuration_type="js.build_tools.npm" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
50
react-vite-client/README.md
Normal file
50
react-vite-client/README.md
Normal file
@@ -0,0 +1,50 @@
|
||||
# React + TypeScript + Vite
|
||||
|
||||
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
|
||||
|
||||
Currently, two official plugins are available:
|
||||
|
||||
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
|
||||
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
|
||||
|
||||
## Expanding the ESLint configuration
|
||||
|
||||
If you are developing a production application, we recommend updating the configuration to enable type aware lint rules:
|
||||
|
||||
- Configure the top-level `parserOptions` property like this:
|
||||
|
||||
```js
|
||||
export default tseslint.config({
|
||||
languageOptions: {
|
||||
// other options...
|
||||
parserOptions: {
|
||||
project: ['./tsconfig.node.json', './tsconfig.app.json'],
|
||||
tsconfigRootDir: import.meta.dirname,
|
||||
},
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
- Replace `tseslint.configs.recommended` to `tseslint.configs.recommendedTypeChecked` or `tseslint.configs.strictTypeChecked`
|
||||
- Optionally add `...tseslint.configs.stylisticTypeChecked`
|
||||
- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and update the config:
|
||||
|
||||
```js
|
||||
// eslint.config.js
|
||||
import react from 'eslint-plugin-react'
|
||||
|
||||
export default tseslint.config({
|
||||
// Set the react version
|
||||
settings: { react: { version: '18.3' } },
|
||||
plugins: {
|
||||
// Add the react plugin
|
||||
react,
|
||||
},
|
||||
rules: {
|
||||
// other rules...
|
||||
// Enable its recommended rules
|
||||
...react.configs.recommended.rules,
|
||||
...react.configs['jsx-runtime'].rules,
|
||||
},
|
||||
})
|
||||
```
|
||||
28
react-vite-client/eslint.config.js
Normal file
28
react-vite-client/eslint.config.js
Normal file
@@ -0,0 +1,28 @@
|
||||
import js from '@eslint/js'
|
||||
import globals from 'globals'
|
||||
import reactHooks from 'eslint-plugin-react-hooks'
|
||||
import reactRefresh from 'eslint-plugin-react-refresh'
|
||||
import tseslint from 'typescript-eslint'
|
||||
|
||||
export default tseslint.config(
|
||||
{ ignores: ['dist'] },
|
||||
{
|
||||
extends: [js.configs.recommended, ...tseslint.configs.recommended],
|
||||
files: ['**/*.{ts,tsx}'],
|
||||
languageOptions: {
|
||||
ecmaVersion: 2020,
|
||||
globals: globals.browser,
|
||||
},
|
||||
plugins: {
|
||||
'react-hooks': reactHooks,
|
||||
'react-refresh': reactRefresh,
|
||||
},
|
||||
rules: {
|
||||
...reactHooks.configs.recommended.rules,
|
||||
'react-refresh/only-export-components': [
|
||||
'warn',
|
||||
{ allowConstantExport: true },
|
||||
],
|
||||
},
|
||||
},
|
||||
)
|
||||
14
react-vite-client/index.html
Normal file
14
react-vite-client/index.html
Normal file
@@ -0,0 +1,14 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link href="/src/styles.css" rel="stylesheet">
|
||||
<title>Vite + React + TS</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
73
react-vite-client/nswag.json
Normal file
73
react-vite-client/nswag.json
Normal file
@@ -0,0 +1,73 @@
|
||||
{
|
||||
"runtime": "Net90",
|
||||
"defaultVariables": null,
|
||||
"documentGenerator": {
|
||||
"fromDocument": {
|
||||
"url": "http://localhost:5175/swagger/v1/swagger.json",
|
||||
"output": null,
|
||||
"newLineBehavior": "Auto"
|
||||
}
|
||||
},
|
||||
"codeGenerators": {
|
||||
"openApiToTypeScriptClient": {
|
||||
"className": "{controller}Client",
|
||||
"moduleName": "",
|
||||
"namespace": "",
|
||||
"typeScriptVersion": 4.0,
|
||||
"template": "Fetch",
|
||||
"promiseType": "Promise",
|
||||
"httpClass": "HttpClient",
|
||||
"withCredentials": false,
|
||||
"useSingletonProvider": false,
|
||||
"injectionTokenType": "OpaqueToken",
|
||||
"rxJsVersion": 6.0,
|
||||
"dateTimeType": "Date",
|
||||
"nullValue": "Undefined",
|
||||
"generateClientClasses": true,
|
||||
"generateClientInterfaces": false,
|
||||
"generateOptionalParameters": true,
|
||||
"exportTypes": true,
|
||||
"wrapDtoExceptions": false,
|
||||
"exceptionClass": "SwaggerException",
|
||||
"clientBaseClass": null,
|
||||
"wrapResponses": false,
|
||||
"wrapResponseMethods": [],
|
||||
"generateResponseClasses": true,
|
||||
"responseClass": "SwaggerResponse",
|
||||
"protectedMethods": [],
|
||||
"configurationClass": null,
|
||||
"useTransformOptionsMethod": false,
|
||||
"useTransformResultMethod": false,
|
||||
"generateDtoTypes": true,
|
||||
"operationGenerationMode": "MultipleClientsFromOperationId",
|
||||
"markOptionalProperties": true,
|
||||
"generateCloneMethod": false,
|
||||
"typeStyle": "Class",
|
||||
"enumStyle": "Enum",
|
||||
"useLeafType": false,
|
||||
"classTypes": [],
|
||||
"extendedClasses": [],
|
||||
"extensionCode": null,
|
||||
"generateDefaultValues": true,
|
||||
"excludedTypeNames": [],
|
||||
"excludedParameterNames": [],
|
||||
"handleReferences": false,
|
||||
"generateTypeCheckFunctions": false,
|
||||
"generateConstructorInterface": true,
|
||||
"convertConstructorInterfaceData": false,
|
||||
"importRequiredTypes": true,
|
||||
"useGetBaseUrlMethod": false,
|
||||
"baseUrlTokenName": "API_BASE_URL",
|
||||
"queryNullValue": "",
|
||||
"useAbortSignal": false,
|
||||
"inlineNamedDictionaries": false,
|
||||
"inlineNamedAny": false,
|
||||
"includeHttpContext": false,
|
||||
"templateDirectory": null,
|
||||
"serviceHost": null,
|
||||
"serviceSchemes": null,
|
||||
"output": "src/ApiCient.ts",
|
||||
"newLineBehavior": "Auto"
|
||||
}
|
||||
}
|
||||
}
|
||||
4541
react-vite-client/package-lock.json
generated
Normal file
4541
react-vite-client/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
38
react-vite-client/package.json
Normal file
38
react-vite-client/package.json
Normal file
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"name": "react-vite-client",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "tsc -b && vite build",
|
||||
"lint": "eslint .",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-svg-core": "^6.7.2",
|
||||
"@fortawesome/free-solid-svg-icons": "^6.7.2",
|
||||
"@fortawesome/react-fontawesome": "^0.2.2",
|
||||
"@syncfusion/ej2-react-grids": "^28.2.4",
|
||||
"@tailwindcss/vite": "^4.0.4",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0",
|
||||
"react-router-dom": "^7.1.5",
|
||||
"tailwindcss": "^4.0.4",
|
||||
"use-local-storage-state": "^19.5.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.19.0",
|
||||
"@types/react": "^19.0.8",
|
||||
"@types/react-dom": "^19.0.3",
|
||||
"@vitejs/plugin-react": "^4.3.4",
|
||||
"daisyui": "^5.0.0-beta.7",
|
||||
"eslint": "^9.19.0",
|
||||
"eslint-plugin-react-hooks": "^5.0.0",
|
||||
"eslint-plugin-react-refresh": "^0.4.18",
|
||||
"globals": "^15.14.0",
|
||||
"typescript": "~5.7.2",
|
||||
"typescript-eslint": "^8.22.0",
|
||||
"vite": "^6.1.0"
|
||||
}
|
||||
}
|
||||
1
react-vite-client/public/vite.svg
Normal file
1
react-vite-client/public/vite.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
146
react-vite-client/src/ApiCient.ts
Normal file
146
react-vite-client/src/ApiCient.ts
Normal file
@@ -0,0 +1,146 @@
|
||||
//----------------------
|
||||
// <auto-generated>
|
||||
// Generated using the NSwag toolchain v14.2.0.0 (NJsonSchema v11.1.0.0 (Newtonsoft.Json v13.0.0.0)) (http://NSwag.org)
|
||||
// </auto-generated>
|
||||
//----------------------
|
||||
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// ReSharper disable InconsistentNaming
|
||||
|
||||
export class Client {
|
||||
private http: { fetch(url: RequestInfo, init?: RequestInit): Promise<Response> };
|
||||
private baseUrl: string;
|
||||
protected jsonParseReviver: ((key: string, value: any) => any) | undefined = undefined;
|
||||
|
||||
constructor(baseUrl?: string, http?: { fetch(url: RequestInfo, init?: RequestInit): Promise<Response> }) {
|
||||
this.http = http ? http : window as any;
|
||||
this.baseUrl = baseUrl ?? "http://localhost:5175";
|
||||
}
|
||||
|
||||
getWeatherForecast(): Promise<WeatherForecast[]> {
|
||||
let url_ = this.baseUrl + "/weatherforecast";
|
||||
url_ = url_.replace(/[?&]$/, "");
|
||||
|
||||
let options_: RequestInit = {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Accept": "application/json"
|
||||
}
|
||||
};
|
||||
|
||||
return this.http.fetch(url_, options_).then((_response: Response) => {
|
||||
return this.processGetWeatherForecast(_response);
|
||||
});
|
||||
}
|
||||
|
||||
protected processGetWeatherForecast(response: Response): Promise<WeatherForecast[]> {
|
||||
const status = response.status;
|
||||
let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
|
||||
if (status === 200) {
|
||||
return response.text().then((_responseText) => {
|
||||
let result200: any = null;
|
||||
let resultData200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver);
|
||||
if (Array.isArray(resultData200)) {
|
||||
result200 = [] as any;
|
||||
for (let item of resultData200)
|
||||
result200!.push(WeatherForecast.fromJS(item));
|
||||
}
|
||||
else {
|
||||
result200 = <any>null;
|
||||
}
|
||||
return result200;
|
||||
});
|
||||
} else if (status !== 200 && status !== 204) {
|
||||
return response.text().then((_responseText) => {
|
||||
return throwException("An unexpected server error occurred.", status, _responseText, _headers);
|
||||
});
|
||||
}
|
||||
return Promise.resolve<WeatherForecast[]>(null as any);
|
||||
}
|
||||
}
|
||||
|
||||
export class WeatherForecast implements IWeatherForecast {
|
||||
date?: Date;
|
||||
temperatureC?: number;
|
||||
summary?: string | undefined;
|
||||
temperatureF?: number;
|
||||
|
||||
constructor(data?: IWeatherForecast) {
|
||||
if (data) {
|
||||
for (var property in data) {
|
||||
if (data.hasOwnProperty(property))
|
||||
(<any>this)[property] = (<any>data)[property];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init(_data?: any) {
|
||||
if (_data) {
|
||||
this.date = _data["date"] ? new Date(_data["date"].toString()) : <any>undefined;
|
||||
this.temperatureC = _data["temperatureC"];
|
||||
this.summary = _data["summary"];
|
||||
this.temperatureF = _data["temperatureF"];
|
||||
}
|
||||
}
|
||||
|
||||
static fromJS(data: any): WeatherForecast {
|
||||
data = typeof data === 'object' ? data : {};
|
||||
let result = new WeatherForecast();
|
||||
result.init(data);
|
||||
return result;
|
||||
}
|
||||
|
||||
toJSON(data?: any) {
|
||||
data = typeof data === 'object' ? data : {};
|
||||
data["date"] = this.date ? formatDate(this.date) : <any>undefined;
|
||||
data["temperatureC"] = this.temperatureC;
|
||||
data["summary"] = this.summary;
|
||||
data["temperatureF"] = this.temperatureF;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
export interface IWeatherForecast {
|
||||
date?: Date;
|
||||
temperatureC?: number;
|
||||
summary?: string | undefined;
|
||||
temperatureF?: number;
|
||||
}
|
||||
|
||||
function formatDate(d: Date) {
|
||||
return d.getFullYear() + '-' +
|
||||
(d.getMonth() < 9 ? ('0' + (d.getMonth()+1)) : (d.getMonth()+1)) + '-' +
|
||||
(d.getDate() < 10 ? ('0' + d.getDate()) : d.getDate());
|
||||
}
|
||||
|
||||
export class SwaggerException extends Error {
|
||||
message: string;
|
||||
status: number;
|
||||
response: string;
|
||||
headers: { [key: string]: any; };
|
||||
result: any;
|
||||
|
||||
constructor(message: string, status: number, response: string, headers: { [key: string]: any; }, result: any) {
|
||||
super();
|
||||
|
||||
this.message = message;
|
||||
this.status = status;
|
||||
this.response = response;
|
||||
this.headers = headers;
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
protected isSwaggerException = true;
|
||||
|
||||
static isSwaggerException(obj: any): obj is SwaggerException {
|
||||
return obj.isSwaggerException === true;
|
||||
}
|
||||
}
|
||||
|
||||
function throwException(message: string, status: number, response: string, headers: { [key: string]: any; }, result?: any): any {
|
||||
if (result !== null && result !== undefined)
|
||||
throw result;
|
||||
else
|
||||
throw new SwaggerException(message, status, response, headers, null);
|
||||
}
|
||||
0
react-vite-client/src/App.css
Normal file
0
react-vite-client/src/App.css
Normal file
22
react-vite-client/src/App.tsx
Normal file
22
react-vite-client/src/App.tsx
Normal file
@@ -0,0 +1,22 @@
|
||||
// App.tsx
|
||||
import React from 'react';
|
||||
import {BrowserRouter as Router, Route, Routes} from 'react-router-dom';
|
||||
import HomePage from './pages/HomePage';
|
||||
import AboutPage from './pages/AboutPage';
|
||||
import WeatherPage from "./pages/WeatherPage/WeatherPage.tsx";
|
||||
|
||||
|
||||
const App: React.FC = () => {
|
||||
return (
|
||||
<Router>
|
||||
<Routes>
|
||||
<Route path="/" element={<HomePage/>}/>
|
||||
<Route path="/about" element={<AboutPage/>}/>
|
||||
<Route path="/weather" element={<WeatherPage/>}/>
|
||||
{/* Other routes */}
|
||||
</Routes>
|
||||
</Router>
|
||||
);
|
||||
};
|
||||
|
||||
export default App;
|
||||
19
react-vite-client/src/index.css
Normal file
19
react-vite-client/src/index.css
Normal file
@@ -0,0 +1,19 @@
|
||||
@import "tailwindcss";
|
||||
|
||||
@import "../node_modules/@syncfusion/ej2-base/styles/tailwind3.css";
|
||||
@import "../node_modules/@syncfusion/ej2-buttons/styles/tailwind3.css";
|
||||
@import "../node_modules/@syncfusion/ej2-calendars/styles/tailwind3.css";
|
||||
@import "../node_modules/@syncfusion/ej2-dropdowns/styles/tailwind3.css";
|
||||
@import "../node_modules/@syncfusion/ej2-inputs/styles/tailwind3.css";
|
||||
@import "../node_modules/@syncfusion/ej2-navigations/styles/tailwind3.css";
|
||||
@import "../node_modules/@syncfusion/ej2-popups/styles/tailwind3.css";
|
||||
@import "../node_modules/@syncfusion/ej2-splitbuttons/styles/tailwind3.css";
|
||||
@import "../node_modules/@syncfusion/ej2-react-grids/styles/tailwind3.css";
|
||||
|
||||
|
||||
|
||||
@plugin "daisyui" {
|
||||
themes: light --default, dark --prefersdark;
|
||||
}
|
||||
|
||||
|
||||
110
react-vite-client/src/layouts/MainLayout.tsx
Normal file
110
react-vite-client/src/layouts/MainLayout.tsx
Normal file
@@ -0,0 +1,110 @@
|
||||
// Layout.tsx
|
||||
import React, {useEffect, useState} from 'react';
|
||||
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
|
||||
import {faBars} from '@fortawesome/free-solid-svg-icons';
|
||||
import {handleDarkMode} from "../theme_helpers/ThemeHelpers";
|
||||
import NavMenu from "./NavMenue.tsx";
|
||||
|
||||
interface LayoutProps {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
const Layout: React.FC<LayoutProps> = ({children}) => {
|
||||
|
||||
// Determine if we are on mobile (below 768px)
|
||||
const [isMobile, setIsMobile] = useState<boolean>(() => {
|
||||
if (typeof window !== 'undefined') {
|
||||
return window.innerWidth < 768;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
// Sidebar open state: default open on desktop, closed on mobile.
|
||||
const [isSidebarOpen, setSidebarOpen] = useState<boolean>(() => {
|
||||
if (typeof window !== 'undefined') {
|
||||
return window.innerWidth >= 768;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
|
||||
// Setup dark mode theme.
|
||||
useEffect(() => {
|
||||
return handleDarkMode();
|
||||
}, []);
|
||||
|
||||
|
||||
// Update mobile flag and sidebar state on resize.
|
||||
useEffect(() => {
|
||||
const handleResize = () => {
|
||||
const mobile = window.innerWidth < 768;
|
||||
setIsMobile(mobile);
|
||||
// Set sidebar open to false on mobile, true on desktop.
|
||||
setSidebarOpen(!mobile);
|
||||
};
|
||||
|
||||
window.addEventListener('resize', handleResize);
|
||||
// Initial check.
|
||||
handleResize();
|
||||
|
||||
return () => window.removeEventListener('resize', handleResize);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col min-h-screen relative">
|
||||
{/* Header */}
|
||||
<header className="bg-primary text-primary-content p-4 flex">
|
||||
<div className="container flex">
|
||||
<button
|
||||
className="btn btn-square btn-ghost mr-4"
|
||||
onClick={() => setSidebarOpen(!isSidebarOpen)}
|
||||
>
|
||||
<FontAwesomeIcon icon={faBars} className="fa-2xl"/>
|
||||
</button>
|
||||
<h1 className="text-3xl font-bold">My React Site</h1>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{/* For mobile, add an overlay backdrop when the sidebar is open.
|
||||
The overlay now starts below the header (top-16) */}
|
||||
{isMobile && isSidebarOpen && (
|
||||
<div
|
||||
className="fixed top-16 left-0 right-0 bottom-0 bg-black opacity-50 z-40"
|
||||
onClick={() => setSidebarOpen(false)}
|
||||
></div>
|
||||
)}
|
||||
|
||||
{/* Content Area */}
|
||||
<div className="flex flex-1">
|
||||
{/* Sidebar Navigation */}
|
||||
<aside className={`bg-base-200 transition-transform duration-300
|
||||
${isMobile
|
||||
? "fixed top-16 left-0 bottom-0 w-44 max-w-xs p-3 z-50"
|
||||
: "min-w-36 max-w-52 w-1/5"
|
||||
}
|
||||
${isMobile
|
||||
? (isSidebarOpen ? "translate-x-0" : "-translate-x-full")
|
||||
: (isSidebarOpen ? "block" : "hidden")
|
||||
}
|
||||
`}
|
||||
>
|
||||
<NavMenu/>
|
||||
</aside>
|
||||
|
||||
{/* Main Content */}
|
||||
<main className="flex-grow container mx-auto p-4">
|
||||
{children}
|
||||
</main>
|
||||
</div>
|
||||
|
||||
{/* Footer */}
|
||||
<footer className="bg-neutral text-neutral-content p-4">
|
||||
<div className="container mx-auto text-center">
|
||||
<p>© 2025 My React Site. All rights reserved.</p>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Layout;
|
||||
43
react-vite-client/src/layouts/NavMenue.tsx
Normal file
43
react-vite-client/src/layouts/NavMenue.tsx
Normal file
@@ -0,0 +1,43 @@
|
||||
import React from 'react';
|
||||
import {Link} from 'react-router-dom';
|
||||
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
|
||||
import {faCloudSun, faConciergeBell, faEnvelope, faHouse, faInfoCircle} from '@fortawesome/free-solid-svg-icons';
|
||||
|
||||
const NavMenu: React.FC = () => {
|
||||
return (
|
||||
<ul className="menu gap-y-1">
|
||||
<li className="flex">
|
||||
<Link to="/">
|
||||
<FontAwesomeIcon icon={faHouse}/>
|
||||
Home
|
||||
</Link>
|
||||
</li>
|
||||
<li className="flex">
|
||||
<Link to="/weather">
|
||||
<FontAwesomeIcon icon={faCloudSun}/>
|
||||
Weather
|
||||
</Link>
|
||||
</li>
|
||||
<li className="flex">
|
||||
<Link to="/about">
|
||||
<FontAwesomeIcon icon={faInfoCircle}/>
|
||||
About
|
||||
</Link>
|
||||
</li>
|
||||
<li className="flex">
|
||||
<Link to="/services">
|
||||
<FontAwesomeIcon icon={faConciergeBell}/>
|
||||
Services
|
||||
</Link>
|
||||
</li>
|
||||
<li className="flex">
|
||||
<Link to="/contact">
|
||||
<FontAwesomeIcon icon={faEnvelope}/>
|
||||
Contact
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
);
|
||||
};
|
||||
|
||||
export default NavMenu;
|
||||
15
react-vite-client/src/layouts/with_main_layout.ts.tsx
Normal file
15
react-vite-client/src/layouts/with_main_layout.ts.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
// withLayout.tsx
|
||||
import React from 'react';
|
||||
import Layout from './MainLayout';
|
||||
|
||||
const withLayout = <P extends object>(
|
||||
WrappedComponent: React.ComponentType<P>
|
||||
): React.FC<P> => {
|
||||
return (props: P) => (
|
||||
<Layout>
|
||||
<WrappedComponent {...props} />
|
||||
</Layout>
|
||||
);
|
||||
};
|
||||
|
||||
export default withLayout;
|
||||
15
react-vite-client/src/main.tsx
Normal file
15
react-vite-client/src/main.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
import { StrictMode } from 'react'
|
||||
import { createRoot } from 'react-dom/client'
|
||||
import './index.css'
|
||||
import App from './App.tsx'
|
||||
import { registerLicense } from '@syncfusion/ej2-base';
|
||||
|
||||
|
||||
// Registering Syncfusion license key
|
||||
registerLicense('Ngo9BigBOggjHTQxAR8/V1NMaF5cXmBCf1FpRmJGdld5fUVHYVZUTXxaS00DNHVRdkdmWX1ed3VTR2NYU010XkI=');
|
||||
|
||||
createRoot(document.getElementById('root')!).render(
|
||||
<StrictMode>
|
||||
<App />
|
||||
</StrictMode>,
|
||||
)
|
||||
23
react-vite-client/src/pages/AboutPage.tsx
Normal file
23
react-vite-client/src/pages/AboutPage.tsx
Normal file
@@ -0,0 +1,23 @@
|
||||
// AboutPage.tsx
|
||||
import React from 'react';
|
||||
import withLayout from '../layouts/with_main_layout.ts.tsx'; // Adjust the path based on your project structure
|
||||
|
||||
const AboutPage: React.FC = () => {
|
||||
return (
|
||||
<div className="p-4">
|
||||
<h2 className="text-2xl font-bold mb-4">About Us</h2>
|
||||
<p className="mb-2">
|
||||
Welcome to our website! We are committed to providing you with the best experience possible.
|
||||
</p>
|
||||
<p className="mb-2">
|
||||
Our mission is to build high-quality applications that are fast, responsive, and user-friendly.
|
||||
</p>
|
||||
<p>
|
||||
Whether you're here to learn more about our team or explore our services, we hope you enjoy your visit.
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const NamedAboutPage = withLayout(AboutPage);
|
||||
export default NamedAboutPage;
|
||||
73
react-vite-client/src/pages/HomePage.tsx
Normal file
73
react-vite-client/src/pages/HomePage.tsx
Normal file
@@ -0,0 +1,73 @@
|
||||
import {useState} from 'react'
|
||||
import '../App.css' // Ensure a declaration for CSS is added in a .d.ts file
|
||||
import {ColumnDirective, ColumnsDirective, GridComponent} from '@syncfusion/ej2-react-grids';
|
||||
import with_main_layout from '../layouts/with_main_layout.ts.tsx'; // adjust the path as necessary
|
||||
|
||||
|
||||
function HomePage() {
|
||||
const [count, setCount] = useState(0)
|
||||
|
||||
|
||||
const data = [
|
||||
{
|
||||
OrderID: 10248, CustomerID: 'VINET', EmployeeID: 5, ShipCountry: 'France', Freight: 32.38
|
||||
},
|
||||
{
|
||||
OrderID: 10249, CustomerID: 'TOMSP', EmployeeID: 6, ShipCountry: 'Germany', Freight: 11.61
|
||||
},
|
||||
{
|
||||
OrderID: 10250, CustomerID: 'HANAR', EmployeeID: 4, ShipCountry: 'Brazil', Freight: 65.83
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
<h1>Vite, React, Syncfusion, Daisyui Demo</h1>
|
||||
<div className="card shadow-md w-2/3 mt-5 bg-auto-50">
|
||||
<div className="card-body">
|
||||
<button className="btn w-full m-2" onClick={() => setCount((count) => count + 1)}>
|
||||
count is {count}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="badge badge-secondary m-4">primary</div>
|
||||
|
||||
<div role="alert" className="alert alert-error">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className="h-6 w-6 shrink-0 stroke-current"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24">
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="2"
|
||||
d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
||||
</svg>
|
||||
<span>Error! Task failed successfully.</span>
|
||||
</div>
|
||||
<p>
|
||||
Edit <code>src/App.tsx</code> and save to test HMR
|
||||
</p>
|
||||
<p className="font-bold underline">
|
||||
Click on the Vite and React logos to learn more
|
||||
</p>
|
||||
|
||||
|
||||
<GridComponent dataSource={data}>
|
||||
<ColumnsDirective>
|
||||
<ColumnDirective field='OrderID' width='100' textAlign="Right"/>
|
||||
<ColumnDirective field='CustomerID' width='100'/>
|
||||
<ColumnDirective field='EmployeeID' width='100' textAlign="Right"/>
|
||||
<ColumnDirective field='Freight' width='100' format="C2" textAlign="Right"/>
|
||||
<ColumnDirective field='ShipCountry' width='100'/>
|
||||
</ColumnsDirective>
|
||||
</GridComponent>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export const PageWithLayout = with_main_layout(HomePage)
|
||||
export default PageWithLayout
|
||||
33
react-vite-client/src/pages/WeatherPage/WeatherGrid.tsx
Normal file
33
react-vite-client/src/pages/WeatherPage/WeatherGrid.tsx
Normal file
@@ -0,0 +1,33 @@
|
||||
import React from "react";
|
||||
import {ColumnDirective, ColumnsDirective, Filter, GridComponent, Inject, Page, Reorder, Resize, Sort} from "@syncfusion/ej2-react-grids";
|
||||
import {WeatherForecast} from "../../ApiCient.ts";
|
||||
|
||||
interface WeatherGridProps {
|
||||
|
||||
/**
|
||||
* Represents an optional array of weather forecast data.
|
||||
* Each element in the array provides detailed weather forecast information.
|
||||
*/
|
||||
data?: WeatherForecast[];
|
||||
|
||||
testing?: boolean;
|
||||
}
|
||||
|
||||
const NavMenu: React.FC<WeatherGridProps> = ({data: weatherData}) => {
|
||||
return (
|
||||
<div>
|
||||
<GridComponent dataSource={weatherData || []} allowPaging={true} pageSettings={{pageCount: 5}} allowReordering={true} allowSorting={true}
|
||||
allowFiltering={true} allowResizing={true}>
|
||||
<ColumnsDirective>
|
||||
<ColumnDirective field="date" format="dd.MM.yy" headerText="Date" width="150"/>
|
||||
<ColumnDirective field="temperatureC" headerText="Temp. (C)" width="60"/>
|
||||
<ColumnDirective field="temperatureF" headerText="Temp. (F)" width="60"/>
|
||||
<ColumnDirective field="summary" headerText="Summary"/>
|
||||
</ColumnsDirective>
|
||||
<Inject services={[Page, Sort, Filter, Reorder, Resize]}/>
|
||||
</GridComponent>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default NavMenu;
|
||||
75
react-vite-client/src/pages/WeatherPage/WeatherPage.tsx
Normal file
75
react-vite-client/src/pages/WeatherPage/WeatherPage.tsx
Normal file
@@ -0,0 +1,75 @@
|
||||
import with_main_layout from "../../layouts/with_main_layout.ts.tsx";
|
||||
import useLocalStorageState from 'use-local-storage-state'; // install via npm
|
||||
import {Client, WeatherForecast} from "../../ApiCient.ts";
|
||||
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
|
||||
import {faDownload, faTrash} from "@fortawesome/free-solid-svg-icons";
|
||||
import {useState} from "react";
|
||||
import WeatherGrid from "./WeatherGrid.tsx";
|
||||
import {useSignalR} from "../../signalr/SignalRHelper.ts";
|
||||
|
||||
function WeatherPage() {
|
||||
// Use the hook to persist your weather data. It will default to null.
|
||||
const [weatherData, setWeatherData] = useLocalStorageState<WeatherForecast[] | null>('WeatherPage-Forecast', {defaultValue: [],});
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [currentState, setCurrentState] = useState<string | null>(null);
|
||||
const {subscribe} = useSignalR('http://localhost:5175', 'WeatherUpdateHub');
|
||||
|
||||
|
||||
const fetchWeatherData = async () => {
|
||||
const apiClient = new Client();
|
||||
try {
|
||||
const progressCleanup = subscribe<string>('ProgressUpdate', (_, message) => {
|
||||
setCurrentState(message);
|
||||
});
|
||||
setLoading(true);
|
||||
const data = await apiClient.getWeatherForecast();
|
||||
setWeatherData(data);
|
||||
setLoading(false);
|
||||
progressCleanup();
|
||||
setCurrentState(null);
|
||||
} catch (error) {
|
||||
console.error("Error fetching weather data:", error);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="card shadow-md card-sm w-full bg-auto-50">
|
||||
<div className="card-header text-2xl ml-5 mt-1">Weather Data</div>
|
||||
<div className="card-body">
|
||||
<div className="row-auto flex gap-x-2 ">
|
||||
<button className="btn w-48" onClick={fetchWeatherData}>
|
||||
<FontAwesomeIcon className="mr-1" icon={faDownload}/>
|
||||
Fetch Weather Data
|
||||
</button>
|
||||
<button className="btn w-48" onClick={() => setWeatherData(null)}>
|
||||
<FontAwesomeIcon className="mr-1" icon={faTrash}/>
|
||||
Clear Weather
|
||||
</button>
|
||||
</div>
|
||||
<div className="mt-5">
|
||||
{loading ?
|
||||
<div>
|
||||
{/*<p className="m-5">Progress: {currentState ? JSON.parse(currentState).percentage : 0}% - {currentState ? JSON.parse(currentState).message : ''}</p>*/}
|
||||
{(() => {
|
||||
const percentage = currentState ? JSON.parse(currentState).percentage : 0;
|
||||
return (
|
||||
<div className="radial-progress" style={{['--value' as unknown as string]: percentage}} aria-valuenow={percentage} role="progressbar">
|
||||
{percentage}%
|
||||
</div>
|
||||
);
|
||||
})()}
|
||||
</div>
|
||||
:
|
||||
!weatherData || weatherData.length === 0 ? (
|
||||
<p>No weather data, please request it!</p>
|
||||
) : (
|
||||
<WeatherGrid data={weatherData}/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export const PageWithLayout = with_main_layout(WeatherPage);
|
||||
export default PageWithLayout;
|
||||
63
react-vite-client/src/signalr/SignalRHelper.ts
Normal file
63
react-vite-client/src/signalr/SignalRHelper.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
import {useCallback, useEffect, useRef, useState} from 'react';
|
||||
import {HubConnection, HubConnectionBuilder, LogLevel} from '@microsoft/signalr';
|
||||
|
||||
export function useSignalR(baseUrl: string, hubName: string) {
|
||||
const [connection, setConnection] = useState<HubConnection | null>(null);
|
||||
const connectionRef = useRef<HubConnection | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const newConnection: HubConnection = new HubConnectionBuilder()
|
||||
.withUrl(`${baseUrl}/${hubName}`)
|
||||
.withAutomaticReconnect()
|
||||
.configureLogging(LogLevel.Information)
|
||||
.build();
|
||||
|
||||
connectionRef.current = newConnection;
|
||||
setConnection(newConnection);
|
||||
|
||||
newConnection
|
||||
.start()
|
||||
.then(() => console.log('SignalR connected.'))
|
||||
.catch(error => console.error('SignalR connection error:', error));
|
||||
|
||||
// Cleanup on unmount: stop the connection.
|
||||
return () => {
|
||||
newConnection.stop();
|
||||
};
|
||||
}, [baseUrl, hubName]);
|
||||
|
||||
/**
|
||||
* Subscribes to a SignalR event.
|
||||
* @param eventName The event name.
|
||||
* @param callback The callback invoked when the event is received.
|
||||
* @returns A function to unsubscribe the event.
|
||||
*/
|
||||
const subscribe = useCallback(
|
||||
<T>(eventName: string, callback: (...args: T[]) => void): (() => void) => {
|
||||
if (connectionRef.current) {
|
||||
connectionRef.current.on(eventName, callback);
|
||||
return () => connectionRef.current?.off(eventName, callback);
|
||||
}
|
||||
return () => {
|
||||
};
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
/**
|
||||
* Sends a message via the SignalR connection.
|
||||
* @param methodName The hub method name.
|
||||
* @param args Arguments for the hub method.
|
||||
*/
|
||||
const sendMessage = useCallback(async (methodName: string, ...args: any[]): Promise<void> => {
|
||||
if (connectionRef.current) {
|
||||
try {
|
||||
await connectionRef.current.send(methodName, ...args);
|
||||
} catch (error) {
|
||||
console.error('Error sending message:', error);
|
||||
}
|
||||
}
|
||||
}, []);
|
||||
|
||||
return {connection, subscribe, sendMessage};
|
||||
}
|
||||
25
react-vite-client/src/theme_helpers/ThemeHelpers.ts
Normal file
25
react-vite-client/src/theme_helpers/ThemeHelpers.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
export function handleDarkMode(): () => void {
|
||||
const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
|
||||
|
||||
// Function to update dark mode for Syncfusion controls
|
||||
const updateDarkMode = (e: MediaQueryList | MediaQueryListEvent) => {
|
||||
if (e.matches) {
|
||||
// If the system is in dark mode, add the class
|
||||
document.body.classList.add("e-dark-mode");
|
||||
} else {
|
||||
// Otherwise, remove it
|
||||
document.body.classList.remove("e-dark-mode");
|
||||
}
|
||||
};
|
||||
|
||||
// Set initial mode based on system preference:
|
||||
updateDarkMode(mediaQuery); // Pass the initial `mediaQuery` directly
|
||||
|
||||
// Listen for changes in system preference:
|
||||
mediaQuery.addEventListener("change", updateDarkMode);
|
||||
|
||||
// Cleanup function to remove the event listener
|
||||
return () => {
|
||||
mediaQuery.removeEventListener("change", updateDarkMode);
|
||||
};
|
||||
}
|
||||
1
react-vite-client/src/vite-env.d.ts
vendored
Normal file
1
react-vite-client/src/vite-env.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/// <reference types="vite/client" />
|
||||
26
react-vite-client/tsconfig.app.json
Normal file
26
react-vite-client/tsconfig.app.json
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
||||
"target": "ES2020",
|
||||
"useDefineForClassFields": true,
|
||||
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||
"module": "ESNext",
|
||||
"skipLibCheck": true,
|
||||
|
||||
/* Bundler mode */
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"isolatedModules": true,
|
||||
"moduleDetection": "force",
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx",
|
||||
|
||||
/* Linting */
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noUncheckedSideEffectImports": true
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|
||||
7
react-vite-client/tsconfig.json
Normal file
7
react-vite-client/tsconfig.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"files": [],
|
||||
"references": [
|
||||
{ "path": "./tsconfig.app.json" },
|
||||
{ "path": "./tsconfig.node.json" }
|
||||
]
|
||||
}
|
||||
24
react-vite-client/tsconfig.node.json
Normal file
24
react-vite-client/tsconfig.node.json
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
|
||||
"target": "ES2022",
|
||||
"lib": ["ES2023"],
|
||||
"module": "ESNext",
|
||||
"skipLibCheck": true,
|
||||
|
||||
/* Bundler mode */
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"isolatedModules": true,
|
||||
"moduleDetection": "force",
|
||||
"noEmit": true,
|
||||
|
||||
/* Linting */
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noUncheckedSideEffectImports": true
|
||||
},
|
||||
"include": ["vite.config.ts"]
|
||||
}
|
||||
1
react-vite-client/tsconfig.tsbuildinfo
Normal file
1
react-vite-client/tsconfig.tsbuildinfo
Normal file
@@ -0,0 +1 @@
|
||||
{"root":["./src/app.tsx","./src/main.tsx","./src/vite-env.d.ts","./src/layouts/mainlayout.tsx","./src/layouts/with_main_layout.ts.tsx","./src/theme_helpers/themehelpers.ts"],"errors":true,"version":"5.7.3"}
|
||||
11
react-vite-client/vite.config.ts
Normal file
11
react-vite-client/vite.config.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import {defineConfig} from 'vite'
|
||||
import react from '@vitejs/plugin-react'
|
||||
import tailwindcss from '@tailwindcss/vite'
|
||||
|
||||
// https://vite.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
react(),
|
||||
tailwindcss(),
|
||||
],
|
||||
})
|
||||
1742
react-vite-client/yarn.lock
Normal file
1742
react-vite-client/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user