first commit
This commit is contained in:
38
src/components/App.js
Normal file
38
src/components/App.js
Normal file
@@ -0,0 +1,38 @@
|
||||
import { h } from 'preact';
|
||||
import { Router } from 'preact-router';
|
||||
import ContextsProviders from '../Contexts';
|
||||
import Header from './Header';
|
||||
|
||||
|
||||
// Code-splitting is automated for `routes` directory
|
||||
import Home from '../routes/Home';
|
||||
import Plant from '../routes/Plant';
|
||||
import Profile from '../routes/Profile';
|
||||
import {useEffect} from "preact/hooks";
|
||||
|
||||
const App = () => {
|
||||
|
||||
useEffect(() => {
|
||||
if (!Notification) {
|
||||
alert('Le navigateur ne supporte pas les notifications.');
|
||||
} else if (Notification.permission !== 'granted') {
|
||||
Notification.requestPermission();
|
||||
}
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div id="app" class="h-screen overflow-auto flex flex-col">
|
||||
<ContextsProviders>
|
||||
<Header />
|
||||
<main className="flex-1 dark:bg-gray-800 dark:text-white">
|
||||
<Router>
|
||||
<Home path="/" />
|
||||
<Plant path="plant/:id" />
|
||||
<Profile path="/profile" />
|
||||
</Router>
|
||||
</main>
|
||||
</ContextsProviders>
|
||||
</div>
|
||||
)}
|
||||
|
||||
export default App;
|
||||
10
src/components/Button.js
Normal file
10
src/components/Button.js
Normal file
@@ -0,0 +1,10 @@
|
||||
import { classNames } from "../utilities/classNames"
|
||||
|
||||
export const Button = ({ children, className = "", type = "text", ...props }) => {
|
||||
|
||||
return (
|
||||
<button type={type} className={classNames("border px-2 py-1 shadow rounded", className)} {...props}>
|
||||
{children}
|
||||
</button>
|
||||
)
|
||||
}
|
||||
59
src/components/Form.js
Normal file
59
src/components/Form.js
Normal file
@@ -0,0 +1,59 @@
|
||||
import {classNames} from "../utilities/classNames";
|
||||
|
||||
|
||||
export const InputField = ({children, name, type = "text", ...props}) => {
|
||||
|
||||
const id = props.id ?? name
|
||||
const classStyle = props.className ?? ''
|
||||
|
||||
if (props.className) {
|
||||
delete props.className
|
||||
}
|
||||
|
||||
return <fieldset className={classNames(classStyle)}>
|
||||
{children && <label htmlFor={id} className="block text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||
{ children }
|
||||
</label>}
|
||||
<input id={id}
|
||||
name={name}
|
||||
type={type}
|
||||
className="focus:ring-indigo-500 focus:border-indigo-500 block w-full px-2 py-1 mt-1 sm:text-sm border border-gray-300 rounded-md dark:bg-gray-500"
|
||||
{...props}/>
|
||||
</fieldset>
|
||||
}
|
||||
|
||||
export const TextAreaField = ({children, name, ...props}) => {
|
||||
|
||||
const id = props.id ?? name
|
||||
const classStyle = props.className ?? ''
|
||||
|
||||
if (props.className) {
|
||||
delete props.className
|
||||
}
|
||||
|
||||
return <fieldset className={classNames(classStyle)}>
|
||||
{children && <label htmlFor={id} className="block text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||
{ children }
|
||||
</label>}
|
||||
<textarea id={id}
|
||||
name={name}
|
||||
className="focus:ring-indigo-500 focus:border-indigo-500 block w-full px-2 py-1 mt-1 sm:text-sm border border-gray-300 dark:bg-gray-500 rounded-md"
|
||||
{...props}
|
||||
/>
|
||||
</fieldset>
|
||||
}
|
||||
|
||||
export const SelectField = ({children, name, options, className = '', ...props}) => {
|
||||
|
||||
const id = props.id ?? name
|
||||
|
||||
return <fieldset className={className}>
|
||||
{children && <label htmlFor={id} className="block text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||
{ children }
|
||||
</label>}
|
||||
<select id={id} name={name} className="focus:ring-indigo-500 focus:border-indigo-500 block w-full px-2 py-1 mt-1 sm:text-sm border border-gray-300 dark:bg-gray-500 rounded-md" {...props}>
|
||||
{options.map((option, index) => <option key={index} value={option}
|
||||
className="capitalize">{option}</option>)}
|
||||
</select>
|
||||
</fieldset>
|
||||
}
|
||||
29
src/components/Header.js
Normal file
29
src/components/Header.js
Normal file
@@ -0,0 +1,29 @@
|
||||
import { h } from "preact"
|
||||
import { Link } from "preact-router/match"
|
||||
|
||||
const Header = () => {
|
||||
return (
|
||||
<header class="bg-green-700 text-white flex justify-between items-center p-2 text-lg">
|
||||
<Link href="/" class="font-bold text-xl">
|
||||
Plantes
|
||||
</Link>
|
||||
<nav class="flex">
|
||||
<NavLink path="/profile">Me</NavLink>
|
||||
</nav>
|
||||
</header>
|
||||
)
|
||||
}
|
||||
|
||||
export default Header
|
||||
|
||||
const NavLink = ({ path, children }) => {
|
||||
return (
|
||||
<Link
|
||||
href={path}
|
||||
activeClassName="font-bold bg-green-800"
|
||||
class="py-1 px-2 rounded"
|
||||
>
|
||||
{children}
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
28
src/components/Modals.js
Normal file
28
src/components/Modals.js
Normal file
@@ -0,0 +1,28 @@
|
||||
import { classNames } from "../utilities/classNames"
|
||||
|
||||
export const Modal = ({children, isOpen, customClose = false, ...props}) => {
|
||||
|
||||
const handleClose = e => props.onChange(e)
|
||||
|
||||
return <>
|
||||
{isOpen &&
|
||||
<div className="overlay fixed block top-0 bottom-0 left-0 right-0 z-10 bg-gray-800 bg-opacity-80" onClick={handleClose}>
|
||||
<div className="h-full flex justify-center items-center">
|
||||
<div className="flex flex-col bg-white dark:bg-gray-700 dark:text-white rounded p-2 min-h-48 min-w-48" {...props}>
|
||||
<div className="flex-1">
|
||||
{children}
|
||||
</div>
|
||||
{!customClose &&<div className="flex justify-end">
|
||||
<button type="button" className="bg-gray-300 dark:bg-gray-600 hover:bg-gray-400 hover:dark:bg-gray-800 px-2 py-1 rounded shadow close-button" onClick={handleClose}>Close</button>
|
||||
</div>}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
}
|
||||
</>
|
||||
}
|
||||
|
||||
export const ModalTitle = ({children, ...props}) => {
|
||||
return <div className="bg-green-700 text-white text-2xl font-bold text-center -mx-2 -mt-2 p-2 rounded-tl rounded-tr" {...props}>{children}</div>
|
||||
}
|
||||
9
src/components/PageLayout.js
Normal file
9
src/components/PageLayout.js
Normal file
@@ -0,0 +1,9 @@
|
||||
import { classNames } from "../utilities/classNames";
|
||||
|
||||
|
||||
export const PageLayout = ({children, ...props}) => {
|
||||
|
||||
return <div class={classNames("p-2", props.class ?? "")}>
|
||||
{children}
|
||||
</div>
|
||||
}
|
||||
17
src/components/Plants.js
Normal file
17
src/components/Plants.js
Normal file
@@ -0,0 +1,17 @@
|
||||
import { Link } from "preact-router/match"
|
||||
import {getPicture} from "../utilities/pictures";
|
||||
|
||||
export const PlantThumb = ({ plant, children }) => {
|
||||
|
||||
return <Link href={`/plant/${plant.id}`} class="block h-48">
|
||||
<div className="bg-green-400 relative rounded shadow-lg flex flex-col">
|
||||
<img src={getPicture(plant.id)} alt="" className="object-cover h-48 w-full rounded" />
|
||||
<div className="bg-green-700 text-white p-2 text-center absolute bottom-0 w-full rounded-bl rounded-br">
|
||||
{children}
|
||||
</div>
|
||||
<div title="Actions" className="absolute right-2 top-2 rounded-full flex justify-center items-center bg-green-700 w-6 h-6">
|
||||
{plant.actions.length}
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
}
|
||||
27
src/components/Tasks.js
Normal file
27
src/components/Tasks.js
Normal file
@@ -0,0 +1,27 @@
|
||||
import {Button} from "./Button"
|
||||
import {useContext} from "preact/hooks"
|
||||
import {PlantsContext} from "../Contexts"
|
||||
|
||||
export const Tasks = () => {
|
||||
|
||||
const { plants, doneTask, history } = useContext(PlantsContext)
|
||||
|
||||
const taskIsRequired = (action) => {
|
||||
if (history()[action.id]) {
|
||||
let lastTask = new Date(history()[action.id])
|
||||
return lastTask.addDays(Number(action.frequency)) < (new Date())
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
return <div className="mb-5">
|
||||
<h1>Tasks</h1>
|
||||
<div>
|
||||
{plants.map(plant => plant.actions.filter(action => taskIsRequired(action)).map(action => <div className="flex items-center gap-2">
|
||||
<span className="capitalize"><b>{plant.name}</b> {action.action_type}</span>
|
||||
<span> every {action.frequency} days {action.id}</span>
|
||||
<span><Button onClick={() => doneTask(action.id)}>Done</Button></span>
|
||||
</div>))}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
Reference in New Issue
Block a user