first commit
This commit is contained in:
commit
2934405992
25 changed files with 4804 additions and 0 deletions
BIN
.DS_Store
vendored
Normal file
BIN
.DS_Store
vendored
Normal file
Binary file not shown.
15
backend_service/app.py
Normal file
15
backend_service/app.py
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
import json
|
||||
from flask import Flask, jsonify
|
||||
from flask_cors import CORS
|
||||
|
||||
app = Flask(__name__)
|
||||
CORS(app)
|
||||
|
||||
with open('config.json', mode='r') as f:
|
||||
datas = json.loads(f.read())
|
||||
|
||||
@app.route('/list')
|
||||
def list():
|
||||
return jsonify(datas)
|
||||
|
||||
app.run('0.0.0.0', port=80)
|
||||
44
backend_service/config.json
Normal file
44
backend_service/config.json
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
[
|
||||
{
|
||||
"last_online": 1764740658,
|
||||
"peer_id": 1,
|
||||
"tags": [
|
||||
{
|
||||
"name": "tag1",
|
||||
"price": 1,
|
||||
"sale": 11
|
||||
},
|
||||
{
|
||||
"name": "tag2",
|
||||
"price": 2,
|
||||
"sale": 12
|
||||
},
|
||||
{
|
||||
"name": "tag3",
|
||||
"price": 3,
|
||||
"sale": 13
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"last_online": 1764750653,
|
||||
"peer_id": 2,
|
||||
"tags": [
|
||||
{
|
||||
"name": "tag4",
|
||||
"price": 4,
|
||||
"sale": 14
|
||||
},
|
||||
{
|
||||
"name": "tag5",
|
||||
"price": 5,
|
||||
"sale": 15
|
||||
},
|
||||
{
|
||||
"name": "tag6",
|
||||
"price": 6,
|
||||
"sale": 16
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
9
backend_service/dockerfile
Normal file
9
backend_service/dockerfile
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
FROM python:3.9
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN pip3 install flask flask-cors
|
||||
|
||||
CMD ["python3", "app.py"]
|
||||
44
bitmap_service/app.py
Normal file
44
bitmap_service/app.py
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
from flask import Flask, request, jsonify
|
||||
from flask_cors import CORS
|
||||
from PIL import Image
|
||||
import requests
|
||||
import io
|
||||
|
||||
app = Flask(__name__)
|
||||
CORS(app)
|
||||
|
||||
@app.route('/')
|
||||
def root():
|
||||
return 'ok'
|
||||
|
||||
@app.route('/api/bitmap', methods=['POST'])
|
||||
def bitmap():
|
||||
|
||||
if 'file' not in request.files:
|
||||
return jsonify({"error": "no file"}), 400
|
||||
|
||||
file = request.files['file']
|
||||
|
||||
if file.filename == '':
|
||||
return jsonify({"error": "no file"}), 400
|
||||
|
||||
if not file.filename.lower().endswith('.png'):
|
||||
return jsonify({"error": "invalid file format"}), 400
|
||||
|
||||
try:
|
||||
image_stream = io.BytesIO(file.read())
|
||||
img = Image.open(image_stream)
|
||||
grayscale_img = img.convert('L')
|
||||
bitmap_data = list(grayscale_img.getdata())
|
||||
|
||||
payload = ''.join([chr(i) for i in bitmap_data])
|
||||
print(payload)
|
||||
req = requests.post('http://10.141.142.75/api/tag', data=payload)
|
||||
|
||||
return jsonify({'code': req.status_code})
|
||||
|
||||
except Exception as e:
|
||||
return jsonify({"error": e}), 500
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run('0.0.0.0', port=80)
|
||||
9
bitmap_service/dockerfile
Normal file
9
bitmap_service/dockerfile
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
FROM python:3.9
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN pip3 install pillow flask flask-cors requests
|
||||
|
||||
CMD ["python3", "app.py"]
|
||||
16
docker-compose.yml
Normal file
16
docker-compose.yml
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
services:
|
||||
frontend_service:
|
||||
build: ./frontend_service
|
||||
container_name: frontend_service
|
||||
ports:
|
||||
- "12000:3000"
|
||||
backend_service:
|
||||
build: ./backend_service
|
||||
container_name: backend_service
|
||||
ports:
|
||||
- "12001:80"
|
||||
bitmap_service:
|
||||
build: ./bitmap_service
|
||||
container_name: bitmap_service
|
||||
ports:
|
||||
- "12002:80"
|
||||
4
frontend_service/.dockerignore
Normal file
4
frontend_service/.dockerignore
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
.react-router
|
||||
build
|
||||
node_modules
|
||||
README.md
|
||||
7
frontend_service/.gitignore
vendored
Normal file
7
frontend_service/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
.DS_Store
|
||||
.env
|
||||
/node_modules/
|
||||
|
||||
# React Router
|
||||
/.react-router/
|
||||
/build/
|
||||
22
frontend_service/Dockerfile
Normal file
22
frontend_service/Dockerfile
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
FROM node:20-alpine AS development-dependencies-env
|
||||
COPY . /app
|
||||
WORKDIR /app
|
||||
RUN npm ci
|
||||
|
||||
FROM node:20-alpine AS production-dependencies-env
|
||||
COPY ./package.json package-lock.json /app/
|
||||
WORKDIR /app
|
||||
RUN npm ci --omit=dev
|
||||
|
||||
FROM node:20-alpine AS build-env
|
||||
COPY . /app/
|
||||
COPY --from=development-dependencies-env /app/node_modules /app/node_modules
|
||||
WORKDIR /app
|
||||
RUN npm run build
|
||||
|
||||
FROM node:20-alpine
|
||||
COPY ./package.json package-lock.json /app/
|
||||
COPY --from=production-dependencies-env /app/node_modules /app/node_modules
|
||||
COPY --from=build-env /app/build /app/build
|
||||
WORKDIR /app
|
||||
CMD ["npm", "run", "start"]
|
||||
87
frontend_service/README.md
Normal file
87
frontend_service/README.md
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
# Welcome to React Router!
|
||||
|
||||
A modern, production-ready template for building full-stack React applications using React Router.
|
||||
|
||||
[](https://stackblitz.com/github/remix-run/react-router-templates/tree/main/default)
|
||||
|
||||
## Features
|
||||
|
||||
- 🚀 Server-side rendering
|
||||
- ⚡️ Hot Module Replacement (HMR)
|
||||
- 📦 Asset bundling and optimization
|
||||
- 🔄 Data loading and mutations
|
||||
- 🔒 TypeScript by default
|
||||
- 🎉 TailwindCSS for styling
|
||||
- 📖 [React Router docs](https://reactrouter.com/)
|
||||
|
||||
## Getting Started
|
||||
|
||||
### Installation
|
||||
|
||||
Install the dependencies:
|
||||
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
|
||||
### Development
|
||||
|
||||
Start the development server with HMR:
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
|
||||
Your application will be available at `http://localhost:5173`.
|
||||
|
||||
## Building for Production
|
||||
|
||||
Create a production build:
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
|
||||
## Deployment
|
||||
|
||||
### Docker Deployment
|
||||
|
||||
To build and run using Docker:
|
||||
|
||||
```bash
|
||||
docker build -t my-app .
|
||||
|
||||
# Run the container
|
||||
docker run -p 3000:3000 my-app
|
||||
```
|
||||
|
||||
The containerized application can be deployed to any platform that supports Docker, including:
|
||||
|
||||
- AWS ECS
|
||||
- Google Cloud Run
|
||||
- Azure Container Apps
|
||||
- Digital Ocean App Platform
|
||||
- Fly.io
|
||||
- Railway
|
||||
|
||||
### DIY Deployment
|
||||
|
||||
If you're familiar with deploying Node applications, the built-in app server is production-ready.
|
||||
|
||||
Make sure to deploy the output of `npm run build`
|
||||
|
||||
```
|
||||
├── package.json
|
||||
├── package-lock.json (or pnpm-lock.yaml, or bun.lockb)
|
||||
├── build/
|
||||
│ ├── client/ # Static assets
|
||||
│ └── server/ # Server-side code
|
||||
```
|
||||
|
||||
## Styling
|
||||
|
||||
This template comes with [Tailwind CSS](https://tailwindcss.com/) already configured for a simple default starting experience. You can use whatever CSS framework you prefer.
|
||||
|
||||
---
|
||||
|
||||
Built with ❤️ using React Router.
|
||||
64
frontend_service/app/app.css
Normal file
64
frontend_service/app/app.css
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
body {
|
||||
font-family: 'Noto Sans TC', 'Heiti TC', sans-serif;
|
||||
background-color: #f5f5f5;
|
||||
margin: 0;
|
||||
padding: 20px;
|
||||
color: #333;
|
||||
margin-left: 50px;
|
||||
}
|
||||
|
||||
|
||||
h1 {
|
||||
font-size: 2.5em;
|
||||
margin-bottom: 30px;
|
||||
font-weight: 700;
|
||||
color: #2c3e50;
|
||||
}
|
||||
|
||||
/* 機器列表佈局 (使用 Flexbox) */
|
||||
.machine-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap; /* 允許卡片換行 */
|
||||
gap: 20px; /* 卡片之間的間距 */
|
||||
}
|
||||
|
||||
.machine-card {
|
||||
background-color: #ffffff;
|
||||
border-radius: 12px;
|
||||
padding: 25px;
|
||||
width: 280px; /* 卡片固定寬度,可根據需求調整 */
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); /* 輕微陰影,增加立體感 */
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
height: 150px;
|
||||
}
|
||||
|
||||
.machine-card:hover {
|
||||
transform: translateY(-5px); /* 鼠標懸停時輕微上移 */
|
||||
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.15); /* 陰影加深 */
|
||||
}
|
||||
|
||||
.label-name {
|
||||
font-size: 1.2em;
|
||||
font-weight: 600;
|
||||
color: #2c3e50;
|
||||
margin-bottom: auto;
|
||||
}
|
||||
|
||||
.status-indicator {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
border-radius: 50%;
|
||||
align-self: flex-start; /* 將圓點放置在卡片底部左側 */
|
||||
border: 3px solid rgba(0, 0, 0, 0.05); /* 輕微邊框,使其更清晰 */
|
||||
}
|
||||
|
||||
.status-indicator.running {
|
||||
background-color: #4CAF50; /* 綠色 */
|
||||
}
|
||||
|
||||
.status-indicator.error {
|
||||
background-color: #F44336; /* 紅色 */
|
||||
}
|
||||
75
frontend_service/app/root.tsx
Normal file
75
frontend_service/app/root.tsx
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
import {
|
||||
isRouteErrorResponse,
|
||||
Links,
|
||||
Meta,
|
||||
Outlet,
|
||||
Scripts,
|
||||
ScrollRestoration,
|
||||
} from "react-router";
|
||||
|
||||
import type { Route } from "./+types/root";
|
||||
import "./app.css";
|
||||
|
||||
export const links: Route.LinksFunction = () => [
|
||||
{ rel: "preconnect", href: "https://fonts.googleapis.com" },
|
||||
{
|
||||
rel: "preconnect",
|
||||
href: "https://fonts.gstatic.com",
|
||||
crossOrigin: "anonymous",
|
||||
},
|
||||
{
|
||||
rel: "stylesheet",
|
||||
href: "https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap",
|
||||
},
|
||||
];
|
||||
|
||||
export function Layout({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charSet="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<Meta />
|
||||
<Links />
|
||||
</head>
|
||||
<body>
|
||||
{children}
|
||||
<ScrollRestoration />
|
||||
<Scripts />
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
|
||||
export default function App() {
|
||||
return <Outlet />;
|
||||
}
|
||||
|
||||
export function ErrorBoundary({ error }: Route.ErrorBoundaryProps) {
|
||||
let message = "Oops!";
|
||||
let details = "An unexpected error occurred.";
|
||||
let stack: string | undefined;
|
||||
|
||||
if (isRouteErrorResponse(error)) {
|
||||
message = error.status === 404 ? "404" : "Error";
|
||||
details =
|
||||
error.status === 404
|
||||
? "The requested page could not be found."
|
||||
: error.statusText || details;
|
||||
} else if (import.meta.env.DEV && error && error instanceof Error) {
|
||||
details = error.message;
|
||||
stack = error.stack;
|
||||
}
|
||||
|
||||
return (
|
||||
<main className="pt-16 p-4 container mx-auto">
|
||||
<h1>{message}</h1>
|
||||
<p>{details}</p>
|
||||
{stack && (
|
||||
<pre className="w-full p-4 overflow-x-auto">
|
||||
<code>{stack}</code>
|
||||
</pre>
|
||||
)}
|
||||
</main>
|
||||
);
|
||||
}
|
||||
7
frontend_service/app/routes.ts
Normal file
7
frontend_service/app/routes.ts
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
import { type RouteConfig, index, route, } from "@react-router/dev/routes";
|
||||
|
||||
export default [
|
||||
index("routes/home.tsx"),
|
||||
route("details/:name", "routes/detail.tsx"),
|
||||
route("*", "routes/404.tsx")
|
||||
] satisfies RouteConfig;
|
||||
8
frontend_service/app/routes/404.tsx
Normal file
8
frontend_service/app/routes/404.tsx
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
export default function NotFound(){
|
||||
return (
|
||||
<>
|
||||
<h1>404</h1>
|
||||
<p>The requested page could not be found.</p>
|
||||
</>
|
||||
)
|
||||
}
|
||||
41
frontend_service/app/routes/components/canvas.tsx
Normal file
41
frontend_service/app/routes/components/canvas.tsx
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
import React, { useEffect, forwardRef } from "react";
|
||||
|
||||
interface CanvaProps {
|
||||
name: string;
|
||||
price: string;
|
||||
}
|
||||
|
||||
const Canva = forwardRef<HTMLCanvasElement, CanvaProps>((props, ref) => {
|
||||
useEffect(()=>{
|
||||
const canvas = (ref as React.RefObject<HTMLCanvasElement> | null)?.current;
|
||||
|
||||
if (!canvas) return;
|
||||
const context = canvas.getContext("2d")
|
||||
if (!context) return;
|
||||
|
||||
canvas.width = 296
|
||||
canvas.height = 152
|
||||
context.clearRect(0, 0, canvas.width, canvas.height)
|
||||
context.font = "24px 'Inter', Arial"
|
||||
context.textAlign = "center"
|
||||
context.fillStyle = "#374151"
|
||||
context.fillText(props.name, canvas.width/2, 50)
|
||||
context.font = "48px 'Inter', Arial";
|
||||
context.fillStyle = "#111827";
|
||||
context.fillText(`$${props.price}`, canvas.width / 2, 110);
|
||||
|
||||
}, [props.name, props.price, ref]);
|
||||
|
||||
return (
|
||||
<div style={{
|
||||
display: 'flex',
|
||||
marginTop: '20px'
|
||||
}}>
|
||||
<canvas ref={ref} style={{
|
||||
border: '1px solid black'
|
||||
}}></canvas>
|
||||
</div>
|
||||
)
|
||||
});
|
||||
|
||||
export default Canva;
|
||||
15
frontend_service/app/routes/components/card.tsx
Normal file
15
frontend_service/app/routes/components/card.tsx
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
import { Link } from "react-router"
|
||||
|
||||
export default function(props: any){
|
||||
let s = `status-indicator ${['error', 'running'][props.status]}`
|
||||
return(
|
||||
<Link to={'/details/'+props.metadata.name}
|
||||
style={{textDecoration: 'none'}}
|
||||
state={{metadata: props.metadata}}>
|
||||
<div className="machine-card">
|
||||
<span className="label-name">{props.metadata.name}</span>
|
||||
<div className={s}></div>
|
||||
</div>
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
101
frontend_service/app/routes/detail.tsx
Normal file
101
frontend_service/app/routes/detail.tsx
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
import Canva from './components/canvas';
|
||||
import type { Route } from "./+types/home";
|
||||
import { useState, useEffect, useRef } from 'react';
|
||||
import { Link, Navigate, useLocation } from 'react-router';
|
||||
|
||||
export function meta({}: Route.MetaArgs) {
|
||||
return [
|
||||
{ title: "detail" }
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
export default function Defail(){
|
||||
const DivStyle = {
|
||||
padding: '10px'
|
||||
}
|
||||
const BtnStyle = {
|
||||
padding: '10px',
|
||||
fontSize: '16px',
|
||||
borderRadius: '5px',
|
||||
border: '1px solid #ccc',
|
||||
}
|
||||
|
||||
const [name, setName] = useState('')
|
||||
const [price, setPrice] = useState('')
|
||||
const [sale, setSale] = useState('')
|
||||
|
||||
const canvasRef = useRef<HTMLCanvasElement>(null)
|
||||
|
||||
const location = useLocation()
|
||||
const metadata = location.state?.metadata
|
||||
|
||||
if(!metadata){
|
||||
return <Navigate to="/" replace/>
|
||||
}
|
||||
|
||||
useEffect(()=>{
|
||||
if(metadata){
|
||||
setName(metadata.name)
|
||||
setPrice(metadata.price)
|
||||
setSale(metadata.sale)
|
||||
}
|
||||
}, [metadata])
|
||||
|
||||
async function handleClick(){
|
||||
const canvas = canvasRef.current
|
||||
if(!canvas){
|
||||
console.error("Canvas 元素尚未準備好。")
|
||||
return
|
||||
}
|
||||
|
||||
const blob: Blob | null = await new Promise(resolve=>{
|
||||
if (canvas instanceof HTMLCanvasElement && typeof canvas.toBlob === 'function') {
|
||||
canvas.toBlob((blobResult)=>{
|
||||
resolve(blobResult)
|
||||
}, 'image/png')
|
||||
} else {
|
||||
resolve(null);
|
||||
}
|
||||
})
|
||||
|
||||
if(!blob){
|
||||
return
|
||||
}
|
||||
|
||||
const formdata = new FormData()
|
||||
|
||||
formdata.append('file', blob, '哈哈哈哈屁眼派對.png')
|
||||
formdata.append('name', name)
|
||||
formdata.append('price', price)
|
||||
formdata.append('sale', sale)
|
||||
|
||||
const req = await fetch("https://bitmap-service.docker.orb.local/api/bitmap", {
|
||||
method: 'POST',
|
||||
body: formdata,
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Link to="/">Home</Link>
|
||||
<h1>Tag詳細資訊</h1>
|
||||
<div style={DivStyle}>
|
||||
<label>名稱: </label>
|
||||
<input type="text" style={BtnStyle} value={name} onChange={e=>{setName(e.target.value)}} />
|
||||
</div>
|
||||
<div style={DivStyle}>
|
||||
<label>售價: </label>
|
||||
<input type="number" style={BtnStyle} value={price} onChange={e=>{setPrice(e.target.value)}}/>
|
||||
</div>
|
||||
<div style={DivStyle}>
|
||||
<label>折扣: </label>
|
||||
<input type="text" style={BtnStyle} value={sale} onChange={e=>{setSale(e.target.value)}}/>
|
||||
</div>
|
||||
{/* Canva 組件必須使用 forwardRef 才能接收 ref */}
|
||||
<Canva name={name} price={price} ref={canvasRef}/>
|
||||
<br />
|
||||
<input type="button" value="送出" onClick={handleClick}/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
65
frontend_service/app/routes/home.tsx
Normal file
65
frontend_service/app/routes/home.tsx
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
import type { Route } from "./+types/home";
|
||||
import { useEffect, useState } from 'react'
|
||||
import Card from './components/card'
|
||||
|
||||
export function meta({}: Route.MetaArgs) {
|
||||
return [
|
||||
{ title: "Index" }
|
||||
];
|
||||
}
|
||||
|
||||
interface TagInfo{
|
||||
name: string,
|
||||
price: number,
|
||||
sale: number
|
||||
}
|
||||
|
||||
interface Peer{
|
||||
peer_id: number,
|
||||
last_online: number,
|
||||
tags: [TagInfo]
|
||||
}
|
||||
|
||||
|
||||
export default function Home(){
|
||||
const [Peers, setPeers] = useState<Peer[]>([])
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
|
||||
useEffect(()=>{
|
||||
const fetchData = async () => {
|
||||
try{
|
||||
const response = await fetch('https://backend-service.docker.orb.local/list')
|
||||
if(!response.ok){
|
||||
throw new Error('Network error')
|
||||
}
|
||||
const data = await response.json()
|
||||
setPeers(data)
|
||||
}catch(error){
|
||||
if(error instanceof Error){
|
||||
setError(error.message)
|
||||
}else{
|
||||
setError('Unknown error')
|
||||
}
|
||||
}finally{
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
fetchData()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<>
|
||||
<h1>機器列表</h1>
|
||||
<div className="machine-list">
|
||||
{loading && <p>Loading...</p>}
|
||||
{error && <p>Error: {error}</p>}
|
||||
{!loading && !error && Peers.map(Peer=>{
|
||||
return Peer.tags.map(Tag=>{
|
||||
return <Card key={Tag.name} name={Tag.name} status='1' metadata={Tag}/>
|
||||
})
|
||||
})}
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
4099
frontend_service/package-lock.json
generated
Normal file
4099
frontend_service/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
30
frontend_service/package.json
Normal file
30
frontend_service/package.json
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
{
|
||||
"name": "newnew",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "react-router build",
|
||||
"dev": "react-router dev",
|
||||
"start": "react-router-serve ./build/server/index.js",
|
||||
"typecheck": "react-router typegen && tsc"
|
||||
},
|
||||
"dependencies": {
|
||||
"@react-router/node": "^7.9.2",
|
||||
"@react-router/serve": "^7.9.2",
|
||||
"isbot": "^5.1.31",
|
||||
"react": "^19.1.1",
|
||||
"react-dom": "^19.1.1",
|
||||
"react-router": "^7.9.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@react-router/dev": "^7.9.2",
|
||||
"@tailwindcss/vite": "^4.1.13",
|
||||
"@types/node": "^22",
|
||||
"@types/react": "^19.1.13",
|
||||
"@types/react-dom": "^19.1.9",
|
||||
"tailwindcss": "^4.1.13",
|
||||
"typescript": "^5.9.2",
|
||||
"vite": "^7.1.7",
|
||||
"vite-tsconfig-paths": "^5.1.4"
|
||||
}
|
||||
}
|
||||
BIN
frontend_service/public/favicon.ico
Normal file
BIN
frontend_service/public/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
7
frontend_service/react-router.config.ts
Normal file
7
frontend_service/react-router.config.ts
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
import type { Config } from "@react-router/dev/config";
|
||||
|
||||
export default {
|
||||
// Config options...
|
||||
// Server-side render by default, to enable SPA mode set this to `false`
|
||||
ssr: true,
|
||||
} satisfies Config;
|
||||
27
frontend_service/tsconfig.json
Normal file
27
frontend_service/tsconfig.json
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"include": [
|
||||
"**/*",
|
||||
"**/.server/**/*",
|
||||
"**/.client/**/*",
|
||||
".react-router/types/**/*"
|
||||
],
|
||||
"compilerOptions": {
|
||||
"lib": ["DOM", "DOM.Iterable", "ES2022"],
|
||||
"types": ["node", "vite/client"],
|
||||
"target": "ES2022",
|
||||
"module": "ES2022",
|
||||
"moduleResolution": "bundler",
|
||||
"jsx": "react-jsx",
|
||||
"rootDirs": [".", "./.react-router/types"],
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"~/*": ["./app/*"]
|
||||
},
|
||||
"esModuleInterop": true,
|
||||
"verbatimModuleSyntax": true,
|
||||
"noEmit": true,
|
||||
"resolveJsonModule": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true
|
||||
}
|
||||
}
|
||||
8
frontend_service/vite.config.ts
Normal file
8
frontend_service/vite.config.ts
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
import { reactRouter } from "@react-router/dev/vite";
|
||||
import tailwindcss from "@tailwindcss/vite";
|
||||
import { defineConfig } from "vite";
|
||||
import tsconfigPaths from "vite-tsconfig-paths";
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [tailwindcss(), reactRouter(), tsconfigPaths()],
|
||||
});
|
||||
Loading…
Add table
Reference in a new issue