prod
This commit is contained in:
commit
9e8fd11cca
|
@ -317,12 +317,12 @@
|
|||
}
|
||||
],
|
||||
"wagonTypeList": [
|
||||
"HalfCarriage",
|
||||
"Platform",
|
||||
"CoveredCarriage",
|
||||
"UsoPlatform",
|
||||
"Hopper",
|
||||
"Tank"
|
||||
["HalfCarriage","Полувагон"],
|
||||
["Platform","Платформа"],
|
||||
["CoveredCarriage","Крытый вагон"],
|
||||
["UsoPlatform","Платформа УСО"],
|
||||
["Hopper","Хоппер"],
|
||||
["Tank","Цистерна"]
|
||||
],
|
||||
"ownersList": [
|
||||
"НТС",
|
||||
|
|
|
@ -7,7 +7,7 @@ import prittierConfig from 'eslint-plugin-prettier/recommended.js';
|
|||
import react from 'eslint-plugin-react';
|
||||
import eslintPluginUnicorn from 'eslint-plugin-unicorn';
|
||||
import globals from 'globals';
|
||||
|
||||
import reactHooksPlugin from 'eslint-plugin-react-hooks';
|
||||
/** @type { import("eslint").Linter.FlatConfig[] } */
|
||||
export default [
|
||||
{
|
||||
|
@ -52,11 +52,20 @@ export default [
|
|||
'import/no-named-as-default-member': 'warn',
|
||||
'import/no-duplicates': 'warn',
|
||||
'@typescript-eslint/no-unused-vars': 'off',
|
||||
'unicorn/filename-case': [
|
||||
'error',
|
||||
{
|
||||
case: 'camelCase',
|
||||
},
|
||||
],
|
||||
'react-hooks/rules-of-hooks': 'error', // Checks rules of Hooks
|
||||
'react-hooks/exhaustive-deps': 'warn', // Checks effect dependencies
|
||||
},
|
||||
plugins: {
|
||||
'@typescript-eslint': typescript,
|
||||
react,
|
||||
import: importPlugin,
|
||||
'react-hooks': reactHooksPlugin,
|
||||
},
|
||||
},
|
||||
importPlugin.configs.typescript,
|
||||
|
|
12
package.json
12
package.json
|
@ -22,7 +22,7 @@
|
|||
"author": "",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@remix-run/dev": "^2.4.0",
|
||||
"@remix-run/dev": "^2.4.1",
|
||||
"@types/bcrypt": "^5.0.2",
|
||||
"@types/eslint": "^8.56.0",
|
||||
"@types/lodash": "^4.14.202",
|
||||
|
@ -36,6 +36,7 @@
|
|||
"eslint-plugin-import": "^2.29.1",
|
||||
"eslint-plugin-prettier": "^5.1.0",
|
||||
"eslint-plugin-react": "^7.33.2",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"eslint-plugin-unicorn": "github:sindresorhus/eslint-plugin-unicorn",
|
||||
"lodash": "^4.17.21",
|
||||
"prettier": "3.1.1",
|
||||
|
@ -45,9 +46,10 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@nextui-org/react": "^2.2.9",
|
||||
"@remix-run/node": "^2.4.0",
|
||||
"@remix-run/react": "^2.4.0",
|
||||
"@remix-run/serve": "^2.4.0",
|
||||
"@reduxjs/toolkit": "^2.0.1",
|
||||
"@remix-run/node": "^2.4.1",
|
||||
"@remix-run/react": "^2.4.1",
|
||||
"@remix-run/serve": "^2.4.1",
|
||||
"bcrypt": "^5.1.1",
|
||||
"framer-motion": "^10.16.16",
|
||||
"isbot": "^3.7.1",
|
||||
|
@ -55,6 +57,8 @@
|
|||
"node-fetch": "^3.3.2",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-redux": "^9.0.4",
|
||||
"redux": "^5.0.1",
|
||||
"remix-auth": "^3.6.0",
|
||||
"remix-auth-form": "^1.4.0"
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import type { IStation } from 'app/services/api/interfaces.server';
|
||||
import React from 'react';
|
||||
import {Select, SelectSection, SelectItem} from "@nextui-org/react";
|
||||
import { Select, SelectSection, SelectItem } from '@nextui-org/react';
|
||||
|
||||
export default function ControlFormHeader({stations}: {stations: IStation[]}) {
|
||||
return(
|
||||
|
@ -14,10 +14,10 @@ export default function ControlFormHeader({stations}: {stations: IStation[]}) {
|
|||
>
|
||||
{stations.map((station) => (
|
||||
<SelectItem key={station.title} value={station.title}>
|
||||
{station.title}
|
||||
{station.title}
|
||||
</SelectItem>
|
||||
))}
|
||||
</Select>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
import React from 'react';
|
||||
|
||||
export default function controlTable() {
|
||||
return(
|
||||
<div id="control-table">
|
||||
|
||||
</div>
|
||||
)
|
||||
}
|
|
@ -0,0 +1,307 @@
|
|||
import React from 'react';
|
||||
import {
|
||||
Modal,
|
||||
ModalContent,
|
||||
ModalHeader,
|
||||
ModalBody,
|
||||
Button,
|
||||
useDisclosure,
|
||||
Listbox,
|
||||
ListboxItem,
|
||||
Select,
|
||||
SelectItem,
|
||||
} from '@nextui-org/react';
|
||||
import { IStationData } from 'app/services/api/interfaces.server';
|
||||
import { useSearchParams, useNavigate } from '@remix-run/react';
|
||||
|
||||
type Tdb = {
|
||||
stationsData: IStationData[];
|
||||
ownersList: string[];
|
||||
wagonTypeList: string[][];
|
||||
};
|
||||
|
||||
type Tfilters = {
|
||||
[filterKey: string]: {
|
||||
title: string;
|
||||
items: { key: string; value: string }[];
|
||||
};
|
||||
};
|
||||
|
||||
const filterMenuKeys: Record<string, string> = {
|
||||
wagons: 'Вагоны',
|
||||
ways: 'Ж/Д Пути',
|
||||
stations: 'Станции',
|
||||
};
|
||||
|
||||
function getFilterSelectedKeys(searchParameters: URLSearchParams) {
|
||||
const result: {
|
||||
[filterMenuKey: string]: { [filterKey: string]: Set<string> };
|
||||
} = {};
|
||||
const _filters = [...searchParameters.entries()]
|
||||
.filter((parameter) => parameter[0].startsWith('filter'))
|
||||
.map((parameter) => [parameter[0].replace('filter', ''), parameter[1]]);
|
||||
for (const [name, value] of _filters) {
|
||||
const [filterMenuKey, filterKey] = name
|
||||
.match(/[A-Z][a-z]*/g)!
|
||||
.map((value_) => value_.toLowerCase());
|
||||
if (!(filterMenuKey in result)) {
|
||||
result[filterMenuKey] = {};
|
||||
}
|
||||
result[filterMenuKey][filterKey] = new Set(value.split(','));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export function FilterMenu({
|
||||
activeFilterMenuKey,
|
||||
filters,
|
||||
statesSelectedKeys,
|
||||
}: {
|
||||
activeFilterMenuKey: string;
|
||||
filters: Tfilters;
|
||||
statesSelectedKeys: {
|
||||
[activeFilterMenuKey: string]: {
|
||||
[filterKey: string]: [
|
||||
Set<string>,
|
||||
React.Dispatch<React.SetStateAction<Set<string>>>,
|
||||
];
|
||||
};
|
||||
};
|
||||
}) {
|
||||
const [searchParameters] = useSearchParams();
|
||||
|
||||
const _t = getFilterSelectedKeys(searchParameters);
|
||||
for (const filterKey of Object.keys(filters)) {
|
||||
if (
|
||||
!(
|
||||
filterKey in
|
||||
Object.keys(statesSelectedKeys[activeFilterMenuKey])
|
||||
) &&
|
||||
!(activeFilterMenuKey in _t)
|
||||
) {
|
||||
_t[activeFilterMenuKey] = {};
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{Object.keys(filters).map((filterKey) => {
|
||||
return (
|
||||
<>
|
||||
<h4>{filters[filterKey].title}</h4>
|
||||
<Select
|
||||
aria-label="Один из пунктов фильтра"
|
||||
placeholder="Не выбрано"
|
||||
selectionMode="multiple"
|
||||
onChange={(event)=> {
|
||||
statesSelectedKeys[activeFilterMenuKey][
|
||||
filterKey
|
||||
][1](new Set(event.target.value.split(",")))
|
||||
}}
|
||||
selectedKeys={
|
||||
statesSelectedKeys[activeFilterMenuKey][
|
||||
filterKey
|
||||
][0]
|
||||
}
|
||||
onSelectionChange={(keys) =>
|
||||
(keys as Set<string>)
|
||||
}
|
||||
>
|
||||
{filters[filterKey].items.map((value) => {
|
||||
return (
|
||||
<SelectItem
|
||||
key={value.key}
|
||||
value={value.value}
|
||||
>
|
||||
{value.key}
|
||||
</SelectItem>
|
||||
);
|
||||
})}
|
||||
</Select>
|
||||
</>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default function ModalFilter({ db }: { db: Tdb }) {
|
||||
const filters: { [filterMenuKey: string]: Tfilters } = {
|
||||
wagons: {
|
||||
owner: {
|
||||
title: 'Собственник',
|
||||
items: db.ownersList.map((owner) => {
|
||||
return { key: owner, value: owner };
|
||||
}),
|
||||
},
|
||||
state: {
|
||||
title: 'Состояние вагона',
|
||||
items: [
|
||||
{
|
||||
key: 'Не прибыл',
|
||||
value: 'NotArrived',
|
||||
},
|
||||
{
|
||||
key: 'Простаивает',
|
||||
value: 'StandingBy',
|
||||
},
|
||||
{
|
||||
key: 'Закончил',
|
||||
value: 'Finished',
|
||||
},
|
||||
{
|
||||
key: 'В работе',
|
||||
value: 'inProgress',
|
||||
},
|
||||
],
|
||||
},
|
||||
type: {
|
||||
title: 'Тип вагона',
|
||||
items: db.wagonTypeList.map((type) => {
|
||||
return { key: type[1], value: type[0] };
|
||||
}),
|
||||
},
|
||||
},
|
||||
ways: {
|
||||
empty: {
|
||||
title: 'Загруженность пути',
|
||||
items: [
|
||||
{
|
||||
key: 'Полная',
|
||||
value: 'full',
|
||||
},
|
||||
{
|
||||
key: 'Частичная',
|
||||
value: 'half',
|
||||
},
|
||||
{
|
||||
key: 'Отсутствует',
|
||||
value: 'empty',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
const [searchParameters] = useSearchParams();
|
||||
const navigate = useNavigate();
|
||||
const { isOpen, onOpen, onOpenChange } = useDisclosure();
|
||||
const [activeFilterMenuKey, setActiveFilterMenuKey] = React.useState(
|
||||
Object.keys(filterMenuKeys)[0],
|
||||
);
|
||||
const statesSelectedKeys: {
|
||||
[activeFilterMenuKey: string]: {
|
||||
[filterKey: string]: [
|
||||
Set<string>,
|
||||
React.Dispatch<React.SetStateAction<Set<string>>>,
|
||||
];
|
||||
};
|
||||
} = {};
|
||||
statesSelectedKeys[activeFilterMenuKey] = {};
|
||||
const _t = getFilterSelectedKeys(searchParameters);
|
||||
|
||||
for (const filterMenuKey in filters) {
|
||||
if (!(filterMenuKey in statesSelectedKeys)) {
|
||||
statesSelectedKeys[filterMenuKey] = {};
|
||||
}
|
||||
for (const filterKey in filters[filterMenuKey]) {
|
||||
/* eslint-disable-next-line */
|
||||
statesSelectedKeys[filterMenuKey][filterKey] = React.useState(
|
||||
_t[filterMenuKey] === undefined
|
||||
? new Set()
|
||||
: _t[filterMenuKey][filterKey] ?? new Set(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="mb-4">
|
||||
<Button onPress={onOpen}>Фильтр</Button>
|
||||
<Modal
|
||||
isOpen={isOpen}
|
||||
onOpenChange={onOpenChange}
|
||||
scrollBehavior="outside"
|
||||
size="xl"
|
||||
>
|
||||
<ModalContent aria-label="Пользовательский фильтр">
|
||||
{(onClose) => (
|
||||
<>
|
||||
<ModalHeader>Пользовательский фильтр</ModalHeader>
|
||||
<ModalBody>
|
||||
<Listbox
|
||||
onAction={(menuKey) =>
|
||||
setActiveFilterMenuKey(
|
||||
menuKey.toString(),
|
||||
)
|
||||
}
|
||||
>
|
||||
{Object.keys(filterMenuKeys).map((key) => (
|
||||
<ListboxItem key={key}>
|
||||
{filterMenuKeys[key]}
|
||||
</ListboxItem>
|
||||
))}
|
||||
</Listbox>
|
||||
<div>
|
||||
<FilterMenu
|
||||
activeFilterMenuKey={
|
||||
activeFilterMenuKey
|
||||
}
|
||||
filters={filters[activeFilterMenuKey]}
|
||||
statesSelectedKeys={statesSelectedKeys}
|
||||
/>
|
||||
</div>
|
||||
<Button
|
||||
onClick={() => {
|
||||
for (const menuKey in statesSelectedKeys) {
|
||||
for (const filterKey in statesSelectedKeys[
|
||||
menuKey
|
||||
]) {
|
||||
const _m=Array.from(
|
||||
statesSelectedKeys[
|
||||
menuKey
|
||||
][
|
||||
filterKey
|
||||
][0].values()
|
||||
)
|
||||
if (
|
||||
_m[0] === '' || _m.length==0
|
||||
) {
|
||||
searchParameters.delete(
|
||||
`filter${
|
||||
menuKey[0].toUpperCase() +
|
||||
menuKey.slice(1)
|
||||
}${
|
||||
filterKey[0].toUpperCase() +
|
||||
filterKey.slice(1)
|
||||
}`,
|
||||
);
|
||||
} else {
|
||||
searchParameters.set(
|
||||
`filter${
|
||||
menuKey[0].toUpperCase() +
|
||||
menuKey.slice(1)
|
||||
}${
|
||||
filterKey[0].toUpperCase() +
|
||||
filterKey.slice(1)
|
||||
}`,
|
||||
_m.join(','),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
navigate(
|
||||
'?' + searchParameters.toString(),
|
||||
);
|
||||
onClose();
|
||||
}}
|
||||
>
|
||||
Применить
|
||||
</Button>
|
||||
<Button onClick={onClose}>Отмена</Button>
|
||||
</ModalBody>
|
||||
</>
|
||||
)}
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
import React from 'react';
|
||||
import {
|
||||
Modal,
|
||||
ModalContent,
|
||||
ModalHeader,
|
||||
ModalBody,
|
||||
Button,
|
||||
useDisclosure,
|
||||
Listbox,
|
||||
ListboxItem,
|
||||
Select,
|
||||
SelectItem,
|
||||
ModalFooter,
|
||||
} from '@nextui-org/react';
|
||||
import { IStationData } from 'app/services/api/interfaces.server';
|
||||
import { useSearchParams, useNavigate } from '@remix-run/react';
|
||||
|
||||
export default function ModalOperations () {
|
||||
const {isOpen, onOpen, onOpenChange} = useDisclosure();
|
||||
return (
|
||||
<>
|
||||
<Button onPress={onOpen}>Операции</Button>
|
||||
<Modal isOpen={isOpen} onOpenChange={onOpenChange}>
|
||||
<ModalContent>
|
||||
{(onClose) => (
|
||||
<>
|
||||
<ModalHeader className="flex flex-col gap-1">Операции</ModalHeader>
|
||||
<ModalBody className="w-[80%]">
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
Nullam pulvinar risus non risus hendrerit venenatis.
|
||||
Pellentesque sit amet hendrerit risus, sed porttitor quam.
|
||||
</p>
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
Nullam pulvinar risus non risus hendrerit venenatis.
|
||||
Pellentesque sit amet hendrerit risus, sed porttitor quam.
|
||||
</p>
|
||||
<p>
|
||||
Magna exercitation reprehenderit magna aute tempor cupidatat consequat elit
|
||||
dolor adipisicing. Mollit dolor eiusmod sunt ex incididunt cillum quis.
|
||||
Velit duis sit officia eiusmod Lorem aliqua enim laboris do dolor eiusmod.
|
||||
Et mollit incididunt nisi consectetur esse laborum eiusmod pariatur
|
||||
proident Lorem eiusmod et. Culpa deserunt nostrud ad veniam.
|
||||
</p>
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button color="danger" variant="light" onPress={onClose}>
|
||||
Close
|
||||
</Button>
|
||||
<Button color="primary" onPress={onClose}>
|
||||
Action
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</>
|
||||
)}
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -56,32 +56,10 @@ export default function NavbarCom() {
|
|||
Powered by EVRAZ
|
||||
</p>
|
||||
</NavbarBrand>
|
||||
<NavbarItem className="mx-auto">
|
||||
<Link color="foreground" to="#">
|
||||
Features
|
||||
</Link>
|
||||
</NavbarItem>
|
||||
<NavbarItem isActive>
|
||||
<Link to="#" aria-current="page">
|
||||
Customers
|
||||
</Link>
|
||||
</NavbarItem>
|
||||
<NavbarItem>
|
||||
<Link color="foreground" to="#">
|
||||
Integrations
|
||||
</Link>
|
||||
</NavbarItem>
|
||||
</NavbarContent>
|
||||
|
||||
<NavbarContent justify="end">
|
||||
<NavbarItem className="hidden lg:flex">
|
||||
<Link to="#">Login</Link>
|
||||
</NavbarItem>
|
||||
<NavbarItem>
|
||||
<Button as={Link} color="primary" to="#" variant="flat">
|
||||
Sign Up
|
||||
</Button>
|
||||
</NavbarItem>
|
||||
|
||||
</NavbarContent>
|
||||
|
||||
<NavbarMenu>
|
||||
|
|
|
@ -4,6 +4,7 @@ import Wagon from './wagon';
|
|||
import Locomotive from './locomotive';
|
||||
|
||||
export default function Park({park, railRoads}: {park: IPark, railRoads: IStationData[]}) {
|
||||
|
||||
return(
|
||||
<div id="control-park" className='relative mb-12 py-1 pl-8 pr-2 border-secondary-200 border-[1px]'>
|
||||
<div className="absolute left-0 top-0 w-6 h-full bg-secondary-200 flex justify-center items-center" style={{writingMode: "vertical-lr"}}>{park.name}</div>
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import React from 'react';
|
||||
import React, { useContext } from 'react';
|
||||
import { IWagon } from 'app/services/api/interfaces.server';
|
||||
|
||||
import { useSelector, useDispatch } from 'react-redux'
|
||||
import { selectWagon } from 'app/wagonsSlice';
|
||||
export default function Wagon({ wagon }: { wagon: IWagon }) {
|
||||
console.log(wagon)
|
||||
const colors: Record<string, string[]> = {
|
||||
НТС: ['#BCF3FF', '#2988AE'],
|
||||
ГК: ['#C8F4C1', '#6EA566'],
|
||||
|
@ -27,8 +27,16 @@ export default function Wagon({ wagon }: { wagon: IWagon }) {
|
|||
bottomFillColor = '#EB5835';
|
||||
bottomStrokeColor = '#E32112';
|
||||
}
|
||||
const wagons = useSelector(selectWagon);
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const selectWagonHandle = (e: any) => {
|
||||
const id = e.currentTarget.getAttribute("wagon-id");
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<div id="wagon" inventory-data={wagon.id} onClick={e => console.log(e.currentTarget)} className=" w-fit relative flex flex-col items-center">
|
||||
<div id="wagon" wagon-id={wagon.id} onClick={selectWagonHandle} className=" w-fit relative flex flex-col items-center">
|
||||
<div className="relative top-[2px]">
|
||||
{WagonTop(wagon.type, topFillColor, topBorderColor)}
|
||||
</div>
|
||||
|
|
|
@ -1,31 +1,48 @@
|
|||
import React from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import { createContext, useContext } from 'react';
|
||||
import { json, type LoaderFunctionArgs } from '@remix-run/node';
|
||||
import { authenticator } from '../services/auth.server';
|
||||
import { IStation, IStationData } from 'app/services/api/interfaces.server';
|
||||
import { api } from 'app/services/api/json.api.server';
|
||||
import { useLoaderData } from '@remix-run/react';
|
||||
import ControlFormHeader from 'app/coms/controlFormHeader';
|
||||
import ModalFilter from 'app/coms/modalFilter';
|
||||
import Station from 'app/coms/ui/Station';
|
||||
import { Input, Table, TableColumn, TableHeader } from '@nextui-org/react';
|
||||
import { Provider } from 'react-redux'
|
||||
import { store } from 'app/wagonsSlice';
|
||||
import ModalOperations from 'app/coms/modalOperations';
|
||||
|
||||
export async function loader({ request }: LoaderFunctionArgs) {
|
||||
const authData = await authenticator.isAuthenticated(request, {
|
||||
failureRedirect: '/login',
|
||||
});
|
||||
const stations: IStation[] = await api.getStations();
|
||||
const stationsData = await api.getStationsData();
|
||||
const owners: string[] = await api.getOwners();
|
||||
const stationsData: IStationData[] = await api.getStationsData();
|
||||
const wagonTypeList = await api.getWagonTypes();
|
||||
return json({
|
||||
stations,
|
||||
stationsData,
|
||||
owners,
|
||||
authData,
|
||||
wagonTypeList,
|
||||
});
|
||||
}
|
||||
|
||||
export default function Index() {
|
||||
const {authData, stations, stationsData, owners} = useLoaderData<typeof loader>();
|
||||
const { authData, stations, owners, stationsData, wagonTypeList } =
|
||||
useLoaderData<typeof loader>();
|
||||
return (
|
||||
<div id="index" className='w-full h-full'>
|
||||
<div className="flex justify-between">
|
||||
<ModalFilter
|
||||
db={{
|
||||
stationsData: stationsData,
|
||||
ownersList: owners,
|
||||
wagonTypeList,
|
||||
}}
|
||||
/>
|
||||
<ModalOperations />
|
||||
</div>
|
||||
<div id="control-form" className='w-full h-full flex flex-col rounded-sm border-secondary-200 border-[1px]'>
|
||||
<div className="h-9 mb-4 pl-4 bg-secondary-100 text-y-center rounded-t-sm flex items-center">
|
||||
АРМ дежурного станции
|
||||
|
@ -48,13 +65,14 @@ export default function Index() {
|
|||
<div className="w-12 flex justify-center items-center ">
|
||||
Л
|
||||
</div>
|
||||
</div>
|
||||
{stations.map(station => {
|
||||
const stationData = stationsData.filter(el => el.station.id == station.id);
|
||||
return(<Station stationData={stationData} station={station} key={station.id}/>)
|
||||
})
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<Provider store={store}>
|
||||
{stations.map(station => {
|
||||
const stationData = stationsData.filter(el => el.station.id == station.id);
|
||||
return(<Station stationData={stationData} station={station} key={station.id}/>)
|
||||
})}
|
||||
</Provider>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
}
|
||||
|
|
|
@ -28,7 +28,6 @@ export default function Auth() {
|
|||
placeholder="Enter your password"
|
||||
/>
|
||||
<Button type="submit" color="primary">
|
||||
{' '}
|
||||
Login
|
||||
</Button>
|
||||
</div>
|
||||
|
|
|
@ -28,7 +28,6 @@ export default function Login() {
|
|||
placeholder="Enter your password"
|
||||
/>
|
||||
<Button type="submit" color="primary">
|
||||
{' '}
|
||||
Login
|
||||
</Button>
|
||||
</div>
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
import fetch from 'node-fetch';
|
||||
import { env } from 'node:process';
|
||||
import { INorm, IOperationType, IStation, IStationData, IUser } from './interfaces.server';
|
||||
import {
|
||||
INorm,
|
||||
IOperationType,
|
||||
IStation,
|
||||
IStationData,
|
||||
IUser,
|
||||
} from './interfaces.server';
|
||||
|
||||
const endpoints = {
|
||||
getStations: 'stationsList',
|
||||
|
@ -37,7 +43,7 @@ export const api = new (class ApiDB {
|
|||
const response = await fetch(this.url + endpoints.getWagonTypeList, {
|
||||
method: 'GET',
|
||||
});
|
||||
return (await response.json()) as string[];
|
||||
return (await response.json()) as string[][];
|
||||
}
|
||||
async getOwners() {
|
||||
const response = await fetch(this.url + endpoints.getOwnersList, {
|
||||
|
@ -46,13 +52,19 @@ export const api = new (class ApiDB {
|
|||
return (await response.json()) as string[];
|
||||
}
|
||||
|
||||
async getStationData(stationID: number): Promise<IStationData[]> {
|
||||
async getStationData(stationID: number): Promise<IStationData> {
|
||||
const response = await fetch(
|
||||
this.url + endpoints.getStationsData + `?station.id=${stationID}`,
|
||||
{
|
||||
method: 'GET',
|
||||
},
|
||||
);
|
||||
return ((await response.json()) as IStationData[])[0];
|
||||
}
|
||||
async getStationsData(): Promise<IStationData[]> {
|
||||
const response = await fetch(this.url + endpoints.getStationsData, {
|
||||
method: 'GET',
|
||||
});
|
||||
return (await response.json()) as IStationData[];
|
||||
}
|
||||
async getStationsData(): Promise<IStationData[]> {
|
||||
|
|
|
@ -13,7 +13,6 @@ authenticator.use(
|
|||
let login = <string>form.get('login');
|
||||
let password = <string>form.get('password');
|
||||
const users = await api.getUsers();
|
||||
|
||||
for (const user of users) {
|
||||
if (
|
||||
user.login == login &&
|
||||
|
@ -33,7 +32,7 @@ authenticator.use(
|
|||
let password = <string>form.get('password');
|
||||
|
||||
let hashedPassword = '';
|
||||
await hash(password, saltRounds, (error, hash) => {
|
||||
hash(password, saltRounds, (error, hash) => {
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
import { createSlice, configureStore } from '@reduxjs/toolkit'
|
||||
|
||||
const wagonSlice = createSlice({
|
||||
name: 'wagonsSlice',
|
||||
initialState: {
|
||||
selectedWagons: []
|
||||
},
|
||||
reducers: {
|
||||
selectWagon: (state, action) => {
|
||||
state.selectedWagons.find(action.payload) ?
|
||||
state.selectedWagons = state.selectedWagons.filter(el => el == action.payload):
|
||||
state.selectedWagons = <never[]>[...state.selectedWagons, action.payload]
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
export const { selectWagon } = wagonSlice.actions
|
||||
|
||||
export const store = configureStore({
|
||||
reducer: wagonSlice.reducer
|
||||
})
|
Loading…
Reference in New Issue