Commit e30230fc authored by Nipun Lakshan Perera's avatar Nipun Lakshan Perera

Code

parents
/node_modules
\ No newline at end of file
# Default ignored files
/shelf/
/workspace.xml
# CodeStream ignored files
/../../React-Admin-Template\.idea/codestream.xml
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CodeStream">
<option name="webViewContext" value="{&quot;chatProviderAccess&quot;:&quot;strict&quot;,&quot;currentTeamId&quot;:&quot;&quot;,&quot;currentStreamId&quot;:&quot;&quot;,&quot;pullRequestCheckoutBranch&quot;:false,&quot;isRepositioning&quot;:false,&quot;onboardStep&quot;:0,&quot;panelStack&quot;:[&quot;landing-redirect&quot;],&quot;hasFocus&quot;:false,&quot;channelFilter&quot;:&quot;all&quot;,&quot;channelsMuteAll&quot;:false,&quot;codemarkFileFilter&quot;:&quot;all&quot;,&quot;codemarkTypeFilter&quot;:&quot;all&quot;,&quot;codemarkTagFilter&quot;:&quot;all&quot;,&quot;codemarkBranchFilter&quot;:&quot;all&quot;,&quot;codemarkAuthorFilter&quot;:&quot;all&quot;,&quot;codemarksFileViewStyle&quot;:&quot;inline&quot;,&quot;codemarksShowArchived&quot;:false,&quot;codemarksShowResolved&quot;:false,&quot;codemarksWrapComments&quot;:false,&quot;showFeedbackSmiley&quot;:true,&quot;route&quot;:{&quot;name&quot;:&quot;newUserEntry&quot;,&quot;params&quot;:{}},&quot;spatialViewShowPRComments&quot;:false,&quot;currentPullRequestNeedsRefresh&quot;:{&quot;needsRefresh&quot;:false,&quot;providerId&quot;:&quot;&quot;,&quot;pullRequestId&quot;:&quot;&quot;},&quot;__teamless__&quot;:{&quot;selectedRegion&quot;:&quot;us&quot;},&quot;sessionStart&quot;:1668697066264}" />
</component>
</project>
\ No newline at end of file
<component name="InspectionProjectProfileManager">
<settings>
<option name="USE_PROJECT_PROFILE" value="false" />
<version value="1.0" />
</settings>
</component>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.9" project-jdk-type="Python SDK" />
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/React-Admin-Template.iml" filepath="$PROJECT_DIR$/.idea/React-Admin-Template.iml" />
</modules>
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>
\ No newline at end of file
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, sex characteristics, gender identity and expression,
level of experience, education, socio-economic status, nationality, personal
appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at przemek.strucinski@gmail.com. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see
https://www.contributor-covenant.org/faq
This diff is collapsed.
## [Simple React Admin template](https://react-admin-template.netlify.com/) :boom: :ok_hand: :star:
[![Netlify Status](https://api.netlify.com/api/v1/badges/b9dc8cf2-fb7a-4def-824e-42a370a36df5/deploy-status)](https://app.netlify.com/sites/react-admin-template/deploys)
[![Awesome](https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg)](https://react-admin-template.netlify.com/)
Completly **free** simple admin template starter for React.
Only **core functionalities** in order to avoid huge refactor/cleaning.
**Ready to start new project**, small amount of dependencies, easy configuration, modern and fast.
Created by https://foreach.pl trainers for training participants.
**Check also Cheatsheets:**
[React](https://github.com/delprzemo/react-cheatsheet) :star:
[Angular](https://github.com/delprzemo/angular-cheatsheet) :star:
[TypeScript](https://github.com/delprzemo/typescript-cheatsheet) :star:
[![image](https://i.imgur.com/6T37H6V.jpg)](https://react-admin-template.netlify.com/)
<img src="https://i.imgur.com/q607C0f.jpg." data-canonical-src="https://i.imgur.com/q607C0f.jpg" width="400" />
<img src="https://i.imgur.com/a2NUwFX.jpg." data-canonical-src="https://i.imgur.com/q607C0f.jpg" width="245" />
## Demo
[Click here for a demo](https://react-admin-template.netlify.com/)
## Features
- React (without jQuery etc.)
- TypeScript
- React Hooks
- Redux
- Responsive (adjusted to mobile devices)
- React-router-dom
- Bootstrap 4
- Modern, clean, readable layout
- Authentication
- **Clean, easy, ready to start new project**
## Pricing
It's completly **free** (no commercial versions)
## Quick start
#### 1. Get the latest version
Clone repository
```shell
$ git clone https://github.com/delprzemo/react-admin-template.git React-Admin-Template
$ cd React-Admin-Template
```
#### 2. Run `npm install`
npm install will install all modules listed as dependencies in [package.json](package.json) file.
#### 3. Run `npm run start`
It will run application in http://localhost:3000
#### 4. Run `npm run build`
Builds the production version of app and put it into build folder. Now you can use these files to perform real production deployment.
## Contribute
Contributions are always welcome!
## License
[![CC0](https://licensebuttons.net/p/zero/1.0/88x31.png)](https://creativecommons.org/publicdomain/zero/1.0/)
To the extent possible under law, [Przemek Struciński](https://foreach.pl) has waived all copyright and related or neighboring rights to this work.
theme: jekyll-theme-cayman
\ No newline at end of file
This diff is collapsed.
{
"name": "react-template",
"version": "0.1.0",
"private": true,
"dependencies": {
"@types/jest": "24.0.18",
"@types/node": "12.7.12",
"@types/react": "16.9.5",
"@types/react-dom": "16.9.1",
"@types/react-redux": "^7.1.4",
"@types/react-router-dom": "^5.1.0",
"@types/redux-thunk": "^2.1.0",
"axios": "^0.19.0",
"bootstrap": "^4.3.1",
"react": "^16.10.2",
"react-dom": "^16.10.2",
"react-fontawesome": "^1.6.1",
"react-redux": "^7.1.1",
"react-router-dom": "^5.1.2",
"react-scripts": "^3.2.0",
"reactjs-popup": "^1.5.0",
"redux": "^4.0.4",
"redux-thunk": "^2.3.0",
"typescript": "^3.7.0-beta"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": "react-app"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"@fortawesome/fontawesome-free": "^5.11.2"
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<link rel="apple-touch-icon" href="logo192.png" />
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<title>RyHealthProfile</title>
</head>
<body id="page-top">
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>
{
"short_name": "React App",
"name": "Create React App Sample",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
},
{
"src": "logo192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "logo512.png",
"type": "image/png",
"sizes": "512x512"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}
# https://www.robotstxt.org/robotstxt.html
User-agent: *
body {
color: darkslategray;
}
.popup-modal-content {
width : 300px !important;
box-shadow: 0 .5rem 1rem rgba(0, 0, 0, .15) !important;
}
.popup-title {
height : 40px;
position : absolute;
width : 100%;
left : 0px;
background-color: whitesmoke;
top : 0px;
text-align : center;
padding-top : 5px;
color : black;
}
.popup-content {
margin-top : 40px;
padding-top: 10px;
width : 100%;
text-align : center;
}
.popup-modal {
height: 70px;
}
.left-margin {
margin-left: 10px;
}
.toast-wrapper {
position: fixed;
top : 80px;
right : 30px;
z-index: 100 !important;
}
.toast {
opacity: 0.95 !important;
width: 250px;
}
.toast-header {
color: dodgerblue !important;
}
.card-bottom-list {
max-height: 303px;
}
.bg-login-image {
background: url("./assets/login.png") !important;
background-position: center !important;
background-size: cover !important;
}
.bg-gradient-primary-green {
background-image: linear-gradient(180deg,#0e1e24 10%, #00c6b6 100%);
background-size: cover;
}
.icon-green {
color: seagreen;
}
.bg-custom-dark {
background-color: #0e1e24;
}
.dark-breadcrumb {
background-color: #0e1e24 !important;
margin-top: 15px !important;
}
li a {
color: cadetblue;
}
.cadet {
color: cadetblue;
}
.text-green {
color: cadetblue !important;
font-family: sans-serif !important;
}
.toggle-area {
display:none;
}
.collapsed {
display:block;
}
.toggle-button {
background-color: #0e1e24 !important;
border-color: #0e1e24 !important;
width: 40px;
color: seagreen !important;
}
@media (max-width: 380px) {
.collapsed {
display:none !important;
}
.toggle-area {
position: fixed;
bottom: 2%;
left:2%;
z-index: 200 !important;
display: block;
}
}
\ No newline at end of file
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
it('renders without crashing', () => {
const div = document.createElement('div');
ReactDOM.render(<App />, div);
ReactDOM.unmountComponentAtNode(div);
});
import React from "react";
import "./App.css";
import "./styles/sb-admin-2.min.css";
import { BrowserRouter as Router, Switch, Route, } from "react-router-dom";
import Login from "./components/Account/Login";
import SignUp from "./components/Account/Signup";
import Admin from "./components/Admin/Admin";
import { PrivateRoute } from "./common/components/PrivateRoute";
import { AccountRoute } from "./common/components/AccountRoute";
const App: React.FC = () => {
return (
<div className="App" id="wrapper">
<Router>
<Switch>
{/* <PrivateRoute path="/">
<Admin />
</PrivateRoute> */}
{/* */}
<Route path="/Login"><Login /></Route>
<Route path="/SignUp"><SignUp /></Route>
<Route path="/"><Admin /></Route>
{/* <AccountRoute path="/login"><Login /></AccountRoute>
<AccountRoute path="/signup"><SignUp /></AccountRoute> */}
</Switch>
</Router>
</div>
);
};
export default App;
import { Route, Redirect, RouteProps } from "react-router";
import React from "react";
import { useSelector } from "react-redux";
import { IStateType } from "../../store/models/root.interface";
import { IAccount } from "../../store/models/account.interface";
import Login from "../../components/Account/Login";
export function AccountRoute({ children, ...rest }: RouteProps): JSX.Element {
const account: IAccount = useSelector((state: IStateType) => state.account);
return (
<Route
{...rest}
render={() =>
account.email ? (
<Redirect
to={{
pathname: "/admin/home"
}}
/>
) : <Login />
}
/>
);
}
\ No newline at end of file
import React, { useState, ChangeEvent } from "react";
import { CheckboxProps } from "../types/Checkbox.types";
function Checkbox(props: CheckboxProps): JSX.Element {
const [touched, setTouch] = useState(false);
const [error, setError] = useState("");
const [htmlClass, setHtmlClass] = useState("");
const [, setValue] = useState(false);
function onValueChanged(event: ChangeEvent<HTMLInputElement>): void {
let [error, validClass, elementValue] = ["", "", event.target.checked];
[error, validClass] = (!elementValue && props.required) ?
["Value has to be checked", "is-invalid"] : ["", "is-valid"];
props.onChange({ value: elementValue, error: error, touched: touched, field: props.field });
setTouch(true);
setError(error);
setHtmlClass(validClass);
setValue(elementValue);
}
return (
<div className="form-check">
<input
className={`form-check-input ${props.inputClass ? props.inputClass : ""} ${htmlClass}`}
type="checkbox"
id={`id_${props.label}`}
checked={props.value}
onChange={onValueChanged} />
<label className="form-check-label" htmlFor={props.id.toString()}>
{props.label}
</label>
{error ?
<div className="invalid-feedback">
{error}
</div> : null
}
</div>
);
}
export default Checkbox;
\ No newline at end of file
import React, { Dispatch } from "react";
import { useDispatch, useSelector } from "react-redux";
import { IStateType } from "../../store/models/root.interface";
import { INotification } from "../../store/models/notification.interface";
import { removeNotification } from "../../store/actions/notifications.action";
const Notifications: React.FC = () => {
const dispatch: Dispatch<any> = useDispatch();
const notifications: INotification[] = useSelector((state: IStateType) =>
state.notifications.notifications);
function closeNotification(id: number) {
dispatch(removeNotification(id));
}
const notificationList = notifications.map(notification => {
return (
<div className="toast" key={`notification_${notification.id}`}>
<div className="toast-header">
<i className="fas fa-fw fa-bell"></i>
<strong className="mr-auto">{notification.title}</strong>
<small>{notification.date.toLocaleTimeString(navigator.language, { hour: '2-digit', minute: '2-digit' })}</small>
<button type="button"
className="ml-2 mb-1 close"
data-dismiss="toast"
aria-label="Close"
onClick={() => closeNotification(notification.id)}>
<span aria-hidden="true">&times;</span>
</button>
</div>
<div className="toast-body">
{notification.text}
</div>
</div>
)
});
return (
<div className="toast-wrapper">
{notificationList}
</div>
);
};
export default Notifications;
import React, { useState, ChangeEvent } from "react";
import { NumberInputProps } from "../types/NumberInput.types";
function NumberInput(props: NumberInputProps): JSX.Element {
const [touched, setTouch] = useState(false);
const [error, setError] = useState("");
const [htmlClass, setHtmlClass] = useState("");
const [, setValue] = useState(0);
function onValueChanged(event: ChangeEvent<HTMLInputElement>): void {
let elementValue: number = (isNaN(Number(event.target.value))) ? 0 : Number(event.target.value);
let [error, validClass] = ["", ""];
if (!error) {
[error, validClass] = ((props.max != null) && elementValue > (props.max)) ?
[`Value can't be higher than ${props.max} `, "is-invalid"] : ["", "is-valid"];
}
if (!error) {
[error, validClass] = ((props.min != null) && elementValue < (props.min)) ?
[`Value can't be lower than ${props.min} `, "is-invalid"] : ["", "is-valid"];
}
props.onChange({ value: elementValue, error: error, touched: touched, field: props.field });
setTouch(true);
setError(error);
setHtmlClass(validClass);
setValue(elementValue);
}
return (
<div>
<label htmlFor={props.id.toString()}>{props.label}</label>
<input
value={props.value}
type="number"
onChange={onValueChanged}
className={`form-control ${props.inputClass} ${htmlClass}`}
id={`id_${props.label}`}/>
{error ?
<div className="invalid-feedback">
{error}
</div> : null
}
</div>
);
}
export default NumberInput;
\ No newline at end of file
import { Route, RouteProps } from "react-router";
import React from "react";
import { useSelector } from "react-redux";
import { IStateType } from "../../store/models/root.interface";
import { IAccount } from "../../store/models/account.interface";
import Login from "../../components/Account/Login";
export function PrivateRoute({ children, ...rest }: RouteProps): JSX.Element {
const account: IAccount = useSelector((state: IStateType) => state.account);
return (
<Route
{...rest}
render={() =>
account.email ? (
children
) : <Login/>
}
/>
);
}
\ No newline at end of file
import React, { useState, ChangeEvent, Fragment } from "react";
import { SelectProps } from "../types/Select.types";
function SelectInput(props: SelectProps): JSX.Element {
const [touched, setTouch] = useState(false);
const [error, setError] = useState("");
const [htmlClass, setHtmlClass] = useState("");
const [value, setValue] = useState(props.value);
function onValueChanged(event: ChangeEvent<HTMLSelectElement>): void {
let [error, validClass, elementValue] = ["", "", event.target.value];
[error, validClass] = (!elementValue && props.required) ?
["Value has to be selected", "is-invalid"] : ["", "is-valid"];
props.onChange({ value: elementValue, error: error, touched: touched, field: props.field });
setTouch(true);
setError(error);
setHtmlClass(validClass);
setValue(elementValue);
}
const getOptions: (JSX.Element | null)[] = props.options.map(option => {
return (
<option key={option} value={`${option}`}>{option}</option>
)
});
return (
<Fragment>
<label htmlFor={`${props.id}`}>{props.label}</label>
<select
value={value}
id={`${props.id}`}
className={`form-control ${props.inputClass ? props.inputClass : ""} ${htmlClass}`}
onChange={onValueChanged}>
<option value="">Choose...</option>
{getOptions}
</select>
{error ?
<div className="invalid-feedback">
{error}
</div> : null
}
</Fragment>
);
}
export default SelectInput;
\ No newline at end of file
import React, { useState, ChangeEvent } from "react";
import { TextInputProps } from "../types/TextInput.types";
function TextInput(props: TextInputProps): JSX.Element {
const [touched, setTouch] = useState(false);
const [error, setError] = useState("");
const [htmlClass, setHtmlClass] = useState("");
const [, setValue] = useState("");
function onValueChanged(event: ChangeEvent<HTMLInputElement>): void {
let [error, validClass, elementValue] = ["", "", event.target.value];
[error, validClass] = (!elementValue && props.required) ?
["Value cannot be empty", "is-invalid"] : ["", "is-valid"];
if (!error) {
[error, validClass] = (props.maxLength && elementValue && elementValue.length > (props.maxLength)) ?
[`Value can't have more than ${props.maxLength} characters`, "is-invalid"] : ["", "is-valid"];
}
props.onChange({ value: elementValue, error: error, touched: touched, field: props.field });
setTouch(true);
setError(error);
setHtmlClass(validClass);
setValue(elementValue);
}
return (
<div>
<label htmlFor={props.id.toString()}>{props.label}</label>
<input
value={props.value}
type={props.type}
onChange={onValueChanged}
className={`form-control ${props.inputClass} ${htmlClass}`}
id={`id_${props.label}`}
placeholder={props.placeholder} />
{error ?
<div className="invalid-feedback">
{error}
</div> : null
}
</div>
);
}
export default TextInput;
\ No newline at end of file
import React, { PropsWithChildren, ReactElement } from "react";
import { ICardProperties } from "../types/TopCard.types";
function TopCard(props: PropsWithChildren<ICardProperties>): ReactElement {
return (
<div className="col-xl-3 col-md-6 mb-4">
<div className={`card border-left-${props.class} shadow h-100 py-2 `}>
<div className="card-body">
<div className="row no-gutters align-items-center">
<div className="col mr-2">
<div className="text-xs font-weight-bold text-green text-uppercase mb-1">{props.title}</div>
<div className="h5 mb-0 font-weight-bold text-gray-800">{props.text}</div>
</div>
<div className="col-auto">
<i className={`fas fa-${props.icon} fa-2x text-gray-300`}></i>
</div>
</div>
</div>
</div>
</div>
);
}
export default TopCard;
export type CheckboxProps = {
required?: boolean,
onChange: Function,
id: string,
label: string,
value: boolean,
inputClass?: string,
field: string
};
export type OnChangeCheckboxModel = {
value: boolean,
error: string,
touched: boolean
};
\ No newline at end of file
import { IProduct } from "../../store/models/product.interface";
export type OnChangeModel = {
value: string | number | boolean,
error: string,
touched: boolean,
field: string
};
export interface IFormStateField<T> {error: string, value: T};
export interface IProductFormState {
qualification: IFormStateField<string>;
experience: IFormStateField<number>;
rating: IFormStateField<number>;
place: IFormStateField<string>;
profile: IFormStateField<string>;
name: IFormStateField<string>;
description: IFormStateField<string>;
amount: IFormStateField<number>;
price: IFormStateField<number>;
hasExpiryDate: IFormStateField<boolean>;
category: IFormStateField<string>;
}
export interface IOrderFormState {
name: IFormStateField<string>;
product: IFormStateField<IProduct | null>;
amount: IFormStateField<number>;
totalPrice: IFormStateField<number>;
result: IFormStateField<number>;
unit: IFormStateField<string>;
refRange: IFormStateField<string>;
};
\ No newline at end of file
export type NumberInputProps = {
onChange: Function,
id: string,
label: string,
value: number,
max?: number,
min?: number,
inputClass?: string,
field: string
};
export type OnChangeNumberModel = {
value: number,
error: string,
touched: boolean
};
\ No newline at end of file
export type SelectProps = {
required?: boolean,
onChange: Function,
id: string,
label: string,
value: string,
inputClass?: string,
options: string[],
field: string
};
\ No newline at end of file
export type TextInputProps = {
required: boolean,
onChange: Function,
id: string,
label: string,
placeholder: string,
value: string,
type?: string,
maxLength: number,
inputClass?: string,
field: string
};
\ No newline at end of file
export interface ICardProperties {
text: string;
title: string;
icon: string;
class: string;
}
import React, { useState, FormEvent, Dispatch } from "react";
import { useHistory } from "react-router";
import { OnChangeModel } from "../../common/types/Form.types";
import { useDispatch } from "react-redux";
import { login } from "../../store/actions/account.actions";
import TextInput from "../../common/components/TextInput";
import axios from 'axios';
const Login: React.FC = () => {
const dispatch: Dispatch<any> = useDispatch();
const history = useHistory();
const [formState, setFormState] = useState({
email: { error: "", value: "" },
password: { error: "", value: "" }
});
function hasFormValueChanged(model: OnChangeModel): void {
setFormState({ ...formState, [model.field]: { error: model.error, value: model.value } });
}
function submit(e: FormEvent<HTMLFormElement>): void {
e.preventDefault();
if(isFormInvalid()) { return; }
const formData = new FormData();
formData.append("email", formState.email.value);
formData.append("password", formState.password.value);
axios.post('http://127.0.0.1:8000/staffapi/login', formData, {headers: {"Content-type": "multipart/form-date"}})
.then(response => {
console.log(response.data)
if(response.data.hasOwnProperty("token"))
{ dispatch(login(formState.email.value));
history.push('/')
}
});
}
function isFormInvalid() {
return (formState.email.error || formState.password.error
|| !formState.email.value || !formState.password.value);
}
function getDisabledClass(): string {
let isError: boolean = isFormInvalid() as boolean;
return isError ? "disabled" : "";
}
return (
<div className="container">
<div className="row justify-content-center">
<div className="col-xl-10 col-lg-12 col-md-9">
<div className="card o-hidden border-0 shadow-lg my-5">
<div className="card-body p-0">
<div className="row">
<div className="col-lg-6 d-none d-lg-block bg-login-image"></div>
<div className="col-lg-6">
<div className="p-5">
<div className="text-center">
<h1 className="h4 text-gray-900 mb-4">Welcome!</h1>
</div>
<form className="user" onSubmit={submit}>
<div className="form-group">
<TextInput id="input_email"
field="email"
value={formState.email.value}
onChange={hasFormValueChanged}
required={true}
maxLength={100}
label="Email"
placeholder="Email" />
</div>
<div className="form-group">
<TextInput id="input_password"
field="password"
value={formState.password.value}
onChange={hasFormValueChanged}
required={true}
maxLength={100}
type="password"
label="Password"
placeholder="Password" />
</div>
<div className="form-group">
<div className="custom-control custom-checkbox small">
<input type="checkbox" className="custom-control-input" id="customCheck" />
<label className="custom-control-label"
htmlFor="customCheck">Remember Me</label>
</div>
</div>
<button
className={`btn btn-primary btn-user btn-block ${getDisabledClass()}`}
type="submit">
Login
</button>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
);
};
export default Login;
import React, { useState, FormEvent, Dispatch } from "react";
import { useHistory } from "react-router";
import { OnChangeModel } from "../../common/types/Form.types";
import { useDispatch } from "react-redux";
import { login } from "../../store/actions/account.actions";
import TextInput from "../../common/components/TextInput";
import axios from 'axios';
const SignUp: React.FC = () => {
const dispatch: Dispatch<any> = useDispatch();
const history = useHistory();
const [formState, setFormState] = useState({
name: { error: "", value: "" },
email: { error: "", value: "" },
password: { error: "", value: "" }
});
function hasFormValueChanged(model: OnChangeModel): void {
setFormState({ ...formState, [model.field]: { error: model.error, value: model.value } });
}
function submit(e: FormEvent<HTMLFormElement>): void {
e.preventDefault();
if(isFormInvalid()) { return; }
const formData = new FormData();
formData.append("name", formState.name.value);
formData.append("email", formState.email.value);
formData.append("password", formState.password.value);
axios.post('http://127.0.0.1:8000/staffapi/register', formData, {headers: {"Content-type": "multipart/form-date"}})
.then(response => {
console.log(response.data)
if(response.data.hasOwnProperty("name"))
{ history.push('/Login')}
});
// dispatch(login(formState.email.value));
}
function isFormInvalid() {
return (formState.email.error || formState.password.error
|| !formState.email.value || !formState.password.value);
}
function getDisabledClass(): string {
let isError: boolean = isFormInvalid() as boolean;
return isError ? "disabled" : "";
}
return (
<div className="container">
<div className="row justify-content-center">
<div className="col-xl-10 col-lg-12 col-md-9">
<div className="card o-hidden border-0 shadow-lg my-5">
<div className="card-body p-0">
<div className="row">
<div className="col-lg-6 d-none d-lg-block bg-login-image"></div>
<div className="col-lg-6">
<div className="p-5">
<div className="text-center">
<h1 className="h4 text-gray-900 mb-4">Registration</h1>
</div>
<form className="user" onSubmit={submit}>
<div className="form-group">
<TextInput id="input_name"
field="name"
value={formState.name.value}
onChange={hasFormValueChanged}
required={true}
maxLength={100}
label="Name"
placeholder="Name" />
</div>
<div className="form-group">
<TextInput id="input_email"
field="email"
value={formState.email.value}
onChange={hasFormValueChanged}
required={true}
maxLength={100}
label="Email"
placeholder="Email" />
</div>
<div className="form-group">
<TextInput id="input_password"
field="password"
value={formState.password.value}
onChange={hasFormValueChanged}
required={true}
maxLength={100}
type="password"
label="Password"
placeholder="Password" />
</div>
{/* <div className="form-group">
<div className="custom-control custom-checkbox small">
<input type="checkbox" className="custom-control-input" id="customCheck" />
<label className="custom-control-label"
htmlFor="customCheck">Remember Me</label>
</div>
</div> */}
<button
className={`btn btn-primary btn-user btn-block ${getDisabledClass()}`}
type="submit">
SignUp
</button>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
);
};
export default SignUp;
import React, { Fragment } from "react";
import LeftMenu from "../LeftMenu/LeftMenu";
import TopMenu from "../TopMenu/TopMenu";
import { Switch, Route } from "react-router";
import Users from "../Users/Users";
import Products from "../Products/Products";
import Orders from "../Orders/Orders";
import Prescription from "../Prescription/Prescription";
import Home from "../Home/Home";
import Notifications from "../../common/components/Notification";
const Admin: React.FC = () => {
return (
<Fragment>
<Notifications />
<LeftMenu />
<div id="content-wrapper" className="d-flex flex-column">
<div id="content">
<TopMenu />
<div className="container-fluid">
<Switch>
<Route path={`/patients`}><Users /></Route>
<Route path={`/doctors`}><Products /></Route>
<Route path={`/reports`}><Orders /></Route>
<Route path={`/prescription`}><Prescription /></Route>
<Route path="/"><Home /></Route>
</Switch>
</div>
</div>
</div>
</Fragment>
);
};
export default Admin;
import React, { Fragment, Dispatch, useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { updateCurrentPath } from "../../store/actions/root.actions";
import TopCard from "../../common/components/TopCard";
import { IProductState, IStateType } from "../../store/models/root.interface";
import ProductList from "../Products/ProductsList";
import { IOrder } from "../../store/models/order.interface";
import OrderList from "../Orders/OrderList";
import axios from "axios";
const Home: React.FC = () => {
const products: IProductState = useSelector((state: IStateType) => state.products);
const numberItemsCount: number = products.products.length;
const totalPrice: number = products.products.reduce((prev, next) => prev + ((next.price * next.amount) || 0), 0);
const totalProductAmount: number = products.products.reduce((prev, next) => prev + (next.amount || 0), 0);
const orders: IOrder[] = useSelector((state: IStateType) => state.orders.orders);
const totalSales: number = orders.reduce((prev, next) => prev + next.totalPrice, 0);
const totalOrderAmount: number = orders.reduce((prev, next) => prev + next.amount, 0);
const email: string = useSelector((state: IStateType) => state.account.email);
const dispatch: Dispatch<any> = useDispatch();
dispatch(updateCurrentPath("home", ""));
const [resState, setresState] = useState(0);
const [appState, setappState] = useState(0);
let AppList = null
useEffect(() => {
var nameMatch = email.match(/^([^@]*)@/);
var name = nameMatch ? nameMatch[1] : null;
const article = { email : "dr.rasika" };
axios.post('http://127.0.0.1:8000/api/specificAppointment', article)
.then(response => {
AppList = response.data.map((Appointment: any) => {
return (
<tr className={`table-row`}
key={`order_${Appointment.id}`}>
<th scope="row">{Appointment.id}</th>
<td>{Appointment.fullName}</td>
<td>{Appointment.appointmentTime}</td>
</tr>);
})
setappState(response.data.length)
setresState(AppList)
});
},[]);
return (
<Fragment>
<h1 className="h3 mb-2 text-gray-800">Dashboard</h1>
<p className="mb-4">Summary and overview of our doctor stuff here</p>
<div className="row">
<TopCard title="DOCTORS COUNT" text={`${numberItemsCount}`} icon="box" class="primary" />
<TopCard title="APPONITMENT COUNT" text={`${appState}`} icon="warehouse" class="danger" />
<TopCard title="DOCTOR PRICE" text={`$${totalPrice}`} icon="dollar-sign" class="success" />
</div>
{/* <div className="row">
<TopCard title="EARNINGS" text={totalSales.toString()} icon="donate" class="primary" />
<TopCard title="TOTAL REPORT" text={totalOrderAmount.toString()} icon="calculator" class="danger" />
</div> */}
<div className="row">
<div className="col-xl-6 col-lg-6">
<div className="card shadow mb-4">
<div className="card-header py-3">
<h6 className="m-0 font-weight-bold text-green">Patient list</h6>
</div>
<div className="card-body">
<ProductList />
</div>
</div>
</div>
<div className="col-xl-6 col-lg-6">
<div className="card shadow mb-4">
<div className="card-header py-3">
<h6 className="m-0 font-weight-bold text-green">Appoiments</h6>
</div>
<div className="card-body">
<div className="table-responsive portlet">
<table className="table">
<thead className="thead-light">
<tr>
<th scope="col">#</th>
<th scope="col">Name</th>
<th scope="col">Time</th>
{/* <th scope="col">unit</th>
<th scope="col">Range</th> */}
</tr>
</thead>
<tbody>
{resState}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</Fragment>
);
};
export default Home;
import React, { Fragment, useState } from "react";
import { Link } from "react-router-dom";
const LeftMenu: React.FC = () => {
let [leftMenuVisibility, setLeftMenuVisibility] = useState(false);
function changeLeftMenuVisibility() {
setLeftMenuVisibility(!leftMenuVisibility);
}
function getCollapseClass() {
return (leftMenuVisibility) ? "" : "collapsed";
}
return (
<Fragment>
<div className="toggle-area">
<button className="btn btn-primary toggle-button" onClick={() => changeLeftMenuVisibility()}>
<i className="fas fa-bolt"></i>
</button>
</div>
<ul className={`navbar-nav bg-gradient-primary-green sidebar sidebar-dark accordion ${getCollapseClass()}`}
id="collapseMenu">
<a className="sidebar-brand d-flex align-items-center justify-content-center" href="index.html">
{/* <div className="sidebar-brand-icon icon-green rotate-n-15">
<i className="fas fa-bolt"></i>
</div> */}
<div className="sidebar-brand-text mx-3">RyHealthProfile</div>
</a>
<hr className="sidebar-divider my-0" />
<li className="nav-item active">
<Link className="nav-link" to="Home">
<i className="fas fa-fw fa-tachometer-alt"></i>
<span>Dashboard</span>
</Link>
</li>
<hr className="sidebar-divider" />
<div className="sidebar-heading">
Managment
</div>
<li className="nav-item">
<Link className="nav-link" to={`/doctors`}>
<i className="fas fa-fw fa-user"></i>
<span>Doctor Profiles</span>
</Link>
</li>
<li className="nav-item">
<Link className="nav-link" to={`/reports`}>
<i className="fas fa-book-open"></i>
<span>Lab Reports</span>
</Link>
</li>
<li className="nav-item">
<Link className="nav-link" to={`/prescription`}>
<i className="fas fa-book-open"></i>
<span>Prescription</span>
</Link>
</li>
<hr className="sidebar-divider" />
<div className="sidebar-heading">
Profile
</div>
<li className="nav-item">
<Link className="nav-link" to={`/patients`}>
<i className="fas fa-fw fa-user"></i>
<span>Patients</span>
</Link>
</li>
<hr className="sidebar-divider d-none d-md-block" />
</ul>
</Fragment>
);
};
export default LeftMenu;
import React, { useState, FormEvent, Fragment, Dispatch } from "react";
import { IProduct } from "../../store/models/product.interface";
import TextInput from "../../common/components/TextInput";
import NumberInput from "../../common/components/NumberInput";
import { OnChangeModel, IOrderFormState } from "../../common/types/Form.types";
import { useDispatch, useSelector } from "react-redux";
import { addOrder } from "../../store/actions/orders.actions";
import { addNotification } from "../../store/actions/notifications.action";
import { clearSelectedProduct, changeProductAmount } from "../../store/actions/products.action";
import { IOrder } from "../../store/models/order.interface";
import { IStateType } from "../../store/models/root.interface";
import axios from 'axios'
const OrderForm: React.FC = () => {
const dispatch: Dispatch<any> = useDispatch();
const selectedProduct: IProduct | null = useSelector((state: IStateType) => state.products.selectedProduct);
const orders: IOrder[] = useSelector((state: IStateType) => state.orders.orders);
const [selectedFile, setSelectedFile] = useState("");
const initialFormState: IOrderFormState = {
name: { error: "", value: "" },
result: { error: "", value: 0 },
unit: { error: "", value: "" },
refRange: { error: "", value: "" },
product: { error: "", value: null },
amount: { error: "", value: 0 },
totalPrice: { error: "", value: 0 },
};
const [formState, setFormState] = useState(initialFormState);
function hasAmountChanged(model: OnChangeModel): void {
let totalPrice: number = formState.totalPrice.value;
if (selectedProduct) {
totalPrice = selectedProduct.price * (model.value as number);
}
setFormState({
...formState,
amount: { error: model.error, value: model.value as number },
totalPrice: { error: model.error, value: totalPrice }
});
}
function hasFormValueChanged(model: OnChangeModel): void {
setFormState({ ...formState, [model.field]: { error: model.error, value: model.value } });
}
function resetForm(): void {
setFormState(initialFormState);
}
function saveOrder(e: FormEvent<HTMLFormElement>): void {
e.preventDefault();
if (isFormInvalid()) {
return;
}
saveForm(formState);
}
function saveForm(formState: IOrderFormState): void {
if (selectedProduct) {
if (selectedProduct.amount < formState.amount.value) {
alert("Not enough products in warehouse");
return;
}
formState.product.value = selectedProduct;
// dispatch(addOrder({
// id: 0,
// name: formState.name.value,
// amount: formState.amount.value,
// totalPrice: formState.totalPrice.value,
// product: formState.product.value as IProduct
// }));
// dispatch(addNotification("Order added", `Order ${formState.name.value} added by you`));
// dispatch(clearSelectedProduct());
// dispatch(changeProductAmount(selectedProduct.id, formState.amount.value));
resetForm();
}
}
function isFormInvalid(): boolean {
return (formState.amount.error || formState.totalPrice.error
|| formState.name.error || formState.product.error || !formState.name.value
|| !selectedProduct) as boolean;
}
function getDisabledClass(): string {
let isError: boolean = isFormInvalid();
return isError ? "disabled" : "";
}
const handleFileSelect = (selectorFiles:any) => {
console.log(selectorFiles[0])
setSelectedFile(selectorFiles[0])
// const formData = new FormData();
// formData.append("file", selectedFile);
// axios.post('http://127.0.0.1:8000/staffapi/labreport', formData, {headers: {"Content-type": "multipart/form-date"}})
// .then(response => {
// for (var key in response.data) {
// if (response.data.hasOwnProperty(key)) {
// console.log(key + " -> " + response.data[key]);
// dispatch(addOrder({
// id: parseInt(key),
// name: response.data[key].name,
// result: response.data[key].result,
// unit: response.data[key].unit,
// refRange: response.data[key].refRnage,
// product: null,
// amount: 0,
// totalPrice: 0,
// }));
// // dispatch(addNotification("Order added", `Order ${formState.name.value} added by you`));
// dispatch(clearSelectedProduct());
// // dispatch(changeProductAmount(selectedProduct.id, formState.amount.value));
// }
// }
// });
}
const Send = () => {
const formData = new FormData();
if(selectedFile != "")
{
formData.append("file", selectedFile);
axios.post('http://127.0.0.1:8000/staffapi/labreport', formData, {headers: {"Content-type": "multipart/form-date"}})
.then(response => {
for (var key in response.data) {
if (response.data.hasOwnProperty(key)) {
console.log(key + " -> " + response.data[key]);
dispatch(addOrder({
id: parseInt(key),
name: response.data[key].name,
result: response.data[key].result,
unit: response.data[key].unit,
refRange: response.data[key].refRnage,
product: null,
amount: 0,
totalPrice: 0,
}));
// dispatch(addNotification("Order added", `Order ${formState.name.value} added by you`));
dispatch(clearSelectedProduct());
// dispatch(changeProductAmount(selectedProduct.id, formState.amount.value));
}
}
});
// dispatch(addNotification("Sent", `Prescription sent to nearest Lab`));
}
}
return (
<Fragment>
<div className="card shadow mb-4">
<div className="card-header py-3">
<h6 className="m-0 font-weight-bold text-green">Upload</h6>
</div>
<div className="card-body">
<form onSubmit={saveOrder}>
<div className="form-row">
<input type="file" onChange={(e) => handleFileSelect(e.target.files)}/>
<button onClick={Send}>Send</button>
{/* <div className="form-group col-md-12">
<TextInput id="input_name"
value={formState.name.value}
field="name"
onChange={hasFormValueChanged}
required={true}
maxLength={20}
label="Name"
placeholder="Name" />
</div>
<div className="form-group col-md-6">
<NumberInput id="input_amount"
value={formState.amount.value}
field="amount"
onChange={hasAmountChanged}
max={1000}
min={0}
label="Amount" />
</div>
<div className="form-group col-md-6">
<NumberInput id="input_totalPrice"
value={formState.totalPrice.value}
field="totalPrice"
onChange={hasFormValueChanged}
max={1000}
min={0}
label="Price" />
</div> */}
</div>
{/* <button className="btn btn-danger" onClick={() => resetForm()}>Reset</button> */}
{/* <button type="submit" className={`btn btn-success left-margin ${getDisabledClass()}`}>Create</button> */}
</form>
</div>
</div>
</Fragment>
);
};
export default OrderForm;
import React, { } from "react";
import { useSelector } from "react-redux";
import { IOrder } from "../../store/models/order.interface";
import { IStateType } from "../../store/models/root.interface";
const OrderList: React.FC = () => {
const orders: IOrder[] = useSelector((state: IStateType) => state.orders.orders);
const orderList: JSX.Element[] = orders.map(order => {
return (
<tr className={`table-row`}
key={`order_${order.id}`}>
<th scope="row">{order.id}</th>
<td>{order.name}</td>
<td>{order.result}</td>
<td>{order.unit}</td>
<td>{order.refRange}</td>
</tr>);
})
return (
<div className="table-responsive portlet">
<table className="table">
<thead className="thead-light">
<tr>
<th scope="col">#</th>
<th scope="col">Name</th>
<th scope="col">Result</th>
<th scope="col">unit</th>
<th scope="col">Range</th>
</tr>
</thead>
<tbody>
{orderList}
</tbody>
</table>
</div>
)
}
export default OrderList;
\ No newline at end of file
import React, { Fragment, Dispatch } from "react";
import { useDispatch, useSelector } from "react-redux";
import { updateCurrentPath } from "../../store/actions/root.actions";
import { IOrder } from "../../store/models/order.interface";
import OrderList from "./OrderList";
import TopCard from "../../common/components/TopCard";
import OrderForm from "./OrderForm";
import ProductList from "../Products/ProductsList";
import { IProduct } from "../../store/models/product.interface";
import { changeSelectedProduct, clearSelectedProduct } from "../../store/actions/products.action";
import { IStateType } from "../../store/models/root.interface";
const Orders: React.FC = () => {
const dispatch: Dispatch<any> = useDispatch();
const orders: IOrder[] = useSelector((state: IStateType) => state.orders.orders);
const totalSales: number = orders.reduce((prev, next) => prev + next.totalPrice, 0);
const totalAmount: number = orders.reduce((prev, next) => prev + next.amount, 0);
dispatch(updateCurrentPath("orders", "list"));
dispatch(clearSelectedProduct());
function selectProduct(product: IProduct): void {
dispatch(changeSelectedProduct(product));
}
return (
<Fragment>
<h1 className="h3 mb-2 text-gray-800">Lab Reports</h1>
<p className="mb-4">Reports here</p>
{/* <div className="row">
<TopCard title="TOTAL SALES" text={totalSales.toString()} icon="donate" class="primary" />
<TopCard title="TOTAL AMOUNT" text={totalAmount.toString()} icon="calculator" class="danger" />
</div> */}
<div className="row">
<div className="col-md-6">
<OrderForm />
</div>
<div className="col-md-6">
</div>
</div>
<div className="row">
<div className="col-xl-12 col-lg-12">
<div className="card shadow mb-4">
<div className="card-header py-3">
<h6 className="m-0 font-weight-bold text-green">Report Results</h6>
<div className="header-buttons">
</div>
</div>
<div className="card-body">
<OrderList />
</div>
</div>
</div>
</div>
</Fragment>
)
}
export default Orders;
\ No newline at end of file
import React, { useState, FormEvent, Fragment, Dispatch } from "react";
import { IProduct } from "../../store/models/product.interface";
import TextInput from "../../common/components/TextInput";
import NumberInput from "../../common/components/NumberInput";
import { OnChangeModel, IOrderFormState } from "../../common/types/Form.types";
import { useDispatch, useSelector } from "react-redux";
import { addOrder } from "../../store/actions/orders.actions";
import { addNotification } from "../../store/actions/notifications.action";
import { clearSelectedProduct, changeProductAmount } from "../../store/actions/products.action";
import { IOrder } from "../../store/models/order.interface";
import { IStateType } from "../../store/models/root.interface";
import axios from 'axios'
const OrderForm: React.FC = () => {
const dispatch: Dispatch<any> = useDispatch();
const selectedProduct: IProduct | null = useSelector((state: IStateType) => state.products.selectedProduct);
const orders: IOrder[] = useSelector((state: IStateType) => state.orders.orders);
const [selectedFile, setSelectedFile] = useState("");
const initialFormState: IOrderFormState = {
name: { error: "", value: "" },
result: { error: "", value: 0 },
unit: { error: "", value: "" },
refRange: { error: "", value: "" },
product: { error: "", value: null },
amount: { error: "", value: 0 },
totalPrice: { error: "", value: 0 },
};
const [formState, setFormState] = useState(initialFormState);
function hasAmountChanged(model: OnChangeModel): void {
let totalPrice: number = formState.totalPrice.value;
if (selectedProduct) {
totalPrice = selectedProduct.price * (model.value as number);
}
setFormState({
...formState,
amount: { error: model.error, value: model.value as number },
totalPrice: { error: model.error, value: totalPrice }
});
}
function hasFormValueChanged(model: OnChangeModel): void {
setFormState({ ...formState, [model.field]: { error: model.error, value: model.value } });
}
function resetForm(): void {
setFormState(initialFormState);
}
function saveOrder(e: FormEvent<HTMLFormElement>): void {
e.preventDefault();
if (isFormInvalid()) {
return;
}
saveForm(formState);
}
function saveForm(formState: IOrderFormState): void {
if (selectedProduct) {
if (selectedProduct.amount < formState.amount.value) {
alert("Not enough products in warehouse");
return;
}
formState.product.value = selectedProduct;
// dispatch(addOrder({
// id: 0,
// name: formState.name.value,
// amount: formState.amount.value,
// totalPrice: formState.totalPrice.value,
// product: formState.product.value as IProduct
// }));
// dispatch(addNotification("Order added", `Order ${formState.name.value} added by you`));
// dispatch(clearSelectedProduct());
// dispatch(changeProductAmount(selectedProduct.id, formState.amount.value));
resetForm();
}
}
function isFormInvalid(): boolean {
return (formState.amount.error || formState.totalPrice.error
|| formState.name.error || formState.product.error || !formState.name.value
|| !selectedProduct) as boolean;
}
function getDisabledClass(): string {
let isError: boolean = isFormInvalid();
return isError ? "disabled" : "";
}
const handleFileSelect = (selectorFiles:any) => {
console.log(selectorFiles[0])
setSelectedFile(selectorFiles[0])
}
const Send = () => {
const formData = new FormData();
if(selectedFile != "")
{
// formData.append("file", selectedFile);
// axios.post('http://127.0.0.1:8000/staffapi/labreport', formData, {headers: {"Content-type": "multipart/form-date"}})
// .then(response => {
// for (var key in response.data) {
// if (response.data.hasOwnProperty(key)) {
// console.log(key + " -> " + response.data[key]);
// dispatch(addOrder({
// id: parseInt(key),
// name: response.data[key].name,
// result: response.data[key].result,
// unit: response.data[key].unit,
// refRange: response.data[key].refRnage,
// product: null,
// amount: 0,
// totalPrice: 0,
// }));
// // dispatch(addNotification("Order added", `Order ${formState.name.value} added by you`));
// dispatch(clearSelectedProduct());
// // dispatch(changeProductAmount(selectedProduct.id, formState.amount.value));
// }
// }
// });
dispatch(addNotification("Sent", `Prescription sent to nearest Lab`));
}
}
return (
<Fragment>
<div className="card shadow mb-4">
<div className="card-header py-3">
<h6 className="m-0 font-weight-bold text-green">Upload</h6>
</div>
<div className="card-body">
<form onSubmit={saveOrder}>
<div className="form-row">
<input type="file" onChange={(e) => handleFileSelect(e.target.files)}/>
<button onClick={Send}>Send</button>
{/* <div className="form-group col-md-12">
<TextInput id="input_name"
value={formState.name.value}
field="name"
onChange={hasFormValueChanged}
required={true}
maxLength={20}
label="Name"
placeholder="Name" />
</div>
<div className="form-group col-md-6">
<NumberInput id="input_amount"
value={formState.amount.value}
field="amount"
onChange={hasAmountChanged}
max={1000}
min={0}
label="Amount" />
</div>
<div className="form-group col-md-6">
<NumberInput id="input_totalPrice"
value={formState.totalPrice.value}
field="totalPrice"
onChange={hasFormValueChanged}
max={1000}
min={0}
label="Price" />
</div> */}
</div>
{/* <button className="btn btn-danger" onClick={() => resetForm()}>Reset</button> */}
{/* <button type="submit" className={`btn btn-success left-margin ${getDisabledClass()}`}>Create</button> */}
</form>
</div>
</div>
</Fragment>
);
};
export default OrderForm;
import React, { } from "react";
import { useSelector } from "react-redux";
import { IOrder } from "../../store/models/order.interface";
import { IStateType } from "../../store/models/root.interface";
const OrderList: React.FC = () => {
const orders: IOrder[] = useSelector((state: IStateType) => state.orders.orders);
const orderList: JSX.Element[] = orders.map(order => {
return (
<tr className={`table-row`}
key={`order_${order.id}`}>
<th scope="row">{order.id}</th>
<td>{order.name}</td>
<td>{order.result}</td>
<td>{order.unit}</td>
<td>{order.refRange}</td>
</tr>);
})
return (
<div className="table-responsive portlet">
<table className="table">
<thead className="thead-light">
<tr>
<th scope="col">#</th>
<th scope="col">Name</th>
<th scope="col">Result</th>
<th scope="col">unit</th>
<th scope="col">Range</th>
</tr>
</thead>
<tbody>
{orderList}
</tbody>
</table>
</div>
)
}
export default OrderList;
\ No newline at end of file
import React, { Fragment, Dispatch } from "react";
import { useDispatch, useSelector } from "react-redux";
import { updateCurrentPath } from "../../store/actions/root.actions";
import { IOrder } from "../../store/models/order.interface";
import OrderList from "./OrderList";
import TopCard from "../../common/components/TopCard";
import OrderForm from "./OrderForm";
import ProductList from "../Products/ProductsList";
import { IProduct } from "../../store/models/product.interface";
import { changeSelectedProduct, clearSelectedProduct } from "../../store/actions/products.action";
import { IStateType } from "../../store/models/root.interface";
const Prescription: React.FC = () => {
const dispatch: Dispatch<any> = useDispatch();
const orders: IOrder[] = useSelector((state: IStateType) => state.orders.orders);
const totalSales: number = orders.reduce((prev, next) => prev + next.totalPrice, 0);
const totalAmount: number = orders.reduce((prev, next) => prev + next.amount, 0);
dispatch(updateCurrentPath("orders", "list"));
dispatch(clearSelectedProduct());
function selectProduct(product: IProduct): void {
dispatch(changeSelectedProduct(product));
}
return (
<Fragment>
<h1 className="h3 mb-2 text-gray-800">Prescription</h1>
<p className="mb-4">Prescription here</p>
{/* <div className="row">
<TopCard title="TOTAL SALES" text={totalSales.toString()} icon="donate" class="primary" />
<TopCard title="TOTAL AMOUNT" text={totalAmount.toString()} icon="calculator" class="danger" />
</div> */}
<div className="row">
<div className="col-md-6">
<OrderForm />
</div>
<div className="col-md-6">
</div>
</div>
{/* <div className="row">
<div className="col-xl-12 col-lg-12">
<div className="card shadow mb-4">
<div className="card-header py-3">
<h6 className="m-0 font-weight-bold text-green">Report Results</h6>
<div className="header-buttons">
</div>
</div>
<div className="card-body">
<OrderList />
</div>
</div>
</div>
</div> */}
</Fragment>
)
}
export default Prescription;
\ No newline at end of file
.header-buttons {
position: absolute;
right: 10px;
top: 8px;
}
.btn-green {
background-color: cadetblue !important;
border-color: cadetblue !important;
margin-right: 3px;
width: 40px;
}
.btn-blue {
background-color: lightblue !important;
border-color: lightblue !important;
margin-right: 3px;
width: 40px;
}
.btn-red {
background-color: indianred !important;
border-color: indianred !important;
margin-right: 3px;
width: 40px;
}
.table-row {
cursor: pointer;
}
.selected {
background-color:lavender;
}
\ No newline at end of file
import React, { Fragment, Dispatch, useState, useEffect } from "react";
import ProductList from "./ProductsList";
import ProductForm from "./ProductsForm";
import TopCard from "../../common/components/TopCard";
import "./Products.css";
import { useDispatch, useSelector } from "react-redux";
import { updateCurrentPath } from "../../store/actions/root.actions";
import { IProductState, IStateType, IRootPageStateType } from "../../store/models/root.interface";
import Popup from "reactjs-popup";
import { removeProduct, clearSelectedProduct, setModificationState,
changeSelectedProduct } from "../../store/actions/products.action";
import { addNotification } from "../../store/actions/notifications.action";
import { ProductModificationStatus, IProduct } from "../../store/models/product.interface";
const Products: React.FC = () => {
const dispatch: Dispatch<any> = useDispatch();
const products: IProductState = useSelector((state: IStateType) => state.products);
const path: IRootPageStateType = useSelector((state: IStateType) => state.root.page);
const numberItemsCount: number = products.products.length;
const totalPrice: number = products.products.reduce((prev, next) => prev + next.price, 0);
const totalAmount: number = products.products.reduce((prev, next) => prev + (next.amount || 0), 0);
const [popup, setPopup] = useState(false);
useEffect(() => {
dispatch(clearSelectedProduct());
dispatch(updateCurrentPath("products", "list"));
}, [path.area, dispatch]);
function onProductSelect(product: IProduct): void {
dispatch(changeSelectedProduct(product));
dispatch(setModificationState(ProductModificationStatus.None));
}
function onProductRemove() {
if(products.selectedProduct) {
setPopup(true);
}
}
return (
<Fragment>
<h1 className="h3 mb-2 text-gray-800">Doctor Profile Managment</h1>
<p className="mb-4">Profiles here</p>
<div className="row">
<TopCard title="DOCTORS COUNT" text={`${numberItemsCount}`} icon="box" class="primary" />
{/* <TopCard title="AMOUNT" text={`${totalAmount}`} icon="warehouse" class="danger" /> */}
<TopCard title="DOCTOR'S FEE" text={`$${totalPrice}`} icon="dollar-sign" class="success" />
</div>
<div className="row">
<div className="col-xl-12 col-lg-12">
<div className="card shadow mb-4">
<div className="card-header py-3">
<h6 className="m-0 font-weight-bold text-green">List of Doctors</h6>
<div className="header-buttons">
<button className="btn btn-success btn-green" onClick={() =>
dispatch(setModificationState(ProductModificationStatus.Create))}>
<i className="fas fa fa-plus"></i>
</button>
<button className="btn btn-success btn-blue" onClick={() =>
dispatch(setModificationState(ProductModificationStatus.Edit))}>
<i className="fas fa fa-pen"></i>
</button>
<button className="btn btn-success btn-red" onClick={() => onProductRemove()}>
<i className="fas fa fa-times"></i>
</button>
</div>
</div>
<div className="card-body">
<ProductList
onSelect={onProductSelect}
/>
</div>
</div>
</div>
{((products.modificationState === ProductModificationStatus.Create)
|| (products.modificationState === ProductModificationStatus.Edit && products.selectedProduct)) ?
<ProductForm /> : null}
</div>
<Popup
className="popup-modal"
open={popup}
onClose={() => setPopup(false)}
closeOnDocumentClick
>
<div className="popup-modal">
<div className="popup-title">
Are you sure?
</div>
<div className="popup-content">
<button type="button"
className="btn btn-danger"
onClick={() => {
if (!products.selectedProduct) {
return;
}
dispatch(addNotification("Product removed", `Product ${products.selectedProduct.name} was removed`));
dispatch(removeProduct(products.selectedProduct.id));
dispatch(clearSelectedProduct());
setPopup(false);
}}>Remove
</button>
</div>
</div>
</Popup>
</Fragment >
);
};
export default Products;
import React, { useState, FormEvent, Dispatch, Fragment } from "react";
import { IStateType, IProductState } from "../../store/models/root.interface";
import { useSelector, useDispatch } from "react-redux";
import { IProduct, ProductModificationStatus } from "../../store/models/product.interface";
import TextInput from "../../common/components/TextInput";
import { editProduct, clearSelectedProduct, setModificationState, addProduct, removeProduct } from "../../store/actions/products.action";
import { addNotification } from "../../store/actions/notifications.action";
import NumberInput from "../../common/components/NumberInput";
import Checkbox from "../../common/components/Checkbox";
import SelectInput from "../../common/components/Select";
import { OnChangeModel, IProductFormState } from "../../common/types/Form.types";
import axios from "axios";
const ProductForm: React.FC = () => {
const dispatch: Dispatch<any> = useDispatch();
const products: IProductState | null = useSelector((state: IStateType) => state.products);
let product: IProduct | null = products.selectedProduct;
const isCreate: boolean = (products.modificationState === ProductModificationStatus.Create);
if (!product || isCreate) {
product = { id: 0, name: "", qualification: "", experience: 0, rating: 0, place: "", profile: "", description: "", hasExpiryDate: false, price: 0, amount: 0, category: ""};
}
const [formState, setFormState] = useState({
name: { error: "", value: product.name },
qualification: { error: "", value: product.qualification},
experience: { error: "", value: product.experience},
rating: { error: "", value: product.rating},
place: { error: "", value: product.place},
profile: { error: "", value: product.profile},
description: { error: "", value: product.description},
amount: { error: "", value: product.amount},
price: { error: "", value: product.price},
hasExpiryDate: { error: "", value: product.hasExpiryDate},
category: { error: "", value: product.category},
});
const [resState, setresState] = useState(0);
function hasFormValueChanged(model: OnChangeModel): void {
setFormState({ ...formState, [model.field]: { error: model.error, value: model.value } });
}
function saveUser(e: FormEvent<HTMLFormElement>): void {
e.preventDefault();
if (isFormInvalid()) {
return;
}
let saveUserFn: Function = (isCreate) ? addProduct : editProduct;
saveForm(formState, saveUserFn);
}
function saveForm(formState: IProductFormState, saveFn: Function): void {
if (product) {
if(products != null){
products.products.map(product => {
dispatch(removeProduct(product.id))
});
}
// dispatch(saveFn({
// ...product,
// name: formState.name.value,
// qualification: formState.qualification.value,
// experience: formState.experience.value,
// rating: formState.rating.value,
// place: formState.place.value,
// profile: formState.profile.value,
// description: formState.description.value,
// }));
const article = { Name : formState.name.value,
Qualification: formState.qualification.value,
Experience: formState.experience.value + " years experience",
Rating: formState.rating.value + "%",
Place: formState.place.value,
Profile: formState.profile.value
};
axios.post('http://127.0.0.1:8000/staffapi/salary', article)
.then(response => {
console.log(response.data)
axios.get(`http://localhost:8000/staffapi/salary`)
.then(res => {
for (let i = 0; i < res.data.length; i++) {
product = {
id: res.data[i].id,
name: res.data[i].Name,
qualification: res.data[i].Qualification,
experience: res.data[i].Experience,
rating: res.data[i].Rating,
place: res.data[i].Place,
profile: res.data[i].Profile,
hasExpiryDate: false,
price: 0,
amount: 0,
category: "",
description: ""
};
if(i == res.data.length-1){
console.log(response.data)
product.price = response.data
}
dispatch(addProduct(product))
}
});
});
dispatch(addNotification("Product edited", `Product ${formState.name.value} edited by you`));
dispatch(clearSelectedProduct());
dispatch(setModificationState(ProductModificationStatus.None));
// if(products != null){
// products.products.map(product => {
// console.log(product)
// dispatch(removeProduct(product.id))
// });
// }
}
}
function cancelForm(): void {
dispatch(setModificationState(ProductModificationStatus.None));
}
function getDisabledClass(): string {
let isError: boolean = isFormInvalid();
return isError ? "disabled" : "";
}
function isFormInvalid(): boolean {
return (formState.qualification.error || formState.experience.error
|| formState.name.error || formState.rating.error || formState.place.error
|| formState.profile.error || !formState.name.value) as boolean;
}
return (
<Fragment>
<div className="col-xl-7 col-lg-7">
<div className="card shadow mb-4">
<div className="card-header py-3">
<h6 className="m-0 font-weight-bold text-green">Product {(isCreate ? "create" : "edit")}</h6>
</div>
<div className="card-body">
<form onSubmit={saveUser}>
<div className="form-row">
<div className="form-group col-md-12">
<TextInput id="input_email"
value={formState.name.value}
field="name"
onChange={hasFormValueChanged}
required={true}
maxLength={20}
label="Name"
placeholder="Name" />
</div>
</div>
<div className="form-row">
<div className="form-group col-md-3">
<SelectInput
id="input_qualification"
field="qualification"
label="Qualification"
options={["BAMS", "MBBS", "BDS", "AFIH"]}
required={true}
onChange={hasFormValueChanged}
value={formState.qualification.value}
/>
</div>
<div className="form-group col-md-3">
<SelectInput
id="input_profile"
field="profile"
label="Profile"
options={["Ayurveda", "Homeopath", "Dentist", "General Medicine"]}
required={true}
onChange={hasFormValueChanged}
value={formState.profile.value}
/>
</div>
</div>
<div className="form-row">
<div className="form-group col-md-3">
<NumberInput id="input_amount"
value={formState.experience.value}
field="experience"
onChange={hasFormValueChanged}
max={100}
min={0}
label="Experience" />
</div>
<div className="form-group col-md-3">
<NumberInput id="input_price"
value={formState.rating.value}
field="rating"
onChange={hasFormValueChanged}
max={100}
min={0}
label="Rating" />
</div>
</div>
<div className="form-row">
<div className="form-group col-md-3">
<SelectInput
id="input_place"
field="place"
label="Place"
options={["Whitefield, Bangalore", "Bannerghatta Road, Bangalore", "Keelkattalai, Chennai", "Porur, Chennai"]}
required={true}
onChange={hasFormValueChanged}
value={formState.place.value}
/>
</div>
</div>
<div className="form-row">
<div className="form-group col-md-3">
<TextInput id="input_description"
field = "description"
value={formState.description.value}
onChange={hasFormValueChanged}
required={false}
maxLength={100}
label="Description"
placeholder="Description" />
</div>
</div>
{/* <div className="form-group">
<Checkbox
id="checkbox_expiry"
field="hasExpiryDate"
value={formState.hasExpiryDate.value}
label="Has expiry date"
onChange={hasFormValueChanged}
/>
</div> */}
<button className="btn btn-danger" onClick={() => cancelForm()}>Cancel</button>
<button type="submit" className={`btn btn-success left-margin ${getDisabledClass()}`}>Save</button>
</form>
</div>
</div>
</div>
</Fragment>
);
};
export default ProductForm;
import React, { Dispatch, useEffect} from "react";
import { useSelector, useDispatch } from "react-redux";
import { IStateType, IProductState } from "../../store/models/root.interface";
import { IProduct } from "../../store/models/product.interface";
import { addProduct, removeProduct } from "../../store/actions/products.action";
import axios from "axios";
export type productListProps = {
onSelect?: (product: IProduct) => void;
children?: React.ReactNode;
};
function ProductList(props: productListProps): JSX.Element {
const dispatch: Dispatch<any> = useDispatch();
let product: IProduct
const products: IProductState = useSelector((state: IStateType) => state.products);
useEffect(() => {
// Update the document title using the browser API
if(products != null){
products.products.map(product => {
// console.log(product)
dispatch(removeProduct(product.id))
});
}
axios.get(`http://localhost:8000/staffapi/salary`)
.then(res => {
for (let i = 0; i < res.data.length; i++) {
product = {
id: res.data[i].id,
name: res.data[i].Name,
qualification: res.data[i].Qualification,
experience: res.data[i].Experience,
rating: res.data[i].Rating,
place: res.data[i].Place,
profile: res.data[i].Profile,
hasExpiryDate: false,
price: 0,
amount: 0,
category: "",
description: ""
};
dispatch(addProduct(product))
}
});
},[]);
// axios.get(`http://localhost:8000/staffapi/salary`)
// .then(res => {
// for (let i = 0; i < res.data.length; i++) {
// product = {
// id: res.data[i].id,
// name: res.data[i].Name,
// qualification: res.data[i].Qualification,
// experience: res.data[i].Experience,
// rating: res.data[i].Rating,
// place: res.data[i].Place,
// profile: res.data[i].Profile,
// hasExpiryDate: false,
// price: 0,
// amount: 0,
// category: "",
// description: ""
// };
// console.log(product)
// dispatch(addProduct(product))
// }
// });
const productElements: (JSX.Element | null)[] = products.products.map(product => {
if (!product) { return null; }
return (<tr className={`table-row ${(products.selectedProduct && products.selectedProduct.id === product.id) ? "selected" : ""}`}
onClick={() => {
if(props.onSelect) props.onSelect(product);
}}
key={`product_${product.id}`}>
<th scope="row">{product.id}</th>
<td>{product.name}</td>
<td>{product.qualification}</td>
<td>{product.experience}</td>
<td>{product.place}</td>
</tr>);
});
return (
<div className="table-responsive portlet">
<table className="table">
<thead className="thead-light">
<tr>
<th scope="col">#</th>
<th scope="col">Name</th>
<th scope="col">Qualification</th>
<th scope="col">Experience</th>
<th scope="col">Place</th>
</tr>
</thead>
<tbody>
{productElements}
</tbody>
</table>
</div>
);
}
export default ProductList;
.white-breadcrumb {
background-color: white !important;
margin-top: 15px !important;
}
.portlet {
max-height: 350px;
}
\ No newline at end of file
import React from "react";
import TopMenuAccount from "./TopMenuAccount";
import "./TopMenu.css";
import { useSelector } from "react-redux";
import { IStateType, IRootPageStateType } from "../../store/models/root.interface";
const TopMenu: React.FC = () => {
const page: IRootPageStateType = useSelector((state: IStateType) => state.root.page);
return (
<nav className="navbar navbar-expand navbar-light bg-custom-dark topbar mb-4 static-top shadow">
{/* <ol className="breadcrumb dark-breadcrumb">
<li className="breadcrumb-item"><a href="# ">{page ? page.area : null}</a></li>
<li className="breadcrumb-item"><a href="# ">{page ? page.subArea : null}</a></li>
</ol> */}
<ul className="navbar-nav ml-auto">
<div className="topbar-divider d-none d-sm-block"></div>
<TopMenuAccount />
</ul>
</nav>
);
};
export default TopMenu;
import React, { useState, Dispatch } from "react";
import { useDispatch, useSelector } from "react-redux";
import { logout } from "../../store/actions/account.actions";
import { IStateType } from "../../store/models/root.interface";
function TopMenuAccount(): JSX.Element {
const dispatch: Dispatch<any> = useDispatch();
const email: string = useSelector((state: IStateType) => state.account.email);
const [isShow, setShow] = useState(false);
return (
<li className="nav-item dropdown no-arrow">
<a className="nav-link dropdown-toggle"
onClick={() => {
setShow(!isShow);
}}
href="# "
id="userDropdown"
role="button"
data-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false">
<span className="mr-2 d-none d-lg-inline small cadet">{email}</span>
<img className="img-profile rounded-circle" alt=""
src="https://source.unsplash.com/QAB-WJcbgJk/60x60" />
</a>
<div className={`dropdown-menu dropdown-menu-right shadow animated--grow-in ${(isShow) ? "show" : ""}`}
aria-labelledby="userDropdown">
<a className="dropdown-item"
onClick={() => dispatch(logout())}
href="# "
data-toggle="modal"
data-target="#logoutModal">
<i className="fas fa-sign-out-alt fa-sm fa-fw mr-2 text-gray-400"></i>
Logout
</a>
</div>
</li>
);
};
export default TopMenuAccount;
import React, { Fragment, Dispatch } from "react";
import TopCard from "../../common/components/TopCard";
import { IUser } from "../../store/models/user.interface";
import { useDispatch, useSelector } from "react-redux";
import { IStateType } from "../../store/models/root.interface";
import { addAdmin, removeAdmin } from "../../store/actions/users.action";
import { updateCurrentPath } from "../../store/actions/root.actions";
const Users: React.FC = () => {
const dispatch: Dispatch<any> = useDispatch();
dispatch(updateCurrentPath("user", "list"));
const users: IUser[] = useSelector((state: IStateType) => state.users.users);
const admins: IUser[] = useSelector((state: IStateType) => state.users.admins);
function setUserAdmin(user: IUser): void {
dispatch(addAdmin(user));
}
function setUserNotAdmin(admin: IUser): void {
dispatch(removeAdmin(admin));
}
const userElements: JSX.Element[] = users.map(user => {
return (
<tr className={`table-row`}
key={`user_${user.id}`}>
<th scope="row">{user.id}</th>
<td>{user.firstName}</td>
<td>{user.lastName}</td>
<td>{user.email}</td>
<td><button className="btn btn-success" onClick={() => setUserAdmin(user)}>Set patient</button> </td>
</tr>);
});
const adminElements: JSX.Element[] = admins.map(admin => {
return (
<tr className={`table-row`}
key={`user_${admin.id}`}>
<th scope="row">{admin.id}</th>
<td>{admin.firstName}</td>
<td>{admin.lastName}</td>
<td>{admin.email}</td>
<td><button className="btn btn-danger" onClick={() => setUserNotAdmin(admin)}>Revert patient8</button> </td>
</tr>);
});
return (
<Fragment>
<h1 className="h3 mb-2 text-gray-800">Users</h1>
<p className="mb-4">Users here</p>
<div className="row">
<TopCard title="ADMINS" text={admins.length.toString()} icon="user-tie" class="primary" />
<TopCard title="USER" text={users.length.toString()} icon="user" class="danger" />
</div>
<div className="row">
<div className="col-xl-12 col-lg-12">
<div className="card shadow mb-4">
<div className="card-header py-3">
<h6 className="m-0 font-weight-bold text-green">Approve Patient List</h6>
<div className="header-buttons">
</div>
</div>
<div className="card-body">
<div className="table-responsive portlet">
<table className="table">
<thead className="thead-light">
<tr>
<th scope="col">#</th>
<th scope="col">First name</th>
<th scope="col">Last name</th>
<th scope="col">Email</th>
<th scope="col">Admin</th>
</tr>
</thead>
<tbody>
{adminElements}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<div className="row">
<div className="col-xl-12 col-lg-12">
<div className="card shadow mb-4">
<div className="card-header py-3">
<h6 className="m-0 font-weight-bold text-green">Patient List</h6>
<div className="header-buttons">
</div>
</div>
<div className="card-body">
<div className="table-responsive portlet">
<table className="table">
<thead className="thead-light">
<tr>
<th scope="col">#</th>
<th scope="col">First name</th>
<th scope="col">Last name</th>
<th scope="col">Email</th>
<th scope="col">Admin</th>
</tr>
</thead>
<tbody>
{userElements}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</Fragment >
);
};
export default Users;
@import url('https://fonts.googleapis.com/css?family=Nunito:200,200i,300,300i,400,400i,600,600i,700,700i,800,800i,900,900i');
body {
margin: 0;
font-family: Nunito, -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
color: darkslategray !important;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
}
import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";
import * as serviceWorker from "./serviceWorker";
import "../node_modules/bootstrap/dist/css/bootstrap.min.css";
import "@fortawesome/fontawesome-free/css/all.min.css";
import {Provider} from "react-redux";
import store from "./store/store";
ReactDOM.render(<Provider store={store}><App /></Provider>, document.getElementById('root'));
// f you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 170.96 159.93"><defs><style>.cls-1{fill:#09d3ac;}</style></defs><path class="cls-1" d="M141.35,73.27c0-6.89-8.11-13-20.58-16.73,3-12.67,1.69-22.75-4.28-26.16a9.55,9.55,0,0,0-4.79-1.2c-5.57,0-12.61,3.89-19.72,10.62-7.11-6.68-14.13-10.55-19.69-10.55a9.46,9.46,0,0,0-4.86,1.22c-5.94,3.42-7.17,13.44-4.21,26.05-12.41,3.71-20.48,9.77-20.5,16.63s8.12,13,20.59,16.72c-3,12.68-1.7,22.75,4.28,26.16a9.41,9.41,0,0,0,4.78,1.2c5.58,0,12.62-3.89,19.73-10.62,7.1,6.68,14.12,10.55,19.69,10.55a9.59,9.59,0,0,0,4.86-1.22c5.94-3.42,7.16-13.44,4.21-26C133.27,86.18,141.34,80.12,141.35,73.27ZM96.56,42.06c8.19-7.33,13.31-8.12,15.13-8.12h0a4.71,4.71,0,0,1,2.42.58c2.86,1.63,4,7.38,3.09,15a56.73,56.73,0,0,1-1,5.79,97.7,97.7,0,0,0-12.58-2.07,98.47,98.47,0,0,0-8.24-10.08C95.75,42.79,96.15,42.42,96.56,42.06ZM71.77,78.94c.78,1.5,1.61,3,2.47,4.51S76,86.49,77,88c-2.71-.39-5.34-.88-7.84-1.46C69.86,84,70.75,81.5,71.77,78.94ZM69,59.9c2.53-.59,5.18-1.08,7.93-1.46-1,1.48-1.86,3-2.76,4.59s-1.69,3-2.46,4.52Q70.18,63.65,69,59.9Zm5.21,13.34q1.86-3.93,4.09-7.86c1.5-2.62,3.11-5.17,4.77-7.61,2.91-.22,5.91-.34,9-.33s6,.12,8.89.35c1.66,2.42,3.25,5,4.75,7.55s2.88,5.22,4.12,7.83c-1.23,2.62-2.6,5.25-4.08,7.85s-3.11,5.18-4.77,7.62c-2.91.23-5.91.34-9,.34s-6-.13-8.89-.36c-1.66-2.42-3.26-4.94-4.76-7.55S75.5,75.85,74.25,73.24Zm32.86-14.77c2.72.38,5.35.87,7.84,1.45-.74,2.47-1.62,5-2.64,7.55-.79-1.5-1.61-3-2.48-4.51S108,59.93,107.11,58.47Zm2.76,24.92q1.29-2.27,2.46-4.53c1,2.6,2,5.16,2.7,7.66-2.52.59-5.17,1.07-7.92,1.45Q108.52,85.75,109.87,83.39ZM92,46.56c1.8,1.92,3.57,4,5.3,6.23-1.71-.07-3.46-.12-5.23-.12s-3.58,0-5.33.12C88.45,50.57,90.2,48.48,92,46.56Zm-22.18-12A4.82,4.82,0,0,1,72.29,34a13.11,13.11,0,0,1,5.19,1.31,39.07,39.07,0,0,1,10,6.78l1.17,1.07a98.49,98.49,0,0,0-8.16,10,98.88,98.88,0,0,0-12.65,2.06c-.44-1.94-.8-3.84-1-5.67C65.8,42,67,36.24,69.81,34.6ZM64.53,85.26a58.75,58.75,0,0,1-5.54-2c-7.1-3-11.5-6.85-11.5-10.14S51.91,66,59,63.05a57.63,57.63,0,0,1,5.44-1.94A97.93,97.93,0,0,0,69,73.25,98.72,98.72,0,0,0,64.53,85.26Zm23,19.1c-8.19,7.33-13.31,8.11-15.14,8.11a4.69,4.69,0,0,1-2.42-.58c-2.86-1.63-4-7.38-3.09-15a56.07,56.07,0,0,1,1-5.78,99.51,99.51,0,0,0,12.58,2.06,97.17,97.17,0,0,0,8.24,10.08Zm4.57-4.51c-1.8-1.92-3.57-4-5.31-6.23,1.72.08,3.47.12,5.24.12s3.58,0,5.33-.11C95.63,95.85,93.87,97.93,92.09,99.85Zm22.18,12a4.82,4.82,0,0,1-2.48.59c-1.82,0-7-.8-15.16-8.1l-1.17-1.07a98.44,98.44,0,0,0,8.15-10,97,97,0,0,0,12.66-2.06c.44,1.94.79,3.84,1,5.67C118.27,104.42,117.12,110.18,114.27,111.81Zm10.8-28.44c-1.71.7-3.52,1.35-5.44,1.93a98.54,98.54,0,0,0-4.57-12.14,98.1,98.1,0,0,0,4.49-12,58.75,58.75,0,0,1,5.54,2c7.09,3,11.5,6.85,11.49,10.14S132.17,80.42,125.07,83.37ZM92,82.39a9.18,9.18,0,1,0-9.17-9.19A9.17,9.17,0,0,0,92,82.39ZM31,17.88V128.53H153.07V17.88ZM148.3,123.77H35.78V22.65H148.3Zm-85-33.9c-3,12.68-1.7,22.75,4.28,26.16a9.41,9.41,0,0,0,4.78,1.2c5.58,0,12.62-3.89,19.73-10.62,7.1,6.68,14.12,10.55,19.69,10.55a9.59,9.59,0,0,0,4.86-1.22c5.94-3.42,7.16-13.44,4.21-26,12.41-3.72,20.48-9.78,20.49-16.63s-8.11-13-20.58-16.73c3-12.67,1.69-22.75-4.28-26.16a9.55,9.55,0,0,0-4.79-1.2c-5.57,0-12.61,3.89-19.72,10.62-7.11-6.68-14.13-10.55-19.69-10.55a9.46,9.46,0,0,0-4.86,1.22c-5.94,3.42-7.17,13.44-4.21,26.05-12.41,3.71-20.48,9.77-20.5,16.63S50.84,86.13,63.31,89.87Zm24.21,14.49c-8.19,7.33-13.31,8.11-15.14,8.11a4.69,4.69,0,0,1-2.42-.58c-2.86-1.63-4-7.38-3.09-15a56.07,56.07,0,0,1,1-5.78,99.51,99.51,0,0,0,12.58,2.06,97.17,97.17,0,0,0,8.24,10.08Zm24.79-36.89c-.79-1.5-1.61-3-2.48-4.51s-1.8-3-2.72-4.49c2.72.38,5.35.87,7.84,1.45C114.21,62.39,113.33,64.92,112.31,67.47Zm2.72,19c-2.52.59-5.17,1.07-7.92,1.45q1.41-2.22,2.76-4.58t2.46-4.53C113.37,81.46,114.28,84,115,86.52Zm-5.21-13.35c-1.23,2.62-2.6,5.25-4.08,7.85s-3.11,5.18-4.77,7.62c-2.91.23-5.91.34-9,.34s-6-.13-8.89-.36c-1.66-2.42-3.26-4.94-4.76-7.55s-2.87-5.22-4.12-7.83q1.86-3.93,4.09-7.86c1.5-2.62,3.11-5.17,4.77-7.61,2.91-.22,5.91-.34,9-.33s6,.12,8.89.35c1.66,2.42,3.25,5,4.75,7.55S108.58,70.56,109.82,73.17ZM77,88c-2.71-.39-5.34-.88-7.84-1.46.74-2.46,1.63-5,2.65-7.55.78,1.5,1.61,3,2.47,4.51S76,86.49,77,88ZM74.21,63c-.87,1.5-1.69,3-2.46,4.52Q70.18,63.65,69,59.9c2.53-.59,5.18-1.08,7.93-1.46C76,59.92,75.11,61.45,74.21,63ZM92.09,99.85c-1.8-1.92-3.57-4-5.31-6.23,1.72.08,3.47.12,5.24.12s3.58,0,5.33-.11C95.63,95.85,93.87,97.93,92.09,99.85Zm22.18,12a4.82,4.82,0,0,1-2.48.59c-1.82,0-7-.8-15.16-8.1l-1.17-1.07a98.44,98.44,0,0,0,8.15-10,97,97,0,0,0,12.66-2.06c.44,1.94.79,3.84,1,5.67C118.27,104.42,117.12,110.18,114.27,111.81Zm5.28-50.66a58.75,58.75,0,0,1,5.54,2c7.09,3,11.5,6.85,11.49,10.14s-4.41,7.16-11.51,10.11c-1.71.7-3.52,1.35-5.44,1.93a98.54,98.54,0,0,0-4.57-12.14A98.1,98.1,0,0,0,119.55,61.15Zm-23-19.09c8.19-7.33,13.31-8.12,15.13-8.12h0a4.71,4.71,0,0,1,2.42.58c2.86,1.63,4,7.38,3.09,15a56.73,56.73,0,0,1-1,5.79,97.7,97.7,0,0,0-12.58-2.07,98.47,98.47,0,0,0-8.24-10.08C95.75,42.79,96.15,42.42,96.56,42.06ZM92,46.56c1.8,1.92,3.57,4,5.3,6.23-1.71-.07-3.46-.12-5.23-.12s-3.58,0-5.33.12C88.45,50.57,90.2,48.48,92,46.56Zm-22.18-12A4.82,4.82,0,0,1,72.29,34a13.11,13.11,0,0,1,5.19,1.31,39.07,39.07,0,0,1,10,6.78l1.17,1.07a98.49,98.49,0,0,0-8.16,10,98.88,98.88,0,0,0-12.65,2.06c-.44-1.94-.8-3.84-1-5.67C65.8,42,67,36.24,69.81,34.6ZM59,63.05a57.63,57.63,0,0,1,5.44-1.94A97.93,97.93,0,0,0,69,73.25a98.72,98.72,0,0,0-4.49,12,58.75,58.75,0,0,1-5.54-2c-7.1-3-11.5-6.85-11.5-10.14S51.91,66,59,63.05Zm33,1a9.18,9.18,0,1,0,9.17,9.19A9.17,9.17,0,0,0,92,64Zm0,0a9.18,9.18,0,1,0,9.17,9.19A9.17,9.17,0,0,0,92,64Zm0,0a9.18,9.18,0,1,0,9.17,9.19A9.17,9.17,0,0,0,92,64Zm49.35,9.24c0-6.89-8.11-13-20.58-16.73,3-12.67,1.69-22.75-4.28-26.16a9.55,9.55,0,0,0-4.79-1.2c-5.57,0-12.61,3.89-19.72,10.62-7.11-6.68-14.13-10.55-19.69-10.55a9.46,9.46,0,0,0-4.86,1.22c-5.94,3.42-7.17,13.44-4.21,26.05-12.41,3.71-20.48,9.77-20.5,16.63s8.12,13,20.59,16.72c-3,12.68-1.7,22.75,4.28,26.16a9.41,9.41,0,0,0,4.78,1.2c5.58,0,12.62-3.89,19.73-10.62,7.1,6.68,14.12,10.55,19.69,10.55a9.59,9.59,0,0,0,4.86-1.22c5.94-3.42,7.16-13.44,4.21-26C133.27,86.18,141.34,80.12,141.35,73.27ZM96.56,42.06c8.19-7.33,13.31-8.12,15.13-8.12h0a4.71,4.71,0,0,1,2.42.58c2.86,1.63,4,7.38,3.09,15a56.73,56.73,0,0,1-1,5.79,97.7,97.7,0,0,0-12.58-2.07,98.47,98.47,0,0,0-8.24-10.08C95.75,42.79,96.15,42.42,96.56,42.06ZM71.77,78.94c.78,1.5,1.61,3,2.47,4.51S76,86.49,77,88c-2.71-.39-5.34-.88-7.84-1.46C69.86,84,70.75,81.5,71.77,78.94ZM69,59.9c2.53-.59,5.18-1.08,7.93-1.46-1,1.48-1.86,3-2.76,4.59s-1.69,3-2.46,4.52Q70.18,63.65,69,59.9Zm5.21,13.34q1.86-3.93,4.09-7.86c1.5-2.62,3.11-5.17,4.77-7.61,2.91-.22,5.91-.34,9-.33s6,.12,8.89.35c1.66,2.42,3.25,5,4.75,7.55s2.88,5.22,4.12,7.83c-1.23,2.62-2.6,5.25-4.08,7.85s-3.11,5.18-4.77,7.62c-2.91.23-5.91.34-9,.34s-6-.13-8.89-.36c-1.66-2.42-3.26-4.94-4.76-7.55S75.5,75.85,74.25,73.24Zm32.86-14.77c2.72.38,5.35.87,7.84,1.45-.74,2.47-1.62,5-2.64,7.55-.79-1.5-1.61-3-2.48-4.51S108,59.93,107.11,58.47Zm2.76,24.92q1.29-2.27,2.46-4.53c1,2.6,2,5.16,2.7,7.66-2.52.59-5.17,1.07-7.92,1.45Q108.52,85.75,109.87,83.39ZM92,46.56c1.8,1.92,3.57,4,5.3,6.23-1.71-.07-3.46-.12-5.23-.12s-3.58,0-5.33.12C88.45,50.57,90.2,48.48,92,46.56Zm-22.18-12A4.82,4.82,0,0,1,72.29,34a13.11,13.11,0,0,1,5.19,1.31,39.07,39.07,0,0,1,10,6.78l1.17,1.07a98.49,98.49,0,0,0-8.16,10,98.88,98.88,0,0,0-12.65,2.06c-.44-1.94-.8-3.84-1-5.67C65.8,42,67,36.24,69.81,34.6ZM64.53,85.26a58.75,58.75,0,0,1-5.54-2c-7.1-3-11.5-6.85-11.5-10.14S51.91,66,59,63.05a57.63,57.63,0,0,1,5.44-1.94A97.93,97.93,0,0,0,69,73.25,98.72,98.72,0,0,0,64.53,85.26Zm23,19.1c-8.19,7.33-13.31,8.11-15.14,8.11a4.69,4.69,0,0,1-2.42-.58c-2.86-1.63-4-7.38-3.09-15a56.07,56.07,0,0,1,1-5.78,99.51,99.51,0,0,0,12.58,2.06,97.17,97.17,0,0,0,8.24,10.08Zm4.57-4.51c-1.8-1.92-3.57-4-5.31-6.23,1.72.08,3.47.12,5.24.12s3.58,0,5.33-.11C95.63,95.85,93.87,97.93,92.09,99.85Zm22.18,12a4.82,4.82,0,0,1-2.48.59c-1.82,0-7-.8-15.16-8.1l-1.17-1.07a98.44,98.44,0,0,0,8.15-10,97,97,0,0,0,12.66-2.06c.44,1.94.79,3.84,1,5.67C118.27,104.42,117.12,110.18,114.27,111.81Zm10.8-28.44c-1.71.7-3.52,1.35-5.44,1.93a98.54,98.54,0,0,0-4.57-12.14,98.1,98.1,0,0,0,4.49-12,58.75,58.75,0,0,1,5.54,2c7.09,3,11.5,6.85,11.49,10.14S132.17,80.42,125.07,83.37ZM92,82.39a9.18,9.18,0,1,0-9.17-9.19A9.17,9.17,0,0,0,92,82.39Zm-69.32,54.5V26.2L17.89,31V141.66H139.94l4.78-4.77Z"/></svg>
\ No newline at end of file
/// <reference types="react-scripts" />
// This optional code is used to register a service worker.
// register() is not called by default.
// This lets the app load faster on subsequent visits in production, and gives
// it offline capabilities. However, it also means that developers (and users)
// will only see deployed updates on subsequent visits to a page, after all the
// existing tabs open on the page have been closed, since previously cached
// resources are updated in the background.
// To learn more about the benefits of this model and instructions on how to
// opt-in, read https://bit.ly/CRA-PWA
const isLocalhost = Boolean(
window.location.hostname === 'localhost' ||
// [::1] is the IPv6 localhost address.
window.location.hostname === '[::1]' ||
// 127.0.0.1/8 is considered localhost for IPv4.
window.location.hostname.match(
/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
)
);
type Config = {
onSuccess?: (registration: ServiceWorkerRegistration) => void;
onUpdate?: (registration: ServiceWorkerRegistration) => void;
};
export function register(config?: Config) {
if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
// The URL constructor is available in all browsers that support SW.
const publicUrl = new URL(
(process as { env: { [key: string]: string } }).env.PUBLIC_URL,
window.location.href
);
if (publicUrl.origin !== window.location.origin) {
// Our service worker won't work if PUBLIC_URL is on a different origin
// from what our page is served on. This might happen if a CDN is used to
// serve assets; see https://github.com/facebook/create-react-app/issues/2374
return;
}
window.addEventListener('load', () => {
const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
if (isLocalhost) {
// This is running on localhost. Let's check if a service worker still exists or not.
checkValidServiceWorker(swUrl, config);
// Add some additional logging to localhost, pointing developers to the
// service worker/PWA documentation.
navigator.serviceWorker.ready.then(() => {
console.log(
'This web app is being served cache-first by a service ' +
'worker. To learn more, visit https://bit.ly/CRA-PWA'
);
});
} else {
// Is not localhost. Just register service worker
registerValidSW(swUrl, config);
}
});
}
}
function registerValidSW(swUrl: string, config?: Config) {
navigator.serviceWorker
.register(swUrl)
.then(registration => {
registration.onupdatefound = () => {
const installingWorker = registration.installing;
if (installingWorker == null) {
return;
}
installingWorker.onstatechange = () => {
if (installingWorker.state === 'installed') {
if (navigator.serviceWorker.controller) {
// At this point, the updated precached content has been fetched,
// but the previous service worker will still serve the older
// content until all client tabs are closed.
console.log(
'New content is available and will be used when all ' +
'tabs for this page are closed. See https://bit.ly/CRA-PWA.'
);
// Execute callback
if (config && config.onUpdate) {
config.onUpdate(registration);
}
} else {
// At this point, everything has been precached.
// It's the perfect time to display a
// "Content is cached for offline use." message.
console.log('Content is cached for offline use.');
// Execute callback
if (config && config.onSuccess) {
config.onSuccess(registration);
}
}
}
};
};
})
.catch(error => {
console.error('Error during service worker registration:', error);
});
}
function checkValidServiceWorker(swUrl: string, config?: Config) {
// Check if the service worker can be found. If it can't reload the page.
fetch(swUrl)
.then(response => {
// Ensure service worker exists, and that we really are getting a JS file.
const contentType = response.headers.get('content-type');
if (
response.status === 404 ||
(contentType != null && contentType.indexOf('javascript') === -1)
) {
// No service worker found. Probably a different app. Reload the page.
navigator.serviceWorker.ready.then(registration => {
registration.unregister().then(() => {
window.location.reload();
});
});
} else {
// Service worker found. Proceed as normal.
registerValidSW(swUrl, config);
}
})
.catch(() => {
console.log(
'No internet connection found. App is running in offline mode.'
);
});
}
export function unregister() {
if ('serviceWorker' in navigator) {
navigator.serviceWorker.ready.then(registration => {
registration.unregister();
});
}
}
export const LOG_IN: string = "LOG_IN";
export const LOG_OUT: string = "LOG_OUT";
export function login(email: string): ILogInActionType {
return { type: LOG_IN, email: email };
}
export function logout(): ILogOutActionType {
return { type: LOG_OUT};
}
interface ILogInActionType { type: string, email: string };
interface ILogOutActionType { type: string };
export const ADD_NOTIFICATION: string = "ADD_NOTIFICATION";
export const REMOVE_NOTIFICATION: string = "REMOVE_NOTIFICATION";
export function addNotification(title: string, text: string): IAddNotificationActionType {
return { type: ADD_NOTIFICATION, text: text, title: title };
}
export function removeNotification(id: number): IRemoveNotificationActionType {
return { type: REMOVE_NOTIFICATION, id: id };
}
interface IAddNotificationActionType { type: string, text: string, title: string };
interface IRemoveNotificationActionType { type: string, id: number };
import { IOrder } from "../models/order.interface";
export const ADD_ORDER: string = "ADD_ORDER";
export function addOrder(order: IOrder): IAddOrderActionType {
return { type: ADD_ORDER, order: order };
}
interface IAddOrderActionType { type: string, order: IOrder };
import { IProduct, ProductModificationStatus } from "../models/product.interface";
export const ADD_PRODUCT: string = "ADD_PRODUCT";
export const EDIT_PRODUCT: string = "EDIT_PRODUCT";
export const REMOVE_PRODUCT: string = "REMOVE_PRODUCT";
export const CHANGE_PRODUCT_AMOUNT: string = "CHANGE_PRODUCT_AMOUNT";
export const CHANGE_PRODUCT_PENDING_EDIT: string = "CHANGE_PRODUCT_PENDING_EDIT";
export const CLEAR_PRODUCT_PENDING_EDIT: string = "CLEAR_PRODUCT_PENDING_EDIT";
export const SET_MODIFICATION_STATE: string = "SET_MODIFICATION_STATE";
export function addProduct(product: IProduct): IAddProductActionType {
return { type: ADD_PRODUCT, product: product };
}
export function editProduct(product: IProduct): IEditProductActionType {
return { type: EDIT_PRODUCT, product: product };
}
export function removeProduct(id: number): IRemoveProductActionType {
return { type: REMOVE_PRODUCT, id: id };
}
export function changeProductAmount(id: number, amount: number): IChangeProductAmountType {
return { type: CHANGE_PRODUCT_AMOUNT, id: id, amount: amount };
}
export function changeSelectedProduct(product: IProduct): IChangeSelectedProductActionType {
return { type: CHANGE_PRODUCT_PENDING_EDIT, product: product };
}
export function clearSelectedProduct(): IClearSelectedProductActionType {
return { type: CLEAR_PRODUCT_PENDING_EDIT };
}
export function setModificationState(value: ProductModificationStatus): ISetModificationStateActionType {
return { type: SET_MODIFICATION_STATE, value: value };
}
interface IAddProductActionType { type: string, product: IProduct };
interface IEditProductActionType { type: string, product: IProduct };
interface IRemoveProductActionType { type: string, id: number };
interface IChangeSelectedProductActionType { type: string, product: IProduct };
interface IClearSelectedProductActionType { type: string };
interface ISetModificationStateActionType { type: string, value: ProductModificationStatus};
interface IChangeProductAmountType {type: string, id: number, amount: number};
\ No newline at end of file
export const UPDATE_CURRENT_PATH: string = "UPDATE_CURRENT_PATH";
export function updateCurrentPath(area: string, subArea: string): IUpdateCurrentPathActionType {
return { type: UPDATE_CURRENT_PATH, area: area, subArea: subArea };
}
interface IUpdateCurrentPathActionType { type: string, area: string, subArea: string };
\ No newline at end of file
import { IUser } from "../models/user.interface";
export const ADD_ADMIN: string = "ADD_ADMIN";
export const REMOVE_ADMIN: string = "REMOVE_ADMIN";
export function addAdmin(user: IUser): IAddAdminActionType {
return { type: ADD_ADMIN, user: user };
}
export function removeAdmin(user: IUser): IRemoveAdminActionType {
return { type: REMOVE_ADMIN, user: user };
}
interface IAddAdminActionType { type: string, user: IUser };
interface IRemoveAdminActionType { type: string, user: IUser };
export interface IAccount {
email: string;
}
\ No newline at end of file
export interface INotification {
id: number,
date: Date,
title: string,
text: string
}
\ No newline at end of file
import { IProduct } from "./product.interface";
export interface IOrder {
id: number;
name: string;
result: number;
unit: string;
refRange: string;
product: IProduct | null;
amount: number;
totalPrice: number;
}
\ No newline at end of file
export interface IProduct {
id: number;
name: string;
qualification: string;
experience: number;
rating: number;
place: string;
profile: string
description: string;
hasExpiryDate: boolean;
price: number;
amount: number;
category: string;
}
export enum ProductModificationStatus {
None = 0,
Create = 1,
Edit = 2
}
\ No newline at end of file
import { IProduct, ProductModificationStatus } from "./product.interface";
import { INotification } from "./notification.interface";
import { IUser } from "./user.interface";
import { IOrder } from "./order.interface";
import { IAccount } from "./account.interface";
export interface IRootPageStateType {
area: string;
subArea: string;
}
export interface IRootStateType {
page: IRootPageStateType;
}
export interface IStateType {
root: IRootStateType;
products: IProductState;
notifications: INotificationState;
users: IUserState;
orders: IOrdersState;
account: IAccount;
}
export interface IProductState {
products: IProduct[];
selectedProduct: IProduct | null;
modificationState: ProductModificationStatus;
}
export interface IActionBase {
type: string;
[prop: string]: any;
}
export interface IOrdersState {
orders: IOrder[];
}
export interface INotificationState {
notifications: INotification[];
}
export interface IUserState {
users: IUser[];
admins: IUser[];
}
\ No newline at end of file
export interface IUser {
id: number;
firstName: string;
lastName: string;
email: string;
}
\ No newline at end of file
import { IActionBase } from "../models/root.interface";
import { IAccount } from "../models/account.interface";
import { LOG_IN, LOG_OUT } from "../actions/account.actions";
const initialState: IAccount = {
email: "admin@react-template.pl"
};
function accountReducer(state: IAccount = initialState, action: IActionBase): IAccount {
switch (action.type) {
case LOG_IN: {
return { ...state, email: (action.email)};
}
case LOG_OUT: {
return { ...state, email: ""};
}
default:
return state;
}
}
export default accountReducer;
\ No newline at end of file
import { IActionBase, INotificationState } from "../models/root.interface";
import { ADD_NOTIFICATION, REMOVE_NOTIFICATION } from "../actions/notifications.action";
const initialState: INotificationState = {
notifications: [{id: 1, date: new Date(), text: "Hello new user", title: "Welcome"}]
};
function notificationReducer(state: INotificationState = initialState, action: IActionBase): INotificationState {
switch (action.type) {
case ADD_NOTIFICATION: {
let maxId: number= Math.max.apply(Math, state.notifications.map(o => o.id));
if(maxId === -Infinity) { maxId = 0; }
let newItem = {
id: maxId + 1,
date: new Date(),
title: action.title,
text: action.text
};
return {...state, notifications: [...state.notifications, newItem]};
}
case REMOVE_NOTIFICATION: {
return {...state, notifications: state.notifications
.filter(Notification => Notification.id !== action.id)};
}
default:
return state;
}
}
export default notificationReducer;
\ No newline at end of file
import { IOrdersState, IActionBase } from "../models/root.interface";
import { ADD_ORDER } from "../actions/orders.actions";
const initialState: IOrdersState = {
orders: [
// {
// id: 1,
// name: "Apple order",
// amount: 12,
// totalPrice: 100,
// product: {
// id: 2, name: "Apple", description: "This is Apple and it is healthy",
// amount: 5, price: 2, hasExpiryDate: true, category: "Fruit"
// },
// },
// {
// id: 2,
// name: "Straw order",
// amount: 7,
// totalPrice: 7,
// product: {
// id: 3, name: "Straw", description: "This is Straw and you can use it for your drink",
// amount: 100, price: 1, hasExpiryDate: false, category: "Kitchen"
// },
// }
]
};
function orderReducer(state: IOrdersState = initialState, action: IActionBase): IOrdersState {
switch (action.type) {
case ADD_ORDER: {
let maxId: number = Math.max.apply(Math, state.orders.map((o) => { return o.id; }));
if(maxId === -Infinity) { maxId = 0; }
return {...state, orders: [...state.orders, {...action.order, id: maxId + 1}]};
}
default:
return state;
}
}
export default orderReducer;
\ No newline at end of file
import { IProductState, IActionBase } from "../models/root.interface";
import { ADD_PRODUCT, CHANGE_PRODUCT_PENDING_EDIT, EDIT_PRODUCT, REMOVE_PRODUCT,
CLEAR_PRODUCT_PENDING_EDIT, SET_MODIFICATION_STATE, CHANGE_PRODUCT_AMOUNT} from "../actions/products.action";
import { IProduct, ProductModificationStatus } from "../models/product.interface";
const initialState: IProductState = {
modificationState: ProductModificationStatus.None,
selectedProduct: null,
products: [
// {
// id: 1, name: "Chocolate", description: "This is Chocolate and it is Sweet",
// amount: 10, price: 4, hasExpiryDate: true, category: "Sweet"
// },
// {
// id: 2, name: "Apple", description: "This is Apple and it is healthy",
// amount: 5, price: 2, hasExpiryDate: true, category: "Fruit"
// },
// {
// id: 3, name: "Straw", description: "This is Straw and you can use it for your drink",
// amount: 100, price: 1, hasExpiryDate: false, category: "Kitchen"
// },
// {
// id: 4, name: "Spoon", description: "This is Spoon and it is useful while eating",
// amount: 3, price: 2, hasExpiryDate: false, category: "Kitchen"
// },
// {
// id: 5, name: "Sugar", description: "This is Sugar and it is to make your life sweet",
// amount: 15, price: 5, hasExpiryDate: true, category: "Sweet"
// }
]
};
function productsReducer(state: IProductState = initialState, action: IActionBase): IProductState {
switch (action.type) {
case ADD_PRODUCT: {
let maxId: number = state.products.length //Math.max.apply(Math, state.products.map(function(o) { return o.id; }));
action.product.id = state.products.length+1;
return { ...state, products: [...state.products, action.product]};
}
case EDIT_PRODUCT: {
const foundIndex: number = state.products.findIndex(pr => pr.id === action.product.id);
let products: IProduct[] = state.products;
products[foundIndex] = action.product;
return { ...state, products: products };
}
case REMOVE_PRODUCT: {
return { ...state, products: state.products.filter(pr => pr.id !== action.id) };
}
case CHANGE_PRODUCT_PENDING_EDIT: {
return { ...state, selectedProduct: action.product };
}
case CLEAR_PRODUCT_PENDING_EDIT: {
return { ...state, selectedProduct: null };
}
case SET_MODIFICATION_STATE: {
return { ...state, modificationState: action.value };
}
case CHANGE_PRODUCT_AMOUNT: {
const foundIndex: number = state.products.findIndex(pr => pr.id === action.id);
let products: IProduct[] = state.products;
products[foundIndex].amount = products[foundIndex].amount - action.amount;
return { ...state, products: products };
}
default:
return state;
}
}
export default productsReducer;
\ No newline at end of file
import { combineReducers, Reducer } from "redux";
import { UPDATE_CURRENT_PATH } from "../actions/root.actions";
import { IRootStateType, IActionBase, IStateType } from "../models/root.interface";
import productsReducer from "./products.reducer";
import notificationReducer from "./notification.reducer";
import userReducer from "./users.reducer";
import orderReducer from "./order.reducer";
import accountReducer from "./account.reducer";
const initialState: IRootStateType = {
page: {area: "home", subArea: ""}
};
function rootReducer(state: IRootStateType = initialState, action: IActionBase): IRootStateType {
switch (action.type) {
case UPDATE_CURRENT_PATH:
return { ...state, page: {area: action.area, subArea: action.subArea}};
default:
return state;
}
}
const rootReducers: Reducer<IStateType> = combineReducers({root: rootReducer,
products: productsReducer,
notifications: notificationReducer,
users: userReducer,
orders: orderReducer,
account: accountReducer
});
export default rootReducers;
\ No newline at end of file
import { IUserState, IActionBase } from "../models/root.interface";
import { ADD_ADMIN, REMOVE_ADMIN } from "../actions/users.action";
const initialState: IUserState = {
users: [
{ id: 1, firstName: "John", lastName: "Smith", email: "jsmith@em.pl", },
{ id: 2, firstName: "Jannice", lastName: "Bing", email: "ohmy@fr.pl" }
],
admins: [
{ id: 3, firstName: "Jannet", lastName: "Crock", email: "jcrock@em.pl" },
]
};
function userReducer(state: IUserState = initialState, action: IActionBase): IUserState {
switch (action.type) {
case ADD_ADMIN: {
return { ...state, users: state.users.filter(x=>x.id !== action.user.id), admins: [...state.admins, action.user]};
}
case REMOVE_ADMIN: {
return { ...state, admins: state.admins.filter(x=>x.id !== action.user.id), users: [...state.users, action.user]};
}
default:
return state;
}
}
export default userReducer;
\ No newline at end of file
import { createStore, applyMiddleware, Store } from "redux";
import thunkMiddleware from "redux-thunk";
import rootReducers from "./reducers/root.reducer";
const store: Store = createStore(rootReducers, applyMiddleware(
thunkMiddleware
));
store.subscribe(() => {});
export default store;
\ No newline at end of file
This diff is collapsed.
This source diff could not be displayed because it is too large. You can view the blob instead.
{
"compilerOptions": {
"target": "es5",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react"
},
"include": [
"src"
]
}
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment