init melon_fe

This commit is contained in:
Ruidy 2021-07-12 09:11:01 +02:00
commit d14a380276
19 changed files with 3219 additions and 0 deletions

292
.gitignore vendored Normal file
View file

@ -0,0 +1,292 @@
### Python template
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
.htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
### macOS template
# General
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
### Node template
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
prod.env
.env
.env.test
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Next.js build output
.next
out
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*
.idea/
tmp
*.pdf
tmp.html

34
README.md Normal file
View file

@ -0,0 +1,34 @@
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
## Getting Started
First, run the development server:
```bash
npm run dev
# or
yarn dev
```
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
You can start editing the page by modifying `pages/index.tsx`. The page auto-updates as you edit the file.
[API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.ts`.
The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages.
## Learn More
To learn more about Next.js, take a look at the following resources:
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
## Deploy on Vercel
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.

45
api/index.ts Normal file
View file

@ -0,0 +1,45 @@
import axios from "axios";
import { Bill, BillForm } from "../types/bill";
const BASE_URL = "http://localhost:8000/api/v1/";
const client = axios.create({ baseURL: BASE_URL });
export const createBill = async (data: BillForm) => {
try {
const { data: response } = await client.post("/", JSON.stringify(data));
return response;
} catch (error) {
console.error(error);
return { error };
}
};
export const fetchOneBill = async (id: number) => {
try {
const { data } = await client.get<Bill>(`/${id}`);
return data;
} catch (error) {
console.error(error);
return {} as Bill;
}
};
export const fetchAllBills = async () => {
try {
const { data } = await client.get<Bill[]>("/");
return data;
} catch (error) {
console.error(error);
return [] as Bill[];
}
};
export const sendBillAsPDF = async (id: number) => {
try {
const { data } = await client.post(`/${id}/send`);
return data;
} catch (error) {
console.error(error);
}
};

View file

@ -0,0 +1,34 @@
import { Form, Select } from "antd";
import { Controller } from "react-hook-form";
type InputSelectProps = {
control: any;
name: string;
label: string;
placeholder: string;
options: string[];
};
export const InputSelect = ({ control, name, label, placeholder, options }: InputSelectProps) => (
<Controller
control={control}
name={name}
render={({ field }) => (
<Form.Item label={label}>
<Select
showSearch
placeholder={placeholder}
optionFilterProp="children"
filterOption={(input, option) =>
option?.children.toLowerCase().indexOf(input.toLowerCase()) >= 0
}
options={options.map((label, value) => ({
value,
label,
}))}
{...field}
/>
</Form.Item>
)}
/>
);

7
components/list.tsx Normal file
View file

@ -0,0 +1,7 @@
import { ReactNode } from "react";
type ListProps<T> = { items: T[]; render: (item: T) => ReactNode };
export default function List<T>({ items, render }: ListProps<T>) {
return <ul>{items.map((item) => render(item))}</ul>;
}

47
layouts/main.tsx Normal file
View file

@ -0,0 +1,47 @@
import { Layout, Menu, Row } from "antd";
import Link from "next/link";
import { useRouter } from "next/router";
const { Header, Content, Footer } = Layout;
export const withLayout =
(Component: (arg: any) => JSX.Element) =>
({ ...props }: any) => {
const { route } = useRouter();
const menuItems = [
{ label: "Home", path: "/" },
{ label: "Bills", path: "/bills" },
];
return (
<>
<Layout style={{ minHeight: "100vh" }}>
<Header>
<Row>
<span style={{ color: "white", paddingRight: "16px" }}>🍉 Melon</span>
<Menu
theme="dark"
mode="horizontal"
selectedKeys={[
(menuItems.findIndex((item) => item.path === route) + 1).toString(),
]}
>
{menuItems.map(({ label, path }, index) => (
<Menu.Item key={index + 1}>
<Link href={path}>
<a>{label}</a>
</Link>
</Menu.Item>
))}
</Menu>
</Row>
</Header>
<Content style={{ padding: "20px 50px" }}>
<Component {...props} />
</Content>
<Footer style={{ textAlign: "center" }}>🍉 Melon - Property Management</Footer>
</Layout>
</>
);
};

3
next-env.d.ts vendored Normal file
View file

@ -0,0 +1,3 @@
/// <reference types="next" />
/// <reference types="next/types/global" />
/// <reference types="next/image-types/global" />

23
package.json Normal file
View file

@ -0,0 +1,23 @@
{
"name": "frontend",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start"
},
"dependencies": {
"antd": "^4.16.6",
"axios": "^0.21.1",
"next": "^11.0.1",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-hook-form": "^7.8.0"
},
"devDependencies": {
"@types/react": "^17.0.11",
"prettier": "^2.2.1",
"typescript": "^4.3.4"
}
}

6
pages/_app.tsx Normal file
View file

@ -0,0 +1,6 @@
import "../styles/globals.css";
import { AppProps } from "next/app";
export default function MyApp({ Component, pageProps }: AppProps) {
return <Component {...pageProps} />;
}

37
pages/bills.tsx Normal file
View file

@ -0,0 +1,37 @@
import { Card, List } from "antd";
import { GetStaticProps } from "next";
import Link from "next/link";
import { fetchAllBills } from "../api";
import { withLayout } from "../layouts/main";
import { Bill } from "../types/bill";
type BillsProps = { bills: Bill[] };
export const getStaticProps: GetStaticProps = async () => {
const bills = await fetchAllBills();
return { props: { bills } };
};
const BillsPage = ({ bills }: BillsProps) => (
<section>
<h1>All bills</h1>
<List
grid={{ gutter: 16, column: 4 }}
pagination={{ pageSize: 12 }}
dataSource={bills}
renderItem={(bill) => (
<Link href={`/bills/${bill.id}`}>
<a>
<List.Item>
<Card title={bill.id} bordered={false}>
<p>{bill.name}</p>
</Card>
</List.Item>
</a>
</Link>
)}
/>
</section>
);
export default withLayout(BillsPage);

56
pages/bills/[id].tsx Normal file
View file

@ -0,0 +1,56 @@
import { Button, message, Space, Typography } from "antd";
import { GetStaticPaths, GetStaticProps } from "next";
import { useState } from "react";
import { fetchAllBills, fetchOneBill, sendBillAsPDF } from "../../api";
import { withLayout } from "../../layouts/main";
import { Bill } from "../../types/bill";
type BillProps = { bill: Bill };
type QueryParams = { id: string };
export const getStaticPaths: GetStaticPaths<QueryParams> = async () => {
const bills = await fetchAllBills();
const paths = bills.map(({ id }) => ({
params: { id: id.toString() },
}));
return { paths, fallback: false };
};
export const getStaticProps: GetStaticProps<BillProps, QueryParams> = async ({ params }) => {
const billID = parseInt(params!.id);
const bill = await fetchOneBill(billID);
return { props: { bill } };
};
const BillPage = ({ bill }: BillProps) => {
const [sent, setSent] = useState(false);
const handleSendPDF = (id: number) => {
sendBillAsPDF(id);
message.success("The bill will be sent to the customer");
setSent(() => true);
};
return (
<main>
<Typography.Title>Facture #VFNI{`${bill.number}`}</Typography.Title>
<Space>
<Button type="primary" onClick={() => handleSendPDF(bill.id)} disabled={sent}>
{sent ? "The bill is on its way to your customer" : "Send Bill"}
</Button>
</Space>
<Space>
<Typography.Text>{bill?.name}</Typography.Text>
<Typography.Text>{bill?.price} </Typography.Text>
<Typography.Text>
from {bill?.start} to {bill?.end}
</Typography.Text>
</Space>
</main>
);
};
export default withLayout(BillPage);

105
pages/index.tsx Normal file
View file

@ -0,0 +1,105 @@
import { Button, Form, Input, Switch } from "antd";
import { Controller, useForm } from "react-hook-form";
import { createBill } from "../api";
import { InputSelect } from "../components/inputSelect";
import { withLayout } from "../layouts/main";
import { BillForm } from "../types/bill";
const Home = () => {
const { register, handleSubmit, control } = useForm<BillForm>();
const onSubmit = handleSubmit((data) => createBill(data));
return (
<main>
<section>
<h1>Create a new bill</h1>
<Form layout="vertical" onFinish={onSubmit}>
<Form.Item label="Customer name">
<Input placeholder="Cameron Doe" {...register("name")} />
</Form.Item>
<Form.Item label="Customer phone number">
<Input placeholder="06 12 34 56 78" {...register("phoneNumber")} />
</Form.Item>
<Form.Item label="Price">
<Input placeholder="75.00" addonAfter="€" {...register("price")} />
</Form.Item>
<Form.Item label="Start">
<Input type="date" {...register("start")} />
</Form.Item>
<Form.Item label="End">
<Input type="date" {...register("end")} />
</Form.Item>
<InputSelect
control={control}
name="room"
label="Room"
placeholder="Choose a room to stay"
options={["T2 Corail", "T3 Azur"]}
/>
<Form.Item label="Customers number">
<Input type="number" {...register("customers")} placeholder="1" />
</Form.Item>
<InputSelect
control={control}
name="platform"
label="Booking Platform"
placeholder="Select a booking platform"
options={["Site", "Booking.com", "AirBnB", "TripAdvisor", "Perso"]}
/>
<Controller
control={control}
name="taxes"
defaultValue={true}
render={({ field }) => {
const { value, ...props } = field;
return (
<Form.Item label="Include taxes">
<Switch
checkedChildren="yes"
unCheckedChildren="no"
defaultChecked
checked={value}
{...props}
/>
</Form.Item>
);
}}
/>
<InputSelect
control={control}
name="paymentMethod"
label="Payment method"
placeholder="How do you want to pay?"
options={["Card", "Cash", "Cheque", "Transfer"]}
/>
<InputSelect
control={control}
name="paymentStatus"
label="Payment status"
placeholder="What is the current payment status?"
options={["Pending", "Canceled", "Completed", "Refunded"]}
/>
<Form.Item>
<Button type="primary" htmlType="submit">
Submit
</Button>
</Form.Item>
</Form>
</section>
</main>
);
};
export default withLayout(Home);

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

4
public/vercel.svg Normal file
View file

@ -0,0 +1,4 @@
<svg width="283" height="64" viewBox="0 0 283 64" fill="none"
xmlns="http://www.w3.org/2000/svg">
<path d="M141.04 16c-11.04 0-19 7.2-19 18s8.96 18 20 18c6.67 0 12.55-2.64 16.19-7.09l-7.65-4.42c-2.02 2.21-5.09 3.5-8.54 3.5-4.79 0-8.86-2.5-10.37-6.5h28.02c.22-1.12.35-2.28.35-3.5 0-10.79-7.96-17.99-19-17.99zm-9.46 14.5c1.25-3.99 4.67-6.5 9.45-6.5 4.79 0 8.21 2.51 9.45 6.5h-18.9zM248.72 16c-11.04 0-19 7.2-19 18s8.96 18 20 18c6.67 0 12.55-2.64 16.19-7.09l-7.65-4.42c-2.02 2.21-5.09 3.5-8.54 3.5-4.79 0-8.86-2.5-10.37-6.5h28.02c.22-1.12.35-2.28.35-3.5 0-10.79-7.96-17.99-19-17.99zm-9.45 14.5c1.25-3.99 4.67-6.5 9.45-6.5 4.79 0 8.21 2.51 9.45 6.5h-18.9zM200.24 34c0 6 3.92 10 10 10 4.12 0 7.21-1.87 8.8-4.92l7.68 4.43c-3.18 5.3-9.14 8.49-16.48 8.49-11.05 0-19-7.2-19-18s7.96-18 19-18c7.34 0 13.29 3.19 16.48 8.49l-7.68 4.43c-1.59-3.05-4.68-4.92-8.8-4.92-6.07 0-10 4-10 10zm82.48-29v46h-9V5h9zM36.95 0L73.9 64H0L36.95 0zm92.38 5l-27.71 48L73.91 5H84.3l17.32 30 17.32-30h10.39zm58.91 12v9.69c-1-.29-2.06-.49-3.2-.49-5.81 0-10 4-10 10V51h-9V17h9v9.2c0-5.08 5.91-9.2 13.2-9.2z" fill="#000"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

1
styles/globals.css Normal file
View file

@ -0,0 +1 @@
@import "antd/dist/antd.css";

22
tsconfig.json Normal file
View file

@ -0,0 +1,22 @@
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noImplicitReturns": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve"
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"]
}

56
types/bill.ts Normal file
View file

@ -0,0 +1,56 @@
export type BillForm = {
name: string;
phoneNumber: string;
price: number;
start: Date;
end: Date;
room: Room;
customers: number;
platform: Platform;
taxes: boolean;
paymentMethod: PaymentMethod;
paymentStatus: PaymentStatus;
};
export interface Bill {
id: number;
phoneNumber: number;
number: number;
name: string;
price: number;
start: Date;
end: Date;
room: Room;
customers: number;
platform: Platform;
taxes: boolean;
paymentMethod: PaymentMethod;
paymentStatus: PaymentStatus;
}
export enum Room {
t2,
t3,
}
export enum Platform {
web,
booking,
airbnb,
tripadvisor,
perso,
}
export enum PaymentMethod {
card,
cash,
cheque,
transfer,
}
export enum PaymentStatus {
pending,
canceled,
completed,
refund,
}

5
types/property.ts Normal file
View file

@ -0,0 +1,5 @@
export default interface Property {
id: string;
name: string;
address: string;
}

2442
yarn.lock Normal file

File diff suppressed because it is too large Load diff