-
Wind App
+
+
Wind
-
- {links.map((link) => (
-
- {link.title}
-
- ))}
-
+
);
}
+
+export const Sidebar = forwardRef(SidebarInner);
diff --git a/front/src/components/ux/sidebar/constants.ts b/front/src/components/ux/sidebar/constants.ts
deleted file mode 100644
index ee7c3c6..0000000
--- a/front/src/components/ux/sidebar/constants.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-import { ROUTES } from '@utils/route';
-
-import { SidebarLink } from './types';
-
-export const links: SidebarLink[] = [
- { url: ROUTES.turbineTypes.path, title: ROUTES.turbineTypes.title },
- { url: ROUTES.parks.path, title: ROUTES.parks.title },
-];
diff --git a/front/src/components/ux/sidebar/styles.module.scss b/front/src/components/ux/sidebar/styles.module.scss
index 5d9099f..d2c10b4 100644
--- a/front/src/components/ux/sidebar/styles.module.scss
+++ b/front/src/components/ux/sidebar/styles.module.scss
@@ -1,20 +1,9 @@
.sidebar {
display: flex;
flex-direction: column;
- padding: 20px;
+ padding: 30px 20px;
border-right: 1px solid var(--clr-border-100);
background-color: var(--clr-layer-200);
box-shadow: 0px 1px 2px var(--clr-shadow-100);
gap: 20px;
}
-
-.links {
- display: flex;
- flex-direction: column;
- gap: 10px;
-}
-
-.link {
- color: var(--clr-text-200);
- text-decoration: none;
-}
diff --git a/front/src/components/ux/sidebar/types.ts b/front/src/components/ux/sidebar/types.ts
index 92522e4..d89a14e 100644
--- a/front/src/components/ux/sidebar/types.ts
+++ b/front/src/components/ux/sidebar/types.ts
@@ -1,4 +1,3 @@
-export type SidebarLink = {
- url: string;
- title: string;
-};
+import { ComponentPropsWithoutRef } from 'react';
+
+export type SidebarProps = ComponentPropsWithoutRef<'div'>;
diff --git a/front/src/components/ux/sign-in-form/component.tsx b/front/src/components/ux/sign-in-form/component.tsx
new file mode 100644
index 0000000..5299ab1
--- /dev/null
+++ b/front/src/components/ux/sign-in-form/component.tsx
@@ -0,0 +1,40 @@
+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
({});
+ const classNames = clsx(className, styles.form);
+
+ const handleSubmit = (event: React.FormEvent) => {
+ event.preventDefault();
+ console.log(getValues());
+ };
+
+ return (
+
+ );
+}
diff --git a/front/src/components/ux/sign-in-form/index.ts b/front/src/components/ux/sign-in-form/index.ts
new file mode 100644
index 0000000..fba6544
--- /dev/null
+++ b/front/src/components/ux/sign-in-form/index.ts
@@ -0,0 +1 @@
+export { SignInForm } from './component';
diff --git a/front/src/components/ux/turbine-type-modal/styles.module.scss b/front/src/components/ux/sign-in-form/styles.module.scss
similarity index 62%
rename from front/src/components/ux/turbine-type-modal/styles.module.scss
rename to front/src/components/ux/sign-in-form/styles.module.scss
index 6c5db31..b996084 100644
--- a/front/src/components/ux/turbine-type-modal/styles.module.scss
+++ b/front/src/components/ux/sign-in-form/styles.module.scss
@@ -1,12 +1,10 @@
.form {
display: grid;
- padding: 40px 20px;
- border-radius: 10px;
+ padding: 40px 20px 20px;
+ border-radius: 15px;
background-color: var(--clr-layer-200);
box-shadow: 0px 1px 2px var(--clr-shadow-100);
- gap: 40px;
- grid-area: form;
- grid-template-rows: auto 1fr auto;
+ gap: 30px;
& > * {
width: 100%;
@@ -23,7 +21,6 @@
}
.buttonBox {
- display: flex;
- justify-content: end;
+ display: grid;
gap: 10px;
}
diff --git a/front/src/components/ux/sign-in-form/types.ts b/front/src/components/ux/sign-in-form/types.ts
new file mode 100644
index 0000000..caa4093
--- /dev/null
+++ b/front/src/components/ux/sign-in-form/types.ts
@@ -0,0 +1,8 @@
+import { ComponentProps } from 'react';
+
+export type SignInFormStore = {
+ email: string;
+ password: string;
+};
+
+export type SignInFormProps = {} & ComponentProps<'form'>;
diff --git a/front/src/components/ux/sign-up-form/component.tsx b/front/src/components/ux/sign-up-form/component.tsx
new file mode 100644
index 0000000..caeb375
--- /dev/null
+++ b/front/src/components/ux/sign-up-form/component.tsx
@@ -0,0 +1,40 @@
+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({});
+ const classNames = clsx(className, styles.form);
+
+ const handleSubmit = (event: React.FormEvent) => {
+ event.preventDefault();
+ console.log(getValues());
+ };
+
+ return (
+
+ );
+}
diff --git a/front/src/components/ux/sign-up-form/index.ts b/front/src/components/ux/sign-up-form/index.ts
new file mode 100644
index 0000000..455d405
--- /dev/null
+++ b/front/src/components/ux/sign-up-form/index.ts
@@ -0,0 +1 @@
+export { SignUpForm } from './component';
diff --git a/front/src/components/ux/sign-up-form/styles.module.scss b/front/src/components/ux/sign-up-form/styles.module.scss
new file mode 100644
index 0000000..b996084
--- /dev/null
+++ b/front/src/components/ux/sign-up-form/styles.module.scss
@@ -0,0 +1,26 @@
+.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;
+}
diff --git a/front/src/components/ux/sign-up-form/types.ts b/front/src/components/ux/sign-up-form/types.ts
new file mode 100644
index 0000000..e089c14
--- /dev/null
+++ b/front/src/components/ux/sign-up-form/types.ts
@@ -0,0 +1,9 @@
+import { ComponentProps } from 'react';
+
+export type SignUpFormStore = {
+ email: string;
+ password: string;
+ passwordRepeat: string;
+};
+
+export type SignUpFormProps = {} & ComponentProps<'form'>;
diff --git a/front/src/components/ux/theme-select/component.tsx b/front/src/components/ux/theme-select/component.tsx
index 8b34ebe..f656983 100644
--- a/front/src/components/ux/theme-select/component.tsx
+++ b/front/src/components/ux/theme-select/component.tsx
@@ -17,7 +17,7 @@ export function ThemeSelect() {
getOptionKey={(t) => t.key}
getOptionLabel={(t) => t.name}
onChange={handleChange}
- label={{ text: 'Theme', position: 'left' }}
+ label={{ text: 'Theme' }}
id="theme"
/>
);
diff --git a/front/src/components/ux/theme-select/index.ts b/front/src/components/ux/theme-select/index.ts
new file mode 100644
index 0000000..bb82484
--- /dev/null
+++ b/front/src/components/ux/theme-select/index.ts
@@ -0,0 +1 @@
+export * from './component';
diff --git a/front/src/components/ux/theme-select/styles.module.scss b/front/src/components/ux/theme-select/styles.module.scss
deleted file mode 100644
index c9bbd3f..0000000
--- a/front/src/components/ux/theme-select/styles.module.scss
+++ /dev/null
@@ -1,4 +0,0 @@
-.select {
- padding: 20px;
- background-color: var(--clr-layer-200);
-}
diff --git a/front/src/components/ux/turbine-type-modal/component.tsx b/front/src/components/ux/turbine-type-modal/component.tsx
deleted file mode 100644
index 2d246d6..0000000
--- a/front/src/components/ux/turbine-type-modal/component.tsx
+++ /dev/null
@@ -1,79 +0,0 @@
-import {
- Button,
- Heading,
- NumberInput,
- Overlay,
- TextInput,
-} from '@components/ui';
-import { Controller, useForm } from '@utils/form';
-import React, { useEffect, useState } from 'react';
-import { createTurbineTypes, editTurbineTypes } from 'src/api/wind';
-
-import styles from './styles.module.scss';
-import { TurbineTypeFormValues, TurbineTypeModalProps } from './types';
-import { turbineTypeToFormValues } from './utils';
-
-export function TurbineTypeModal({
- turbineType,
- open,
- onClose,
- onSuccess,
-}: TurbineTypeModalProps) {
- const [pending, setPending] = useState(false);
- const isEdit = turbineType !== null;
-
- const { register, control, getValues, reset } =
- useForm({
- initialValues: {},
- });
-
- useEffect(() => {
- reset(turbineType ? turbineTypeToFormValues(turbineType) : {});
- }, [turbineType]);
-
- const handleSubmit = async (event: React.FormEvent) => {
- event.preventDefault();
- setPending(true);
- if (isEdit) {
- await editTurbineTypes(getValues(), turbineType.id);
- } else {
- await createTurbineTypes(getValues());
- }
- setPending(false);
- onClose();
- onSuccess();
- };
-
- return (
-
-
-
- );
-}
diff --git a/front/src/components/ux/turbine-type-modal/index.ts b/front/src/components/ux/turbine-type-modal/index.ts
deleted file mode 100644
index 0bfef12..0000000
--- a/front/src/components/ux/turbine-type-modal/index.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-export * from './component';
-export { type TurbineTypeFormValues } from './types';
diff --git a/front/src/components/ux/turbine-type-modal/types.ts b/front/src/components/ux/turbine-type-modal/types.ts
deleted file mode 100644
index 82e4d6f..0000000
--- a/front/src/components/ux/turbine-type-modal/types.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-import { TurbineType } from 'src/api/wind';
-
-export type TurbineTypeFormValues = {
- name: string;
- height: string;
- bladeLength: string;
-};
-
-export type TurbineTypeModalProps = {
- turbineType: TurbineType;
- open: boolean;
- onClose: () => void;
- onSuccess: () => void;
-};
diff --git a/front/src/index.tsx b/front/src/index.tsx
index 47025bc..64ba82b 100644
--- a/front/src/index.tsx
+++ b/front/src/index.tsx
@@ -1,7 +1,7 @@
import React from 'react';
import { createRoot } from 'react-dom/client';
-import App from './components/app';
+import { App } from './components/app';
const rootElement = document.getElementById('root');
const root = createRoot(rootElement);
diff --git a/front/src/utils/device/index.ts b/front/src/utils/device/index.ts
new file mode 100644
index 0000000..04bca77
--- /dev/null
+++ b/front/src/utils/device/index.ts
@@ -0,0 +1 @@
+export * from './utils';
diff --git a/front/src/utils/device/types.ts b/front/src/utils/device/types.ts
new file mode 100644
index 0000000..c4946dc
--- /dev/null
+++ b/front/src/utils/device/types.ts
@@ -0,0 +1 @@
+export type DeviceType = 'desktop' | 'mobile';
diff --git a/front/src/utils/device/utils.ts b/front/src/utils/device/utils.ts
new file mode 100644
index 0000000..91c4b93
--- /dev/null
+++ b/front/src/utils/device/utils.ts
@@ -0,0 +1,28 @@
+import { useLayoutEffect, useState } from 'react';
+
+import { DeviceType } from './types';
+
+const defineDeviceType = (): DeviceType => {
+ const width = window.innerWidth;
+ if (width <= 800) {
+ return 'mobile';
+ }
+ return 'desktop';
+};
+
+export const useDeviceType = () => {
+ const [deviceType, setDeviceType] = useState(defineDeviceType());
+
+ const handleResize = () => {
+ setDeviceType(defineDeviceType());
+ };
+
+ useLayoutEffect(() => {
+ window.addEventListener('resize', handleResize);
+ return () => {
+ window.removeEventListener('resize', handleResize);
+ };
+ }, []);
+
+ return deviceType;
+};
diff --git a/front/src/utils/form/controller/types.ts b/front/src/utils/form/controller/types.ts
index cd1bcd5..b000f59 100644
--- a/front/src/utils/form/controller/types.ts
+++ b/front/src/utils/form/controller/types.ts
@@ -1,3 +1,5 @@
+import { ReactNode } from 'react';
+
export type ControllerRenderParams = {
name: string;
value: T;
@@ -9,5 +11,5 @@ export type ControllerProps = {
hash: number;
value: T;
onChange: (value: T) => void;
- render: (params: ControllerRenderParams) => React.ReactNode;
+ render: (params: ControllerRenderParams) => ReactNode;
};
diff --git a/front/src/utils/form/types.ts b/front/src/utils/form/types.ts
index 684b874..820a6f0 100644
--- a/front/src/utils/form/types.ts
+++ b/front/src/utils/form/types.ts
@@ -1,3 +1,5 @@
+import { MutableRefObject } from 'react';
+
export type FormValues = Partial;
export type FormStore = {
@@ -5,4 +7,4 @@ export type FormStore = {
hash: number;
};
-export type FormStoreRef = React.MutableRefObject>;
+export type FormStoreRef = MutableRefObject>;
diff --git a/front/src/utils/miss-click/index.tsx b/front/src/utils/miss-click/index.tsx
index 4b9f7d3..8d7294f 100644
--- a/front/src/utils/miss-click/index.tsx
+++ b/front/src/utils/miss-click/index.tsx
@@ -1,29 +1,41 @@
import { useEffect } from 'react';
-export const useMissClick = (
- ignore: React.MutableRefObject[],
- callback: () => void,
- visible: boolean,
-) => {
- useEffect(() => {
- if (!visible) {
+import { UseMissClickParams } from './types';
+
+export const useMissClick = ({
+ callback,
+ enabled,
+ blacklist,
+ whitelist,
+}: UseMissClickParams) => {
+ const handleClick = (event: MouseEvent) => {
+ const { target } = event;
+ if (!(target instanceof Element)) {
return;
}
- const listener = (event: MouseEvent) => {
- const { target } = event;
- if (!(target instanceof Element)) {
+ for (let i = 0; i < whitelist.length; i += 1) {
+ if (whitelist[i]?.current?.contains(target)) {
return;
}
- for (let i = 0; i < ignore.length; i += 1) {
- if (ignore[i].current?.contains(target)) {
- return;
- }
- }
+ }
+ if (!blacklist) {
callback();
- };
- window.addEventListener('click', listener);
+ return;
+ }
+ for (let i = 0; i < blacklist.length; i += 1) {
+ if (blacklist[i]?.current?.contains(target)) {
+ callback();
+ }
+ }
+ };
+
+ useEffect(() => {
+ if (!enabled) {
+ return;
+ }
+ window.addEventListener('click', handleClick);
return () => {
- window.removeEventListener('click', listener);
+ window.removeEventListener('click', handleClick);
};
- }, [visible]);
+ }, [enabled]);
};
diff --git a/front/src/utils/miss-click/types.ts b/front/src/utils/miss-click/types.ts
new file mode 100644
index 0000000..f2e7d3f
--- /dev/null
+++ b/front/src/utils/miss-click/types.ts
@@ -0,0 +1,8 @@
+import { MutableRefObject } from 'react';
+
+export type UseMissClickParams = {
+ callback: () => void;
+ enabled: boolean;
+ whitelist: MutableRefObject[];
+ blacklist?: MutableRefObject[];
+};
diff --git a/front/src/utils/route/constants.ts b/front/src/utils/route/constants.ts
index 7922590..c5b2deb 100644
--- a/front/src/utils/route/constants.ts
+++ b/front/src/utils/route/constants.ts
@@ -3,8 +3,10 @@ import { arrayToObject } from '@utils/array';
import { AppRoute, AppRouteName } from './types';
export const ROUTES: Record = {
- turbineTypes: { path: 'turbine-types', title: 'Turbine types' },
- parks: { path: 'parks', title: 'Parks' },
+ turbineTypes: { path: '/turbine-types', title: 'Turbine Types' },
+ turbineType: { path: '/turbine-types/:id', title: 'Turbine Type' },
+ parks: { path: '/parks', title: 'Parks' },
+ park: { path: '/parks/:id', title: 'Park' },
};
export const routeArray = Object.values(ROUTES);
diff --git a/front/src/utils/route/types.ts b/front/src/utils/route/types.ts
index 9f3c9cb..d346794 100644
--- a/front/src/utils/route/types.ts
+++ b/front/src/utils/route/types.ts
@@ -1,4 +1,4 @@
-export type AppRouteName = 'turbineTypes' | 'parks';
+export type AppRouteName = 'turbineTypes' | 'turbineType' | 'parks' | 'park';
export type AppRoute = {
path: string;
diff --git a/front/tsconfig.json b/front/tsconfig.json
index a57cb58..7805a34 100644
--- a/front/tsconfig.json
+++ b/front/tsconfig.json
@@ -12,6 +12,7 @@
"@storage/*": ["src/utils/storage/*"],
"@store/*": ["src/store/*"],
"@utils/*": ["src/utils/*"],
+ "@api/*": ["src/api/*"],
},
"plugins": [
{