[front]: api fetch
2
front/src/api/floris/constants.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
// export const BASE_URL = 'http://localhost:8000/api';
|
||||||
|
export const BASE_URL = 'http://192.168.1.110:8000/api';
|
@ -0,0 +1 @@
|
|||||||
|
export { downloadImage, getWindmillData } from './service';
|
26
front/src/api/floris/service.ts
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import { WindmillFormStore } from '@components/ux/windmill-form';
|
||||||
|
|
||||||
|
import { BASE_URL } from './constants';
|
||||||
|
import { GetWindmillDataRes } from './types';
|
||||||
|
import { getWindmillDataParams } from './utils';
|
||||||
|
|
||||||
|
export const getWindmillData = async (store: Partial<WindmillFormStore>) => {
|
||||||
|
const params = getWindmillDataParams(store);
|
||||||
|
const url = `${BASE_URL}/floris/get_windmill_data?${params}`;
|
||||||
|
const init: RequestInit = {
|
||||||
|
method: 'GET',
|
||||||
|
};
|
||||||
|
const res: Response = await fetch(url, init);
|
||||||
|
const data: GetWindmillDataRes = await res.json();
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const downloadImage = async (imageName: string) => {
|
||||||
|
const url = `${BASE_URL}/floris/download_image/${imageName}`;
|
||||||
|
const init: RequestInit = {
|
||||||
|
method: 'GET',
|
||||||
|
};
|
||||||
|
const res: Response = await fetch(url, init);
|
||||||
|
const data = await res.blob();
|
||||||
|
return data;
|
||||||
|
};
|
4
front/src/api/floris/types.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
export type GetWindmillDataRes = {
|
||||||
|
file_name: string;
|
||||||
|
data: number[];
|
||||||
|
};
|
9
front/src/api/floris/utils.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import { WindmillFormStore } from '@components/ux/windmill-form';
|
||||||
|
|
||||||
|
export const getWindmillDataParams = (store: Partial<WindmillFormStore>) => {
|
||||||
|
const layoutX = store.windmills?.map((row) => `layout_x=${row.x}`).join('&');
|
||||||
|
const layoutY = store.windmills?.map((row) => `layout_y=${row.y}`).join('&');
|
||||||
|
const dateStart = `date_start=${store.dateFrom?.substring(0, 10)}`;
|
||||||
|
const dateEnd = `date_end=${store.dateTo?.substring(0, 10)}`;
|
||||||
|
return `${layoutX}&${layoutY}&${dateStart}&${dateEnd}`;
|
||||||
|
};
|
1
front/src/api/index.tsx
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from './floris';
|
@ -1,13 +1,31 @@
|
|||||||
|
import { Heading } from '@components/ui';
|
||||||
import { WindmillForm } from '@components/ux';
|
import { WindmillForm } from '@components/ux';
|
||||||
import React from 'react';
|
import { WindmillFormResponse } from '@components/ux/windmill-form';
|
||||||
|
import React, { useState } from 'react';
|
||||||
|
|
||||||
import styles from './styles.module.scss';
|
import styles from './styles.module.scss';
|
||||||
|
|
||||||
export function HomePage() {
|
export function HomePage() {
|
||||||
|
const [formResponse, setFormResponse] = useState<WindmillFormResponse>({
|
||||||
|
power: [],
|
||||||
|
image: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleFormResponse = (response: WindmillFormResponse) => {
|
||||||
|
setFormResponse(response);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.home}>
|
<div className={styles.page}>
|
||||||
<div className={styles.about}>
|
<div className={styles.wrapperForm}>
|
||||||
<WindmillForm className={styles.form} />
|
<WindmillForm onResponse={handleFormResponse} />
|
||||||
|
</div>
|
||||||
|
<div className={styles.result}>
|
||||||
|
<Heading tag="h3">Result</Heading>
|
||||||
|
<div className={styles.power}>{formResponse.power.join(' ')}</div>
|
||||||
|
<div className={styles.image}>
|
||||||
|
{formResponse.image && <img src={formResponse.image} alt="Image" />}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -1,11 +1,41 @@
|
|||||||
.about {
|
.page {
|
||||||
display: grid;
|
display: grid;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
|
gap: 20px;
|
||||||
grid-template:
|
grid-template:
|
||||||
'. form .' auto
|
'. form result .' auto
|
||||||
/ auto minmax(0, 380px) auto;
|
/ auto minmax(0, 380px) minmax(0, 700px) auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form {
|
.wrapperForm {
|
||||||
grid-area: form;
|
grid-area: form;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.result {
|
||||||
|
display: grid;
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 10px;
|
||||||
|
background-color: var(--clr-layer-200);
|
||||||
|
box-shadow: 0px 1px 2px var(--clr-shadow-100);
|
||||||
|
gap: 20px;
|
||||||
|
grid-area: result;
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image {
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
img {
|
||||||
|
max-width: 100%;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (width <= 1000px) {
|
||||||
|
.page {
|
||||||
|
grid-template:
|
||||||
|
'form' auto
|
||||||
|
'result' auto
|
||||||
|
/ 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -2,20 +2,28 @@ import { Button, DateInput, Heading } from '@components/ui';
|
|||||||
import { Controller, useForm } from '@utils/form';
|
import { Controller, useForm } from '@utils/form';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { downloadImage, getWindmillData } from 'src/api';
|
||||||
|
|
||||||
import { WindmillTable } from '../windmill-table';
|
import { WindmillTable } from '../windmill-table';
|
||||||
import { initialValues } from './constants';
|
import { initialValues } from './constants';
|
||||||
import styles from './styles.module.scss';
|
import styles from './styles.module.scss';
|
||||||
import { WindmillFormProps, WindmillFormStore } from './types';
|
import { WindmillFormProps, WindmillFormStore } from './types';
|
||||||
|
|
||||||
export function WindmillForm({ className, ...props }: WindmillFormProps) {
|
export function WindmillForm({
|
||||||
const { control, reset } = useForm<WindmillFormStore>({
|
onResponse,
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: WindmillFormProps) {
|
||||||
|
const { control, reset, getValues } = useForm<WindmillFormStore>({
|
||||||
initialValues,
|
initialValues,
|
||||||
});
|
});
|
||||||
const classNames = clsx(className, styles.form);
|
const classNames = clsx(className, styles.form);
|
||||||
|
|
||||||
const handleSubmit = (event: React.FormEvent) => {
|
const handleSubmit = async (event: React.FormEvent) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
const res = await getWindmillData(getValues());
|
||||||
|
const image = await downloadImage(res.file_name);
|
||||||
|
onResponse({ power: res.data, image: URL.createObjectURL(image) });
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleResetButtonClick = () => {
|
const handleResetButtonClick = () => {
|
||||||
|
@ -1,2 +1,6 @@
|
|||||||
export { WindmillForm } from './component';
|
export { WindmillForm } from './component';
|
||||||
export { type WindmillConfig } from './types';
|
export {
|
||||||
|
type WindmillConfig,
|
||||||
|
type WindmillFormResponse,
|
||||||
|
type WindmillFormStore,
|
||||||
|
} from './types';
|
||||||
|
@ -10,4 +10,11 @@ export type WindmillFormStore = {
|
|||||||
windmills: WindmillConfig[];
|
windmills: WindmillConfig[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type WindmillFormProps = {} & React.ComponentProps<'form'>;
|
export type WindmillFormResponse = {
|
||||||
|
power: number[];
|
||||||
|
image: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type WindmillFormProps = {
|
||||||
|
onResponse: (response: WindmillFormResponse) => void;
|
||||||
|
} & React.ComponentProps<'form'>;
|
||||||
|
@ -31,7 +31,7 @@ export function WindmillTable({ value, onChange }: WindmillTableProps) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div className={styles.table}>
|
||||||
<header className={styles.header}>
|
<header className={styles.header}>
|
||||||
<Span className={styles.span} />
|
<Span className={styles.span} />
|
||||||
<Span className={styles.span}>x</Span>
|
<Span className={styles.span}>x</Span>
|
||||||
|
@ -1,3 +1,9 @@
|
|||||||
|
.table {
|
||||||
|
border-radius: 10px;
|
||||||
|
background-color: var(--clr-layer-200);
|
||||||
|
box-shadow: 0px 2px 2px var(--clr-shadow-100);
|
||||||
|
}
|
||||||
|
|
||||||
.header {
|
.header {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 46px 1fr 1fr 1fr;
|
grid-template-columns: 46px 1fr 1fr 1fr;
|
||||||
|
BIN
server/public/floris/10d49437-7cfe-4916-be63-185414713dbc.png
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
server/public/floris/11c6c379-5bbc-4b69-b9da-dc8ddfaa9fe0.png
Normal file
After Width: | Height: | Size: 20 KiB |
BIN
server/public/floris/201e0a6d-9f88-4cc4-a0d9-8b70cb6733c9.png
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
server/public/floris/21355189-7f85-45d4-ab9c-0318c641cb9e.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
server/public/floris/23eeab0b-d1c8-4878-b279-f9ab015f86d3.png
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
server/public/floris/3129dcf4-e51f-4506-bef7-13a25236d74d.png
Normal file
After Width: | Height: | Size: 34 KiB |
BIN
server/public/floris/32e5bb87-b1c3-4702-a5ee-c3a20531cf9b.png
Normal file
After Width: | Height: | Size: 34 KiB |
BIN
server/public/floris/3853a22d-7b97-4332-b376-9f5bf5bc9a32.png
Normal file
After Width: | Height: | Size: 34 KiB |
BIN
server/public/floris/46bd2173-0b7b-438f-a6ec-605dae8c747a.png
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
server/public/floris/4b4fca7d-3d50-4c43-a882-95dcb5097dea.png
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
server/public/floris/536f2c1f-5ba2-4132-a4f8-198b3a08049f.png
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
server/public/floris/5cdff5db-da5c-4b34-aa5a-77e97970f0eb.png
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
server/public/floris/666e5339-abdf-42a2-95d2-08624c4d2dd0.png
Normal file
After Width: | Height: | Size: 35 KiB |
BIN
server/public/floris/6fd4366c-4a95-4000-a3c5-f3d21454cbb3.png
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
server/public/floris/72730cb8-c22d-4e5f-8fab-7f11efc3722c.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
server/public/floris/739023a2-c499-416c-b337-688765e9d168.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
server/public/floris/796471b9-458b-421b-9542-540d580417b4.png
Normal file
After Width: | Height: | Size: 29 KiB |
BIN
server/public/floris/7dea2b6f-0ac6-4337-b8ca-65ea49bc6a2e.png
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
server/public/floris/8892e6aa-e89a-4474-87d0-c4c612cfcb86.png
Normal file
After Width: | Height: | Size: 39 KiB |
BIN
server/public/floris/88cac8b7-9809-4254-819e-120de581c9eb.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
server/public/floris/891aa2e6-e485-491c-bf99-ba27a685da6c.png
Normal file
After Width: | Height: | Size: 23 KiB |
BIN
server/public/floris/8a5dbea5-95e5-4b5d-aa82-5165cf4bf748.png
Normal file
After Width: | Height: | Size: 36 KiB |
BIN
server/public/floris/9130196b-589a-4f64-b08c-82c254dfc45d.png
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
server/public/floris/95b56284-e148-4578-aed2-5ac2516671f5.png
Normal file
After Width: | Height: | Size: 39 KiB |
BIN
server/public/floris/9a553ecd-7dbe-4ac4-af1e-c3f240cbd0e5.png
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
server/public/floris/9f0341b1-b3aa-420c-a992-488ea3089704.png
Normal file
After Width: | Height: | Size: 36 KiB |
BIN
server/public/floris/a32bc4ae-2b8d-4385-afcb-f2d52438ecca.png
Normal file
After Width: | Height: | Size: 25 KiB |
BIN
server/public/floris/a46a6ec8-37d1-46fa-bbcd-742095bb2dbc.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
server/public/floris/ac5d2be4-35f3-493b-92e7-255d26bd82a5.png
Normal file
After Width: | Height: | Size: 9.5 KiB |
BIN
server/public/floris/bb06d08e-fea3-4d15-b58f-9a328192c449.png
Normal file
After Width: | Height: | Size: 25 KiB |
BIN
server/public/floris/d0ef5542-8044-49ec-a744-f2e367b52bba.png
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
server/public/floris/d9b256a1-468c-434b-a9f9-e92c1019f360.png
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
server/public/floris/d9e2a707-c2d7-4eaa-a733-66444bd1e5a0.png
Normal file
After Width: | Height: | Size: 9.5 KiB |
BIN
server/public/floris/dc3e8cb1-7d7c-4914-aee2-cf5d2750aa1c.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
server/public/floris/e094937c-5b79-4fa8-b0ad-1d39c11eb419.png
Normal file
After Width: | Height: | Size: 34 KiB |
BIN
server/public/floris/e544e81f-989f-44e4-a3a4-d58cc8824ccb.png
Normal file
After Width: | Height: | Size: 36 KiB |
BIN
server/public/floris/ebdc4e43-53a6-4ec1-a7d7-d9dae8e06397.png
Normal file
After Width: | Height: | Size: 26 KiB |
BIN
server/public/floris/f66ca7d5-3983-4220-aac9-94fd4ca689dd.png
Normal file
After Width: | Height: | Size: 34 KiB |
BIN
server/public/floris/fb81bda6-a28b-475a-8e29-154da40e12d6.png
Normal file
After Width: | Height: | Size: 24 KiB |
BIN
server/src/.cache.sqlite
Normal file
@ -9,10 +9,24 @@ from fastapi.staticfiles import StaticFiles
|
|||||||
from routers.floris_router import router as floris_router
|
from routers.floris_router import router as floris_router
|
||||||
from routers.floris_template_router import router as floris_template_router
|
from routers.floris_template_router import router as floris_template_router
|
||||||
from routers.weather_router import router as weather_router
|
from routers.weather_router import router as weather_router
|
||||||
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
|
|
||||||
|
|
||||||
app = FastAPI()
|
app = FastAPI()
|
||||||
|
|
||||||
|
origins = [
|
||||||
|
"http://localhost:5000",
|
||||||
|
"*"
|
||||||
|
]
|
||||||
|
|
||||||
|
app.add_middleware(
|
||||||
|
CORSMiddleware,
|
||||||
|
allow_origins=origins,
|
||||||
|
allow_credentials=True,
|
||||||
|
allow_methods=["*"],
|
||||||
|
allow_headers=["*"],
|
||||||
|
)
|
||||||
|
|
||||||
app.mount("/static", StaticFiles(directory=Path("../static")), name="static")
|
app.mount("/static", StaticFiles(directory=Path("../static")), name="static")
|
||||||
app.mount("/public", StaticFiles(directory=Path("../public")), name="public")
|
app.mount("/public", StaticFiles(directory=Path("../public")), name="public")
|
||||||
|
|
||||||
|