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>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.0" />
|
<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" />
|
<PackageReference Include="NSwag.AspNetCore" Version="14.2.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|||||||
@@ -1,20 +1,24 @@
|
|||||||
|
using Api.SignalR;
|
||||||
|
using Microsoft.AspNetCore.SignalR;
|
||||||
|
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
// Add services to the container.
|
// Add services to the container.
|
||||||
// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi
|
// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi
|
||||||
builder.Services.AddEndpointsApiExplorer();
|
builder.Services.AddEndpointsApiExplorer();
|
||||||
builder.Services.AddOpenApi();
|
builder.Services.AddOpenApi();
|
||||||
builder.Services.AddOpenApiDocument(config =>
|
builder.Services.AddSignalR();
|
||||||
{
|
builder.Services.AddHostedService<SignalRSendService>();
|
||||||
config.Title = "NSwag Demo API";
|
builder.Services.AddSingleton<IUserIdProvider, UserProvider>();
|
||||||
});
|
builder.Services.AddOpenApiDocument(config => { config.Title = "NSwag Demo API"; });
|
||||||
builder.Services.AddCors(options =>
|
builder.Services.AddCors(options =>
|
||||||
{
|
{
|
||||||
options.AddDefaultPolicy(policy =>
|
options.AddDefaultPolicy(policy =>
|
||||||
{
|
{
|
||||||
policy.AllowAnyOrigin()
|
policy.WithOrigins("http://localhost:5173")
|
||||||
.AllowAnyHeader()
|
.AllowAnyHeader()
|
||||||
.AllowAnyMethod();
|
.AllowAnyMethod()
|
||||||
|
.AllowCredentials();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -24,8 +28,8 @@ var app = builder.Build();
|
|||||||
if (app.Environment.IsDevelopment())
|
if (app.Environment.IsDevelopment())
|
||||||
{
|
{
|
||||||
app.MapOpenApi();
|
app.MapOpenApi();
|
||||||
app.UseOpenApi();
|
app.UseOpenApi();
|
||||||
app.UseSwaggerUi();
|
app.UseSwaggerUi();
|
||||||
}
|
}
|
||||||
|
|
||||||
app.UseHttpsRedirection();
|
app.UseHttpsRedirection();
|
||||||
@@ -36,23 +40,36 @@ var summaries = new[]
|
|||||||
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
|
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
|
||||||
};
|
};
|
||||||
|
|
||||||
app.MapGet("/weatherforecast", () =>
|
app.MapGet("/weatherforecast", async () =>
|
||||||
{
|
{
|
||||||
var forecast = Enumerable.Range(1, 5).Select(index =>
|
var hub = app.Services.GetRequiredService<IHubContext<WeatherUpdateHub>>();
|
||||||
new WeatherForecast
|
for (var i = 0; i < 100; i++)
|
||||||
(
|
{
|
||||||
DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
|
await hub.Clients.All.SendAsync("ProgressUpdate", "None", $$"""{"percentage": "{{i}}", "message": "{{DateTime.Now.Millisecond}}"}""");
|
||||||
Random.Shared.Next(-20, 55),
|
await Task.Delay(100);
|
||||||
summaries[Random.Shared.Next(summaries.Length)]
|
}
|
||||||
))
|
|
||||||
.ToArray();
|
|
||||||
return forecast;
|
var forecast = Enumerable.Range(1, 2500).Select(index =>
|
||||||
})
|
new WeatherForecast
|
||||||
.WithName("GetWeatherForecast");
|
(
|
||||||
|
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();
|
app.Run();
|
||||||
|
|
||||||
record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary)
|
record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary)
|
||||||
{
|
{
|
||||||
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
|
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:
|
- [Installation React with Vite and TypeScript](#installation-react-with-vite-and-typescript)
|
||||||
-ASP Net Core
|
- [Routing](#routing)
|
||||||
-Nswag
|
- [Install react-router-dom](#install-react-router-dom)
|
||||||
-SignalR
|
- [Update App.tsx](#update-apptsx)
|
||||||
-React
|
- [Update configuration files](#update-configuration-files)
|
||||||
-Syncfusion
|
- [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
|
```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
|
```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
|
```css
|
||||||
@tailwind base;
|
/* eg. src/index.css */
|
||||||
@tailwind components;
|
|
||||||
@tailwind utilities;
|
@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",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@microsoft/signalr": "^8.0.7",
|
||||||
"@testing-library/jest-dom": "^5.14.1",
|
"@testing-library/jest-dom": "^5.14.1",
|
||||||
"@testing-library/react": "^13.0.0",
|
"@testing-library/react": "^13.0.0",
|
||||||
"@testing-library/user-event": "^13.2.1",
|
"@testing-library/user-event": "^13.2.1",
|
||||||
|
|||||||
@@ -2,32 +2,61 @@ import React, { useEffect, useState } from 'react';
|
|||||||
import logo from './logo.svg';
|
import logo from './logo.svg';
|
||||||
import './App.css';
|
import './App.css';
|
||||||
import { Client, WeatherForecast } from './ApiCient';
|
import { Client, WeatherForecast } from './ApiCient';
|
||||||
|
import { HubConnection, HubConnectionBuilder } from '@microsoft/signalr';
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
|
|
||||||
const [forecast, setForecast] = useState<WeatherForecast[] | undefined>([]);
|
const [forecast, setForecast] = useState<WeatherForecast[] | undefined>([]);
|
||||||
|
const [connection, setConnection] = useState<HubConnection | null>(null);
|
||||||
|
const [message, setMessage] = useState<string>('');
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const client = new Client();
|
const newConnection = new HubConnectionBuilder()
|
||||||
|
.withUrl('http://localhost:5175/WeatherUpdateHub?userId=user1234')
|
||||||
|
.withAutomaticReconnect()
|
||||||
|
.build();
|
||||||
|
|
||||||
const fetchForecast = async () => {
|
setConnection(newConnection);
|
||||||
const forecast = await client.getWeatherForecast();
|
|
||||||
setForecast(forecast);
|
|
||||||
};
|
|
||||||
|
|
||||||
fetchForecast();
|
|
||||||
const intervalId = setInterval(fetchForecast, 1000);
|
|
||||||
|
|
||||||
return () => clearInterval(intervalId);
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
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 (
|
return (
|
||||||
<div className="App">
|
<div className="App">
|
||||||
{/* <header className="App-header">
|
{/* <header className="App-header">
|
||||||
<img src={logo} className="App-logo" alt="logo" />
|
<img src={logo} className="App-logo" alt="logo" />
|
||||||
<p>
|
</div> <p>
|
||||||
Edit <code>src/App.tsx</code> and save to reload.
|
Edit <code>src/App.tsx</code> and save to reload.
|
||||||
</p>
|
</p>
|
||||||
<a
|
<a
|
||||||
@@ -49,6 +78,10 @@ function App() {
|
|||||||
setForecast(forecast);
|
setForecast(forecast);
|
||||||
}} > Call API</button>
|
}} > Call API</button>
|
||||||
|
|
||||||
|
<div className="text-red-50">
|
||||||
|
{message}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
{forecast?.map((item: WeatherForecast) => {
|
{forecast?.map((item: WeatherForecast) => {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1557,6 +1557,17 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz#4fc56c15c580b9adb7dc3c333a134e540b44bfb1"
|
resolved "https://registry.yarnpkg.com/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz#4fc56c15c580b9adb7dc3c333a134e540b44bfb1"
|
||||||
integrity sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==
|
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":
|
"@nicolo-ribaudo/eslint-scope-5-internals@5.1.1-v1":
|
||||||
version "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"
|
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"
|
resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.6.tgz#41b80f2c871d19686216b82309231cfd3cb3d291"
|
||||||
integrity sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==
|
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:
|
accepts@~1.3.4, accepts@~1.3.8:
|
||||||
version "1.3.8"
|
version "1.3.8"
|
||||||
resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e"
|
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"
|
resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
|
||||||
integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==
|
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:
|
eventemitter3@^4.0.0:
|
||||||
version "4.0.7"
|
version "4.0.7"
|
||||||
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f"
|
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"
|
resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400"
|
||||||
integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==
|
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:
|
execa@^5.0.0:
|
||||||
version "5.1.1"
|
version "5.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd"
|
resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd"
|
||||||
@@ -4852,6 +4880,14 @@ fb-watchman@^2.0.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
bser "2.1.1"
|
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:
|
file-entry-cache@^6.0.1:
|
||||||
version "6.0.1"
|
version "6.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027"
|
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"
|
resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-7.1.1.tgz#1aba6693b0f255258a049d621329329322aad558"
|
||||||
integrity sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==
|
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:
|
node-forge@^1:
|
||||||
version "1.3.1"
|
version "1.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3"
|
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"
|
parseurl "~1.3.3"
|
||||||
send "0.19.0"
|
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:
|
set-function-length@^1.2.2:
|
||||||
version "1.2.2"
|
version "1.2.2"
|
||||||
resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449"
|
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:
|
dependencies:
|
||||||
punycode "^2.1.1"
|
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:
|
tryer@^1.0.1:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/tryer/-/tryer-1.0.1.tgz#f2c85406800b9b0f74c9f7465b81eaad241252f8"
|
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"
|
resolved "https://registry.yarnpkg.com/web-vitals/-/web-vitals-2.1.4.tgz#76563175a475a5e835264d373704f9dde718290c"
|
||||||
integrity sha512-sVWcwhU5mX6crfI5Vd2dC4qchyTqxV8URinzt25XqVh+bHEPGH4C3NPrNionCP7Obx59wrYEbNlw4Z8sjALzZg==
|
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:
|
webidl-conversions@^4.0.2:
|
||||||
version "4.0.2"
|
version "4.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad"
|
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"
|
resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf"
|
||||||
integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==
|
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:
|
whatwg-url@^7.0.0:
|
||||||
version "7.1.0"
|
version "7.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-7.1.0.tgz#c2c492f1eca612988efd3d2266be1b9fc6170d06"
|
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"
|
signal-exit "^3.0.2"
|
||||||
typedarray-to-buffer "^3.1.5"
|
typedarray-to-buffer "^3.1.5"
|
||||||
|
|
||||||
ws@^7.4.6:
|
ws@^7.4.5, ws@^7.4.6:
|
||||||
version "7.5.10"
|
version "7.5.10"
|
||||||
resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.10.tgz#58b5c20dc281633f6c19113f39b349bd8bd558d9"
|
resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.10.tgz#58b5c20dc281633f6c19113f39b349bd8bd558d9"
|
||||||
integrity sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==
|
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