[test-entity]: front pt.2
This commit is contained in:
parent
a60304ca0f
commit
658a351d28
@ -3,4 +3,5 @@ export const WIND_ENDPOINTS = {
|
|||||||
turbineType: 'api/wind/turbine_type',
|
turbineType: 'api/wind/turbine_type',
|
||||||
parks: 'api/wind/parks',
|
parks: 'api/wind/parks',
|
||||||
park: 'api/wind/park',
|
park: 'api/wind/park',
|
||||||
|
parkTurbine: 'api/wind/park_turbine',
|
||||||
};
|
};
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
import { ApiResponse } from '@api/types';
|
import { ApiResponse } from '@api/types';
|
||||||
|
import { ParkFormValues } from '@components/pages/park-page/types';
|
||||||
import { TurbineTypeFormValues } from '@components/pages/turbine-type-page/types';
|
import { TurbineTypeFormValues } from '@components/pages/turbine-type-page/types';
|
||||||
|
|
||||||
import { api } from '../api';
|
import { api } from '../api';
|
||||||
import { WIND_ENDPOINTS } from './constants';
|
import { WIND_ENDPOINTS } from './constants';
|
||||||
import { Park, ParkTurbine, ParkWithTurbines, TurbineType } from './types';
|
import { Park, ParkTurbine, ParkWithTurbines, TurbineType } from './types';
|
||||||
import { packTurbineTypes } from './utils';
|
import { packPark, packParkTurbine, packTurbineTypes } from './utils';
|
||||||
|
|
||||||
export const getTurbineTypes = () => {
|
export const getTurbineTypes = () => {
|
||||||
return api.get<TurbineType[]>(WIND_ENDPOINTS.turbines);
|
return api.get<TurbineType[]>(WIND_ENDPOINTS.turbines);
|
||||||
@ -63,3 +64,47 @@ export const getParkWithTurbines = async (
|
|||||||
error: parkPesponse.error || turbinesResponse.error || null,
|
error: parkPesponse.error || turbinesResponse.error || null,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const createPark = async (formValues: Partial<ParkFormValues>) => {
|
||||||
|
const parkPesponse = await api.post<Park>(
|
||||||
|
WIND_ENDPOINTS.park,
|
||||||
|
packPark(formValues),
|
||||||
|
);
|
||||||
|
await Promise.all(
|
||||||
|
formValues.turbines?.map((t) => {
|
||||||
|
return api.post(
|
||||||
|
WIND_ENDPOINTS.parkTurbine,
|
||||||
|
packParkTurbine(t, parkPesponse.data.id),
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
return getParkWithTurbines(String(parkPesponse.data.id));
|
||||||
|
};
|
||||||
|
|
||||||
|
export const updatePark = async (
|
||||||
|
formValues: Partial<ParkFormValues>,
|
||||||
|
id: string,
|
||||||
|
) => {
|
||||||
|
const parkPesponse = await api.put<Park>(
|
||||||
|
`${WIND_ENDPOINTS.park}/${id}`,
|
||||||
|
packPark(formValues),
|
||||||
|
);
|
||||||
|
await Promise.all(
|
||||||
|
formValues.turbines?.map((t) => {
|
||||||
|
if (t.new) {
|
||||||
|
return api.post(
|
||||||
|
WIND_ENDPOINTS.parkTurbine,
|
||||||
|
packParkTurbine(t, parkPesponse.data.id),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (t.delete) {
|
||||||
|
return api.delete(`${WIND_ENDPOINTS.parkTurbine}/${t.id}`);
|
||||||
|
}
|
||||||
|
return api.put(
|
||||||
|
`${WIND_ENDPOINTS.parkTurbine}/${parkPesponse.data.id}/${t.id}`,
|
||||||
|
packParkTurbine(t, parkPesponse.data.id),
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
return getParkWithTurbines(id);
|
||||||
|
};
|
||||||
|
@ -1,9 +1,32 @@
|
|||||||
|
import {
|
||||||
|
ParkFormTurbine,
|
||||||
|
ParkFormValues,
|
||||||
|
} from '@components/pages/park-page/types';
|
||||||
import { TurbineTypeFormValues } from '@components/pages/turbine-type-page/types';
|
import { TurbineTypeFormValues } from '@components/pages/turbine-type-page/types';
|
||||||
|
|
||||||
export const packTurbineTypes = (values: Partial<TurbineTypeFormValues>) => {
|
export const packTurbineTypes = (values: Partial<TurbineTypeFormValues>) => {
|
||||||
return {
|
return {
|
||||||
Name: values.name ?? '',
|
Name: values.name ?? '',
|
||||||
Height: parseInt(values.height ?? '0'),
|
Height: parseInt(values.height || '0'),
|
||||||
BladeLength: parseInt(values.bladeLength ?? '0'),
|
BladeLength: parseInt(values.bladeLength || '0'),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const packPark = (values: Partial<ParkFormValues>) => {
|
||||||
|
return {
|
||||||
|
Name: values.name ?? '',
|
||||||
|
CenterLatitude: parseInt(values.centerLatitude || '0'),
|
||||||
|
CenterLongitude: parseInt(values.centerLongitude || '0'),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const packParkTurbine = (turbine: ParkFormTurbine, parkId: number) => {
|
||||||
|
return {
|
||||||
|
wind_park_id: parkId,
|
||||||
|
turbine_id: turbine.id,
|
||||||
|
x_offset: parseInt(turbine.xOffset || '0'),
|
||||||
|
y_offset: parseInt(turbine.yOffset || '0'),
|
||||||
|
angle: parseInt(turbine.angle || '0'),
|
||||||
|
comment: turbine.comment ?? '',
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -1,11 +1,16 @@
|
|||||||
import { getParkWithTurbines, ParkWithTurbines } from '@api/wind';
|
import {
|
||||||
import { Button, Heading, NumberInput, TextInput } from '@components/ui';
|
createPark,
|
||||||
|
getParkWithTurbines,
|
||||||
|
ParkWithTurbines,
|
||||||
|
updatePark,
|
||||||
|
} from '@api/wind';
|
||||||
|
import { Button, Heading, NumberField, TextInput } from '@components/ui';
|
||||||
|
import { ParkTurbines } from '@components/ux';
|
||||||
import { Controller, useForm } from '@utils/form';
|
import { Controller, useForm } from '@utils/form';
|
||||||
import { useRoute } from '@utils/route';
|
import { ROUTES, useRoute } from '@utils/route';
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useNavigate, useParams } from 'react-router-dom';
|
||||||
|
|
||||||
import { ParkTurbines } from './components';
|
|
||||||
import styles from './styles.module.scss';
|
import styles from './styles.module.scss';
|
||||||
import { ParkFormValues } from './types';
|
import { ParkFormValues } from './types';
|
||||||
import { unpackPark } from './utils';
|
import { unpackPark } from './utils';
|
||||||
@ -14,6 +19,7 @@ export function ParkPage() {
|
|||||||
const [park, setPark] = useState<ParkWithTurbines>(null);
|
const [park, setPark] = useState<ParkWithTurbines>(null);
|
||||||
const [pending, setPending] = useState<boolean>(false);
|
const [pending, setPending] = useState<boolean>(false);
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
|
const navigate = useNavigate();
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
|
|
||||||
const { register, control, getValues, reset } = useForm<ParkFormValues>({});
|
const { register, control, getValues, reset } = useForm<ParkFormValues>({});
|
||||||
@ -35,6 +41,20 @@ export function ParkPage() {
|
|||||||
fetchPark();
|
fetchPark();
|
||||||
}, [id]);
|
}, [id]);
|
||||||
|
|
||||||
|
const handleFormSubmit = async (event: React.FormEvent) => {
|
||||||
|
event.preventDefault();
|
||||||
|
setPending(true);
|
||||||
|
if (isEdit) {
|
||||||
|
const response = await updatePark(getValues(), id);
|
||||||
|
setPark(response.data);
|
||||||
|
reset(unpackPark(response.data));
|
||||||
|
} else {
|
||||||
|
const response = await createPark(getValues());
|
||||||
|
navigate(ROUTES.park.path.replace(':id', String(response.data.id)));
|
||||||
|
}
|
||||||
|
setPending(false);
|
||||||
|
};
|
||||||
|
|
||||||
const handleReset = () => {
|
const handleReset = () => {
|
||||||
if (isEdit) {
|
if (isEdit) {
|
||||||
reset(unpackPark(park));
|
reset(unpackPark(park));
|
||||||
@ -44,7 +64,7 @@ export function ParkPage() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.page}>
|
<div className={styles.page} onSubmit={handleFormSubmit}>
|
||||||
<Heading tag="h1">{route.title}</Heading>
|
<Heading tag="h1">{route.title}</Heading>
|
||||||
<form className={styles.form}>
|
<form className={styles.form}>
|
||||||
<header>
|
<header>
|
||||||
@ -55,13 +75,13 @@ export function ParkPage() {
|
|||||||
<Controller
|
<Controller
|
||||||
{...control('centerLatitude')}
|
{...control('centerLatitude')}
|
||||||
render={(props) => (
|
render={(props) => (
|
||||||
<NumberInput label={{ text: 'Center Latitude' }} {...props} />
|
<NumberField label={{ text: 'Center Latitude' }} {...props} />
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<Controller
|
<Controller
|
||||||
{...control('centerLongitude')}
|
{...control('centerLongitude')}
|
||||||
render={(props) => (
|
render={(props) => (
|
||||||
<NumberInput label={{ text: 'Center Longitude' }} {...props} />
|
<NumberField label={{ text: 'Center Longitude' }} {...props} />
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -76,7 +96,9 @@ export function ParkPage() {
|
|||||||
</form>
|
</form>
|
||||||
<Controller
|
<Controller
|
||||||
{...control('turbines')}
|
{...control('turbines')}
|
||||||
render={(props) => <ParkTurbines {...props} />}
|
render={(props) => (
|
||||||
|
<ParkTurbines savedTurbines={park?.turbines ?? []} {...props} />
|
||||||
|
)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -1 +0,0 @@
|
|||||||
export * from './park-turbines';
|
|
@ -1,19 +0,0 @@
|
|||||||
import { DataGrid } from '@components/ui/data-grid';
|
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
import { columns } from './constants';
|
|
||||||
import { ParkTurbinesProps } from './types';
|
|
||||||
|
|
||||||
export function ParkTurbines({ value = [] }: ParkTurbinesProps) {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<div></div>
|
|
||||||
<DataGrid
|
|
||||||
items={value}
|
|
||||||
columns={columns}
|
|
||||||
getItemKey={({ id }) => String(id)}
|
|
||||||
selectedItems={[]}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
import { DataGridColumnConfig } from '@components/ui/data-grid/types';
|
|
||||||
import { ParkTurbine } from 'src/api/wind';
|
|
||||||
|
|
||||||
export const columns: DataGridColumnConfig<ParkTurbine>[] = [
|
|
||||||
{ name: 'Id', getText: (t) => String(t.id) },
|
|
||||||
{ name: 'Name', getText: (t) => t.name },
|
|
||||||
{ name: 'X', getText: (t) => String(t.xOffset) },
|
|
||||||
{ name: 'Y', getText: (t) => String(t.yOffset) },
|
|
||||||
{ name: 'Angle', getText: (t) => String(t.angle) },
|
|
||||||
{ name: 'Comment', getText: (t) => String(t.comment), flex: '2' },
|
|
||||||
];
|
|
@ -1,6 +0,0 @@
|
|||||||
import { ParkTurbine } from '@api/wind';
|
|
||||||
|
|
||||||
export type ParkTurbinesProps = {
|
|
||||||
value?: ParkTurbine[];
|
|
||||||
onChange?: (value: ParkTurbine[]) => void;
|
|
||||||
};
|
|
@ -1,8 +1,17 @@
|
|||||||
import { ParkTurbine } from '@api/wind';
|
export type ParkFormTurbine = {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
xOffset: string;
|
||||||
|
yOffset: string;
|
||||||
|
angle: string;
|
||||||
|
comment: string;
|
||||||
|
new?: boolean;
|
||||||
|
delete?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
export type ParkFormValues = {
|
export type ParkFormValues = {
|
||||||
name: string;
|
name: string;
|
||||||
centerLatitude: string;
|
centerLatitude: string;
|
||||||
centerLongitude: string;
|
centerLongitude: string;
|
||||||
turbines: ParkTurbine[];
|
turbines: ParkFormTurbine[];
|
||||||
};
|
};
|
||||||
|
@ -7,6 +7,13 @@ export const unpackPark = (park: ParkWithTurbines): ParkFormValues => {
|
|||||||
name: park.name,
|
name: park.name,
|
||||||
centerLatitude: String(park.centerLatitude),
|
centerLatitude: String(park.centerLatitude),
|
||||||
centerLongitude: String(park.centerLongitude),
|
centerLongitude: String(park.centerLongitude),
|
||||||
turbines: park.turbines,
|
turbines: park.turbines.map((t) => ({
|
||||||
|
id: t.id,
|
||||||
|
name: t.name,
|
||||||
|
xOffset: String(t.xOffset),
|
||||||
|
yOffset: String(t.yOffset),
|
||||||
|
angle: String(t.angle),
|
||||||
|
comment: t.comment,
|
||||||
|
})),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -2,7 +2,7 @@ import { DataGridColumnConfig } from '@components/ui/data-grid/types';
|
|||||||
import { Park } from 'src/api/wind';
|
import { Park } from 'src/api/wind';
|
||||||
|
|
||||||
export const columns: DataGridColumnConfig<Park>[] = [
|
export const columns: DataGridColumnConfig<Park>[] = [
|
||||||
{ name: 'Name', getText: (t) => t.name, flex: '2' },
|
{ name: 'Name', getText: (t) => t.name, width: 2 },
|
||||||
{ name: 'Center Latitude', getText: (t) => String(t.centerLatitude) },
|
{ name: 'Center Latitude', getText: (t) => String(t.centerLatitude) },
|
||||||
{ name: 'Center Longitude', getText: (t) => String(t.centerLongitude) },
|
{ name: 'Center Longitude', getText: (t) => String(t.centerLongitude) },
|
||||||
];
|
];
|
||||||
|
@ -12,7 +12,6 @@
|
|||||||
.actions {
|
.actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
border: 1px solid var(--clr-border-100);
|
|
||||||
border-radius: 15px;
|
border-radius: 15px;
|
||||||
background-color: var(--clr-layer-200);
|
background-color: var(--clr-layer-200);
|
||||||
box-shadow: 0px 1px 2px var(--clr-shadow-100);
|
box-shadow: 0px 1px 2px var(--clr-shadow-100);
|
||||||
|
@ -4,7 +4,7 @@ import {
|
|||||||
getTurbineType,
|
getTurbineType,
|
||||||
TurbineType,
|
TurbineType,
|
||||||
} from '@api/wind';
|
} from '@api/wind';
|
||||||
import { Button, Heading, NumberInput, TextInput } from '@components/ui';
|
import { Button, Heading, NumberField, TextInput } from '@components/ui';
|
||||||
import { Controller, useForm } from '@utils/form';
|
import { Controller, useForm } from '@utils/form';
|
||||||
import { ROUTES, useRoute } from '@utils/route';
|
import { ROUTES, useRoute } from '@utils/route';
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
@ -47,6 +47,7 @@ export function TurbineTypePage() {
|
|||||||
if (isEdit) {
|
if (isEdit) {
|
||||||
const response = await editTurbineTypes(getValues(), id);
|
const response = await editTurbineTypes(getValues(), id);
|
||||||
setTurbineType(response.data);
|
setTurbineType(response.data);
|
||||||
|
reset(unpackTurbineType(response.data));
|
||||||
} else {
|
} else {
|
||||||
const response = await createTurbineTypes(getValues());
|
const response = await createTurbineTypes(getValues());
|
||||||
navigate(
|
navigate(
|
||||||
@ -76,13 +77,13 @@ export function TurbineTypePage() {
|
|||||||
<Controller
|
<Controller
|
||||||
{...control('height')}
|
{...control('height')}
|
||||||
render={(props) => (
|
render={(props) => (
|
||||||
<NumberInput label={{ text: 'Height' }} {...props} />
|
<NumberField label={{ text: 'Height' }} {...props} />
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<Controller
|
<Controller
|
||||||
{...control('bladeLength')}
|
{...control('bladeLength')}
|
||||||
render={(props) => (
|
render={(props) => (
|
||||||
<NumberInput label={{ text: 'Blade length' }} {...props} />
|
<NumberField label={{ text: 'Blade length' }} {...props} />
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -2,7 +2,7 @@ import { DataGridColumnConfig } from '@components/ui/data-grid/types';
|
|||||||
import { TurbineType } from 'src/api/wind';
|
import { TurbineType } from 'src/api/wind';
|
||||||
|
|
||||||
export const columns: DataGridColumnConfig<TurbineType>[] = [
|
export const columns: DataGridColumnConfig<TurbineType>[] = [
|
||||||
{ name: 'Name', getText: (t) => t.name, flex: '2' },
|
{ name: 'Name', getText: (t) => t.name, width: 2 },
|
||||||
{ name: 'Height', getText: (t) => String(t.height) },
|
{ name: 'Height', getText: (t) => String(t.height) },
|
||||||
{ name: 'Blade length', getText: (t) => String(t.bladeLength) },
|
{ name: 'Blade length', getText: (t) => String(t.bladeLength) },
|
||||||
];
|
];
|
||||||
|
@ -12,7 +12,6 @@
|
|||||||
.actions {
|
.actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
border: 1px solid var(--clr-border-100);
|
|
||||||
border-radius: 15px;
|
border-radius: 15px;
|
||||||
background-color: var(--clr-layer-200);
|
background-color: var(--clr-layer-200);
|
||||||
box-shadow: 0px 1px 2px var(--clr-shadow-100);
|
box-shadow: 0px 1px 2px var(--clr-shadow-100);
|
||||||
|
@ -15,6 +15,11 @@ export function DataGrid<T>({
|
|||||||
}: DataGridProps<T>) {
|
}: DataGridProps<T>) {
|
||||||
const [allItemsSelected, setAllItemsSelected] = useState<boolean>(false);
|
const [allItemsSelected, setAllItemsSelected] = useState<boolean>(false);
|
||||||
|
|
||||||
|
const columnsTemplate = useMemo(() => {
|
||||||
|
const main = columns.map((c) => `${c.width ?? 1}fr`).join(' ');
|
||||||
|
return `auto ${main}`;
|
||||||
|
}, []);
|
||||||
|
|
||||||
const selectedItemsMap = useMemo(() => {
|
const selectedItemsMap = useMemo(() => {
|
||||||
const map: Record<string, T> = {};
|
const map: Record<string, T> = {};
|
||||||
for (let i = 0; i < selectedItems.length; i += 1) {
|
for (let i = 0; i < selectedItems.length; i += 1) {
|
||||||
@ -35,7 +40,7 @@ export function DataGrid<T>({
|
|||||||
const handleItemSelect = (key: string, item: T) => {
|
const handleItemSelect = (key: string, item: T) => {
|
||||||
const selected = Boolean(selectedItemsMap[key]);
|
const selected = Boolean(selectedItemsMap[key]);
|
||||||
if (!multiselect) {
|
if (!multiselect) {
|
||||||
onItemsSelect(selected ? [] : [item]);
|
onItemsSelect?.(selected ? [] : [item]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
onItemsSelect?.(
|
onItemsSelect?.(
|
||||||
@ -52,6 +57,7 @@ export function DataGrid<T>({
|
|||||||
columns={columns}
|
columns={columns}
|
||||||
allItemsSelected={allItemsSelected}
|
allItemsSelected={allItemsSelected}
|
||||||
onSelectAllItems={handleSelectAllItems}
|
onSelectAllItems={handleSelectAllItems}
|
||||||
|
columnsTemplate={columnsTemplate}
|
||||||
/>
|
/>
|
||||||
{items.map((item) => {
|
{items.map((item) => {
|
||||||
const key = String(getItemKey(item));
|
const key = String(getItemKey(item));
|
||||||
@ -62,6 +68,7 @@ export function DataGrid<T>({
|
|||||||
selected={Boolean(selectedItemsMap[key])}
|
selected={Boolean(selectedItemsMap[key])}
|
||||||
onSelect={() => handleItemSelect(key, item)}
|
onSelect={() => handleItemSelect(key, item)}
|
||||||
key={getItemKey(item)}
|
key={getItemKey(item)}
|
||||||
|
columnsTemplate={columnsTemplate}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
@ -14,6 +14,7 @@ export function DataGridHeader<T>({
|
|||||||
columns,
|
columns,
|
||||||
allItemsSelected,
|
allItemsSelected,
|
||||||
onSelectAllItems,
|
onSelectAllItems,
|
||||||
|
columnsTemplate,
|
||||||
}: DataGridHeaderProps<T>) {
|
}: DataGridHeaderProps<T>) {
|
||||||
const [sort, setSort] = useState<DataGridSort>({ order: 'asc', column: '' });
|
const [sort, setSort] = useState<DataGridSort>({ order: 'asc', column: '' });
|
||||||
|
|
||||||
@ -30,7 +31,10 @@ export function DataGridHeader<T>({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<header className={styles.header}>
|
<header
|
||||||
|
className={styles.header}
|
||||||
|
style={{ gridTemplateColumns: columnsTemplate }}
|
||||||
|
>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={allItemsSelected}
|
checked={allItemsSelected}
|
||||||
onChange={onSelectAllItems}
|
onChange={onSelectAllItems}
|
||||||
@ -44,7 +48,6 @@ export function DataGridHeader<T>({
|
|||||||
});
|
});
|
||||||
return (
|
return (
|
||||||
<RawButton
|
<RawButton
|
||||||
style={{ flex: column.flex }}
|
|
||||||
className={cellClassName}
|
className={cellClassName}
|
||||||
key={column.name}
|
key={column.name}
|
||||||
onClick={() => handleSortButtonClick(column.name)}
|
onClick={() => handleSortButtonClick(column.name)}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
.header {
|
.header {
|
||||||
display: flex;
|
display: grid;
|
||||||
}
|
}
|
||||||
|
|
||||||
.checkboxLabel {
|
.checkboxLabel {
|
||||||
@ -13,7 +13,6 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
flex: 1;
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
border: solid 1px var(--clr-border-100);
|
border: solid 1px var(--clr-border-100);
|
||||||
|
@ -4,4 +4,5 @@ export type DataGridHeaderProps<T> = {
|
|||||||
columns: DataGridColumnConfig<T>[];
|
columns: DataGridColumnConfig<T>[];
|
||||||
allItemsSelected: boolean;
|
allItemsSelected: boolean;
|
||||||
onSelectAllItems: () => void;
|
onSelectAllItems: () => void;
|
||||||
|
columnsTemplate: string;
|
||||||
};
|
};
|
||||||
|
@ -10,20 +10,20 @@ export function DataGridRow<T>({
|
|||||||
columns,
|
columns,
|
||||||
selected,
|
selected,
|
||||||
onSelect,
|
onSelect,
|
||||||
|
columnsTemplate,
|
||||||
}: DataGridRowProps<T>) {
|
}: DataGridRowProps<T>) {
|
||||||
return (
|
return (
|
||||||
<div className={styles.row}>
|
<div
|
||||||
|
className={styles.row}
|
||||||
|
style={{ gridTemplateColumns: columnsTemplate }}
|
||||||
|
>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={selected}
|
checked={selected}
|
||||||
label={{ className: styles.checkboxLabel }}
|
label={{ className: styles.checkboxLabel }}
|
||||||
onChange={onSelect}
|
onChange={onSelect}
|
||||||
/>
|
/>
|
||||||
{columns.map((column) => (
|
{columns.map((column) => (
|
||||||
<div
|
<div className={styles.cell} key={column.name}>
|
||||||
className={styles.cell}
|
|
||||||
style={{ flex: column.flex }}
|
|
||||||
key={column.name}
|
|
||||||
>
|
|
||||||
<Span>{column.getText(object)}</Span>
|
<Span>{column.getText(object)}</Span>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
.row {
|
.row {
|
||||||
display: flex;
|
display: grid;
|
||||||
}
|
}
|
||||||
|
|
||||||
.checkboxLabel {
|
.checkboxLabel {
|
||||||
@ -11,7 +11,6 @@
|
|||||||
.cell {
|
.cell {
|
||||||
display: flex;
|
display: flex;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
flex: 1 0 0;
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
border: solid 1px var(--clr-border-100);
|
border: solid 1px var(--clr-border-100);
|
||||||
|
@ -5,4 +5,5 @@ export type DataGridRowProps<T> = {
|
|||||||
columns: DataGridColumnConfig<T>[];
|
columns: DataGridColumnConfig<T>[];
|
||||||
selected: boolean;
|
selected: boolean;
|
||||||
onSelect: () => void;
|
onSelect: () => void;
|
||||||
|
columnsTemplate: string;
|
||||||
};
|
};
|
||||||
|
@ -24,7 +24,7 @@ export function DataGridPreview() {
|
|||||||
|
|
||||||
const columns: DataGridColumnConfig<Cat>[] = [
|
const columns: DataGridColumnConfig<Cat>[] = [
|
||||||
{ name: 'Name', getText: (cat) => cat.name },
|
{ name: 'Name', getText: (cat) => cat.name },
|
||||||
{ name: 'Breed', getText: (cat) => cat.breed, scale: 2 },
|
{ name: 'Breed', getText: (cat) => cat.breed, width: 2 },
|
||||||
{ name: 'Age', getText: (cat) => cat.age },
|
{ name: 'Age', getText: (cat) => cat.age },
|
||||||
{ name: 'Color', getText: (cat) => cat.color },
|
{ name: 'Color', getText: (cat) => cat.color },
|
||||||
];
|
];
|
||||||
|
@ -4,7 +4,7 @@ export type DataGridColumnConfig<T> = {
|
|||||||
name: string;
|
name: string;
|
||||||
getText: (object: T) => string;
|
getText: (object: T) => string;
|
||||||
sortable?: boolean;
|
sortable?: boolean;
|
||||||
flex?: string;
|
width?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type DataGridSort = {
|
export type DataGridSort = {
|
||||||
|
@ -3,13 +3,14 @@ export { Button } from './button';
|
|||||||
export { Checkbox } from './checkbox';
|
export { Checkbox } from './checkbox';
|
||||||
export { CheckboxGroup } from './checkbox-group';
|
export { CheckboxGroup } from './checkbox-group';
|
||||||
export { DateInput } from './date-input';
|
export { DateInput } from './date-input';
|
||||||
|
export { Dialog } from './dialog';
|
||||||
export { FileUploader } from './file-uploader';
|
export { FileUploader } from './file-uploader';
|
||||||
export { Heading } from './heading';
|
export { Heading } from './heading';
|
||||||
export { IconButton } from './icon-button';
|
export { IconButton } from './icon-button';
|
||||||
export { ImageFileManager } from './image-file-manager';
|
export { ImageFileManager } from './image-file-manager';
|
||||||
export { LinkButton } from './link-button';
|
export { LinkButton } from './link-button';
|
||||||
export { Menu } from './menu';
|
export { Menu } from './menu';
|
||||||
export { Dialog } from './dialog';
|
export { NumberField } from './number-field';
|
||||||
export { NumberInput } from './number-input';
|
export { NumberInput } from './number-input';
|
||||||
export { Overlay } from './overlay';
|
export { Overlay } from './overlay';
|
||||||
export { Pagination } from './pagination';
|
export { Pagination } from './pagination';
|
||||||
|
28
front/src/components/ui/number-field/component.tsx
Normal file
28
front/src/components/ui/number-field/component.tsx
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import React, { ForwardedRef, forwardRef } from 'react';
|
||||||
|
|
||||||
|
import { Label, LabelProps } from '../label';
|
||||||
|
import { NumberInput } from '../number-input';
|
||||||
|
import { NumberFieldProps } from './types';
|
||||||
|
|
||||||
|
function NumberFieldInner(
|
||||||
|
{ scale, label = {}, required, ...props }: Omit<NumberFieldProps, 'ref'>,
|
||||||
|
ref: ForwardedRef<HTMLInputElement>,
|
||||||
|
) {
|
||||||
|
const labelProps: LabelProps = {
|
||||||
|
...label,
|
||||||
|
required: { value: required, ...label.required },
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<Label scale={scale} {...labelProps}>
|
||||||
|
<NumberInput
|
||||||
|
scale={scale}
|
||||||
|
ref={ref}
|
||||||
|
required={required}
|
||||||
|
{...props}
|
||||||
|
invalid={label.error !== undefined}
|
||||||
|
/>
|
||||||
|
</Label>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const NumberField = forwardRef(NumberFieldInner);
|
6
front/src/components/ui/number-field/types.ts
Normal file
6
front/src/components/ui/number-field/types.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import { LabelProps } from '../label/types';
|
||||||
|
import { NumberInputProps } from '../number-input/types';
|
||||||
|
|
||||||
|
export type NumberFieldProps = {
|
||||||
|
label?: LabelProps;
|
||||||
|
} & NumberInputProps;
|
@ -1,6 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { TextInput } from '../text-input';
|
import { Input } from '../input';
|
||||||
import { NumberInputProps } from './types';
|
import { NumberInputProps } from './types';
|
||||||
|
|
||||||
export function NumberInput({
|
export function NumberInput({
|
||||||
@ -34,5 +34,5 @@ export function NumberInput({
|
|||||||
onChange?.(num);
|
onChange?.(num);
|
||||||
};
|
};
|
||||||
|
|
||||||
return <TextInput value={value ?? ''} onChange={handleChange} {...props} />;
|
return <Input value={value ?? ''} onChange={handleChange} {...props} />;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
export { Header } from './header';
|
export { Header } from './header';
|
||||||
|
export { ParkTurbineTable } from './park-turbine-table';
|
||||||
|
export { ParkTurbines } from './park-turbines';
|
||||||
export { Sidebar } from './sidebar';
|
export { Sidebar } from './sidebar';
|
||||||
export { SignInForm } from './sign-in-form';
|
|
||||||
export { SignUpForm } from './sign-up-form';
|
|
||||||
export { ThemeSelect } from './theme-select';
|
export { ThemeSelect } from './theme-select';
|
||||||
|
35
front/src/components/ux/park-turbine-table/component.tsx
Normal file
35
front/src/components/ux/park-turbine-table/component.tsx
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import { ParkFormTurbine } from '@components/pages/park-page/types';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import { ParkTurbineTableHeader, ParkTurbineTableRow } from './components';
|
||||||
|
import { ParkTurbineTableProps } from './types';
|
||||||
|
|
||||||
|
export function ParkTurbineTable({
|
||||||
|
turbines,
|
||||||
|
onChange,
|
||||||
|
selectedIDs,
|
||||||
|
onSelect,
|
||||||
|
}: ParkTurbineTableProps) {
|
||||||
|
const handleRowChange = (turbine: ParkFormTurbine, index: number) => {
|
||||||
|
onChange(turbines.with(index, turbine));
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSelect = (turbineId: number) => {
|
||||||
|
onSelect({ ...selectedIDs, [turbineId]: !selectedIDs[turbineId] });
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<ParkTurbineTableHeader />
|
||||||
|
{turbines.map((turbine, index) => (
|
||||||
|
<ParkTurbineTableRow
|
||||||
|
turbine={turbine}
|
||||||
|
onChange={(t) => handleRowChange(t, index)}
|
||||||
|
selected={selectedIDs[turbine.id] ?? false}
|
||||||
|
onSelect={() => handleSelect(turbine.id)}
|
||||||
|
key={turbine.id}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
@ -0,0 +1,2 @@
|
|||||||
|
export * from './park-turbine-table-header';
|
||||||
|
export * from './park-turbine-table-row';
|
@ -0,0 +1,35 @@
|
|||||||
|
import { Checkbox } from '@components/ui/checkbox';
|
||||||
|
import { Span } from '@components/ui/span';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import styles from './styles.module.scss';
|
||||||
|
|
||||||
|
export function ParkTurbineTableHeader() {
|
||||||
|
return (
|
||||||
|
<header className={styles.header}>
|
||||||
|
<Checkbox
|
||||||
|
checked={false}
|
||||||
|
onChange={() => {}}
|
||||||
|
label={{ className: styles.checkboxLabel }}
|
||||||
|
/>
|
||||||
|
<Span color="t300" className={styles.cell}>
|
||||||
|
Id
|
||||||
|
</Span>
|
||||||
|
<Span color="t300" className={styles.cell}>
|
||||||
|
Name
|
||||||
|
</Span>
|
||||||
|
<Span color="t300" className={styles.cell}>
|
||||||
|
X
|
||||||
|
</Span>
|
||||||
|
<Span color="t300" className={styles.cell}>
|
||||||
|
Y
|
||||||
|
</Span>
|
||||||
|
<Span color="t300" className={styles.cell}>
|
||||||
|
Angle
|
||||||
|
</Span>
|
||||||
|
<Span color="t300" className={styles.cell}>
|
||||||
|
Comment
|
||||||
|
</Span>
|
||||||
|
</header>
|
||||||
|
);
|
||||||
|
}
|
@ -0,0 +1 @@
|
|||||||
|
export * from './component';
|
@ -0,0 +1,26 @@
|
|||||||
|
.header {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: auto 1fr 1fr 1fr 1fr 1fr 3fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkboxLabel {
|
||||||
|
padding: 10px;
|
||||||
|
border: solid 1px var(--clr-border-100);
|
||||||
|
background-color: var(--clr-layer-300);
|
||||||
|
border-top-left-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cell {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
overflow: hidden;
|
||||||
|
align-items: center;
|
||||||
|
padding: 10px;
|
||||||
|
border: solid 1px var(--clr-border-100);
|
||||||
|
background-color: var(--clr-layer-300);
|
||||||
|
transition: all var(--td-100) ease-in-out;
|
||||||
|
|
||||||
|
&:last-of-type {
|
||||||
|
border-top-right-radius: 10px;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,52 @@
|
|||||||
|
import { NumberInput } from '@components/ui';
|
||||||
|
import { Checkbox } from '@components/ui/checkbox';
|
||||||
|
import { Input } from '@components/ui/input';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import styles from './styles.module.scss';
|
||||||
|
import { ParkTurbineTableRowProps } from './types';
|
||||||
|
|
||||||
|
export function ParkTurbineTableRow({
|
||||||
|
turbine,
|
||||||
|
onChange,
|
||||||
|
selected,
|
||||||
|
onSelect,
|
||||||
|
}: ParkTurbineTableRowProps) {
|
||||||
|
return (
|
||||||
|
<div className={styles.row}>
|
||||||
|
<Checkbox
|
||||||
|
checked={selected}
|
||||||
|
onChange={onSelect}
|
||||||
|
label={{ className: styles.checkboxLabel }}
|
||||||
|
/>
|
||||||
|
<Input value={turbine.id} wrapper={{ className: styles.cell }} disabled />
|
||||||
|
<Input
|
||||||
|
value={turbine.name}
|
||||||
|
wrapper={{ className: styles.cell }}
|
||||||
|
disabled
|
||||||
|
/>
|
||||||
|
<NumberInput
|
||||||
|
value={String(turbine.xOffset)}
|
||||||
|
wrapper={{ className: styles.cell }}
|
||||||
|
onChange={(value) => onChange({ ...turbine, xOffset: value })}
|
||||||
|
/>
|
||||||
|
<NumberInput
|
||||||
|
value={String(turbine.yOffset)}
|
||||||
|
wrapper={{ className: styles.cell }}
|
||||||
|
onChange={(value) => onChange({ ...turbine, yOffset: value })}
|
||||||
|
/>
|
||||||
|
<NumberInput
|
||||||
|
value={String(turbine.angle)}
|
||||||
|
wrapper={{ className: styles.cell }}
|
||||||
|
onChange={(value) => onChange({ ...turbine, angle: value })}
|
||||||
|
/>
|
||||||
|
<Input
|
||||||
|
value={turbine.comment}
|
||||||
|
wrapper={{ className: styles.cell }}
|
||||||
|
onChange={(event) =>
|
||||||
|
onChange({ ...turbine, comment: event.target.value })
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
@ -0,0 +1 @@
|
|||||||
|
export * from './component';
|
@ -0,0 +1,16 @@
|
|||||||
|
.row {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: auto 1fr 1fr 1fr 1fr 1fr 3fr;
|
||||||
|
|
||||||
|
.cell {
|
||||||
|
border: solid 1px var(--clr-border-100);
|
||||||
|
border-radius: 0;
|
||||||
|
background-color: var(--clr-layer-200);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkboxLabel {
|
||||||
|
padding: 10px;
|
||||||
|
border: solid 1px var(--clr-border-100);
|
||||||
|
background-color: var(--clr-layer-200);
|
||||||
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
import { ParkFormTurbine } from '@components/pages/park-page/types';
|
||||||
|
|
||||||
|
export type ParkTurbineTableRowProps = {
|
||||||
|
turbine: ParkFormTurbine;
|
||||||
|
onChange: (turbine: ParkFormTurbine) => void;
|
||||||
|
selected: boolean;
|
||||||
|
onSelect: () => void;
|
||||||
|
};
|
1
front/src/components/ux/park-turbine-table/index.tsx
Normal file
1
front/src/components/ux/park-turbine-table/index.tsx
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from './component';
|
8
front/src/components/ux/park-turbine-table/types.ts
Normal file
8
front/src/components/ux/park-turbine-table/types.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { ParkFormTurbine } from '@components/pages/park-page/types';
|
||||||
|
|
||||||
|
export type ParkTurbineTableProps = {
|
||||||
|
turbines: ParkFormTurbine[];
|
||||||
|
onChange: (turbines: ParkFormTurbine[]) => void;
|
||||||
|
selectedIDs: Record<number, boolean>;
|
||||||
|
onSelect: (selected: Record<number, boolean>) => void;
|
||||||
|
};
|
87
front/src/components/ux/park-turbines/component.tsx
Normal file
87
front/src/components/ux/park-turbines/component.tsx
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
import { getTurbineTypes, TurbineType } from '@api/wind';
|
||||||
|
import { ParkFormTurbine } from '@components/pages/park-page/types';
|
||||||
|
import { Autocomplete, Button } from '@components/ui';
|
||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
import { ParkTurbineTable } from '../park-turbine-table';
|
||||||
|
import styles from './styles.module.scss';
|
||||||
|
import { ParkTurbinesProps } from './types';
|
||||||
|
|
||||||
|
export function ParkTurbines({
|
||||||
|
savedTurbines,
|
||||||
|
value = [],
|
||||||
|
onChange,
|
||||||
|
}: ParkTurbinesProps) {
|
||||||
|
const [turbineTypes, setTurbineTypes] = useState<TurbineType[]>([]);
|
||||||
|
const [turbineType, setTurbineType] = useState<TurbineType>(null);
|
||||||
|
const [selectedIDs, setSelectedIDs] = useState<Record<number, boolean>>({});
|
||||||
|
|
||||||
|
const fetchTurbineTypes = async () => {
|
||||||
|
const res = await getTurbineTypes();
|
||||||
|
setTurbineTypes(res.data ?? []);
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetchTurbineTypes();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleAddButtonClick = () => {
|
||||||
|
if (
|
||||||
|
savedTurbines.find((t) => t.id === turbineType.id) ||
|
||||||
|
value.find((t) => t.id === turbineType.id)
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
onChange([
|
||||||
|
...value,
|
||||||
|
{
|
||||||
|
id: turbineType.id,
|
||||||
|
name: turbineType.name,
|
||||||
|
xOffset: '',
|
||||||
|
yOffset: '',
|
||||||
|
angle: '',
|
||||||
|
comment: '',
|
||||||
|
new: true,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDeleteButtonClick = () => {
|
||||||
|
const newValue: ParkFormTurbine[] = [];
|
||||||
|
value.forEach((turbine) => {
|
||||||
|
if (!selectedIDs[turbine.id]) {
|
||||||
|
newValue.push(turbine);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (savedTurbines.find((t) => t.id === turbine.id)) {
|
||||||
|
newValue.push({ ...turbine, delete: true });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
onChange(newValue);
|
||||||
|
setSelectedIDs({});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.parkTurbines}>
|
||||||
|
<div className={styles.actions}>
|
||||||
|
<Autocomplete
|
||||||
|
options={turbineTypes ?? []}
|
||||||
|
getOptionKey={({ id }) => id}
|
||||||
|
getOptionLabel={({ name }) => name}
|
||||||
|
onChange={(t) => setTurbineType(t)}
|
||||||
|
value={turbineType}
|
||||||
|
/>
|
||||||
|
<Button onClick={handleAddButtonClick}>Add</Button>
|
||||||
|
<Button variant="secondary" onClick={handleDeleteButtonClick}>
|
||||||
|
Delete
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<ParkTurbineTable
|
||||||
|
turbines={value.filter((t) => !t.delete)}
|
||||||
|
onChange={onChange}
|
||||||
|
selectedIDs={selectedIDs}
|
||||||
|
onSelect={setSelectedIDs}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
1
front/src/components/ux/park-turbines/index.ts
Normal file
1
front/src/components/ux/park-turbines/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from './component';
|
14
front/src/components/ux/park-turbines/styles.module.scss
Normal file
14
front/src/components/ux/park-turbines/styles.module.scss
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
.parkTurbines {
|
||||||
|
display: grid;
|
||||||
|
gap: 10px;
|
||||||
|
grid-template-rows: auto 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions {
|
||||||
|
display: flex;
|
||||||
|
padding: 10px;
|
||||||
|
border-radius: 15px;
|
||||||
|
background-color: var(--clr-layer-200);
|
||||||
|
box-shadow: 0px 1px 2px var(--clr-shadow-100);
|
||||||
|
gap: 10px;
|
||||||
|
}
|
8
front/src/components/ux/park-turbines/types.ts
Normal file
8
front/src/components/ux/park-turbines/types.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { ParkTurbine } from '@api/wind';
|
||||||
|
import { ParkFormTurbine } from '@components/pages/park-page/types';
|
||||||
|
|
||||||
|
export type ParkTurbinesProps = {
|
||||||
|
savedTurbines: ParkTurbine[];
|
||||||
|
value?: ParkFormTurbine[];
|
||||||
|
onChange?: (value: ParkFormTurbine[]) => void;
|
||||||
|
};
|
@ -1,40 +0,0 @@
|
|||||||
import {
|
|
||||||
Button,
|
|
||||||
Heading,
|
|
||||||
LinkButton,
|
|
||||||
PasswordInput,
|
|
||||||
TextInput,
|
|
||||||
} from '@components/ui';
|
|
||||||
import { useForm } from '@utils/form';
|
|
||||||
import { ROUTES } from '@utils/route';
|
|
||||||
import clsx from 'clsx';
|
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
import styles from './styles.module.scss';
|
|
||||||
import { SignInFormProps, SignInFormStore } from './types';
|
|
||||||
|
|
||||||
export function SignInForm({ className, ...props }: SignInFormProps) {
|
|
||||||
const { register, getValues } = useForm<SignInFormStore>({});
|
|
||||||
const classNames = clsx(className, styles.form);
|
|
||||||
|
|
||||||
const handleSubmit = (event: React.FormEvent) => {
|
|
||||||
event.preventDefault();
|
|
||||||
console.log(getValues());
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<form onSubmit={handleSubmit} className={classNames} {...props}>
|
|
||||||
<Heading tag="h1" className={styles.heading}>
|
|
||||||
Sign in
|
|
||||||
</Heading>
|
|
||||||
<div className={styles.inputBox}>
|
|
||||||
<TextInput {...register('email')} label={{ text: 'Email' }} />
|
|
||||||
<PasswordInput {...register('password')} label={{ text: 'Password' }} />
|
|
||||||
</div>
|
|
||||||
<div className={styles.buttonBox}>
|
|
||||||
<Button type="submit">Sign in</Button>
|
|
||||||
<LinkButton href={ROUTES.signUp.path}>Not a member?</LinkButton>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
);
|
|
||||||
}
|
|
@ -1 +0,0 @@
|
|||||||
export { SignInForm } from './component';
|
|
@ -1,26 +0,0 @@
|
|||||||
.form {
|
|
||||||
display: grid;
|
|
||||||
padding: 40px 20px 20px;
|
|
||||||
border-radius: 15px;
|
|
||||||
background-color: var(--clr-layer-200);
|
|
||||||
box-shadow: 0px 1px 2px var(--clr-shadow-100);
|
|
||||||
gap: 30px;
|
|
||||||
|
|
||||||
& > * {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.heading {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.inputBox {
|
|
||||||
display: grid;
|
|
||||||
gap: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.buttonBox {
|
|
||||||
display: grid;
|
|
||||||
gap: 10px;
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
import { ComponentProps } from 'react';
|
|
||||||
|
|
||||||
export type SignInFormStore = {
|
|
||||||
email: string;
|
|
||||||
password: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type SignInFormProps = {} & ComponentProps<'form'>;
|
|
@ -1,40 +0,0 @@
|
|||||||
import {
|
|
||||||
Button,
|
|
||||||
Heading,
|
|
||||||
LinkButton,
|
|
||||||
PasswordInput,
|
|
||||||
TextInput,
|
|
||||||
} from '@components/ui';
|
|
||||||
import { useForm } from '@utils/form';
|
|
||||||
import { ROUTES } from '@utils/route';
|
|
||||||
import clsx from 'clsx';
|
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
import styles from './styles.module.scss';
|
|
||||||
import { SignUpFormProps, SignUpFormStore } from './types';
|
|
||||||
|
|
||||||
export function SignUpForm({ className, ...props }: SignUpFormProps) {
|
|
||||||
const { register, getValues } = useForm<SignUpFormStore>({});
|
|
||||||
const classNames = clsx(className, styles.form);
|
|
||||||
|
|
||||||
const handleSubmit = (event: React.FormEvent) => {
|
|
||||||
event.preventDefault();
|
|
||||||
console.log(getValues());
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<form onSubmit={handleSubmit} className={classNames} {...props}>
|
|
||||||
<Heading tag="h1" className={styles.heading}>
|
|
||||||
Sign up
|
|
||||||
</Heading>
|
|
||||||
<div className={styles.inputBox}>
|
|
||||||
<TextInput {...register('email')} label={{ text: 'Email' }} />
|
|
||||||
<PasswordInput {...register('password')} label={{ text: 'Password' }} />
|
|
||||||
</div>
|
|
||||||
<div className={styles.buttonBox}>
|
|
||||||
<Button type="submit">Sign up</Button>
|
|
||||||
<LinkButton href={ROUTES.signIn.path}>Already a member?</LinkButton>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
);
|
|
||||||
}
|
|
@ -1 +0,0 @@
|
|||||||
export { SignUpForm } from './component';
|
|
@ -1,26 +0,0 @@
|
|||||||
.form {
|
|
||||||
display: grid;
|
|
||||||
padding: 40px 20px 20px;
|
|
||||||
border-radius: 15px;
|
|
||||||
background-color: var(--clr-layer-200);
|
|
||||||
box-shadow: 0px 1px 2px var(--clr-shadow-100);
|
|
||||||
gap: 30px;
|
|
||||||
|
|
||||||
& > * {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.heading {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.inputBox {
|
|
||||||
display: grid;
|
|
||||||
gap: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.buttonBox {
|
|
||||||
display: grid;
|
|
||||||
gap: 10px;
|
|
||||||
}
|
|
@ -1,9 +0,0 @@
|
|||||||
import { ComponentProps } from 'react';
|
|
||||||
|
|
||||||
export type SignUpFormStore = {
|
|
||||||
email: string;
|
|
||||||
password: string;
|
|
||||||
passwordRepeat: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type SignUpFormProps = {} & ComponentProps<'form'>;
|
|
@ -143,12 +143,12 @@ class WindParkTurbineRepository:
|
|||||||
return db_park_turbine
|
return db_park_turbine
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get(db: Session, park_turbine_id: int):
|
def get(db: Session, park_id: int, turbine_id: int):
|
||||||
return db.query(WindParkTurbine).filter(WindParkTurbine.turbine_id == park_turbine_id).first()
|
return db.query(WindParkTurbine).filter(WindParkTurbine.wind_park_id == park_id, WindParkTurbine.turbine_id == turbine_id).first()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def update(db: Session, park_turbine_id: int, park_turbine: WindParkTurbineCreate):
|
def update(db: Session, park_id: int, turbine_id: int, park_turbine: WindParkTurbineCreate):
|
||||||
db_park_turbine = db.query(WindParkTurbine).filter(WindParkTurbine.turbine_id == park_turbine_id).first()
|
db_park_turbine = db.query(WindParkTurbine).filter(WindParkTurbine.wind_park_id == park_id, WindParkTurbine.turbine_id == turbine_id).first()
|
||||||
if db_park_turbine:
|
if db_park_turbine:
|
||||||
for key, value in park_turbine.dict().items():
|
for key, value in park_turbine.dict().items():
|
||||||
setattr(db_park_turbine, key, value)
|
setattr(db_park_turbine, key, value)
|
||||||
|
@ -77,17 +77,17 @@ async def create_park_turbine(park_turbine: WindParkTurbineCreate, db: Session =
|
|||||||
return WindParkTurbineRepository.create(db=db, park_turbine=park_turbine)
|
return WindParkTurbineRepository.create(db=db, park_turbine=park_turbine)
|
||||||
|
|
||||||
|
|
||||||
@router.get("/park_turbine/{park_turbine_id}", response_model=WindParkTurbineResponse)
|
@router.get("/park_turbine/{park_id}/{turbine_id}", response_model=WindParkTurbineResponse)
|
||||||
async def read_park_turbine(park_turbine_id: int, db: Session = Depends(get_db)):
|
async def read_park_turbine(park_id: int, turbine_id: int, db: Session = Depends(get_db)):
|
||||||
park_turbine = WindParkTurbineRepository.get(db=db, park_turbine_id=park_turbine_id)
|
park_turbine = WindParkTurbineRepository.get(db=db, park_id=park_id, turbine_id=turbine_id)
|
||||||
if park_turbine is None:
|
if park_turbine is None:
|
||||||
raise HTTPException(status_code=404, detail="Park Turbine not found")
|
raise HTTPException(status_code=404, detail="Park Turbine not found")
|
||||||
return park_turbine
|
return park_turbine
|
||||||
|
|
||||||
|
|
||||||
@router.put("/park_turbine/{park_turbine_id}", response_model=WindParkTurbineResponse)
|
@router.put("/park_turbine/{park_id}/{turbine_id}", response_model=WindParkTurbineResponse)
|
||||||
async def update_park_turbine(park_turbine_id: int, park_turbine: WindParkTurbineCreate, db: Session = Depends(get_db)):
|
async def update_park_turbine(park_id: int, turbine_id: int, park_turbine: WindParkTurbineCreate, db: Session = Depends(get_db)):
|
||||||
updated_park_turbine = WindParkTurbineRepository.update(db=db, park_turbine_id=park_turbine_id,
|
updated_park_turbine = WindParkTurbineRepository.update(db=db, park_id=park_id, turbine_id=turbine_id,
|
||||||
park_turbine=park_turbine)
|
park_turbine=park_turbine)
|
||||||
if updated_park_turbine is None:
|
if updated_park_turbine is None:
|
||||||
raise HTTPException(status_code=404, detail="Park Turbine not found")
|
raise HTTPException(status_code=404, detail="Park Turbine not found")
|
||||||
|
Loading…
Reference in New Issue
Block a user