add arhived
This commit is contained in:
@@ -10,10 +10,10 @@ export default function ContextsProviders({children}) {
|
||||
|
||||
const [data, setData] = useLocalStorage('data', {})
|
||||
const [user, setUser] = useUser(data, setData)
|
||||
const {plants, addPlant, editPlant, removePlant, addAction, doneTask, history} = usePlants(data, setData)
|
||||
const {plants, addPlant, editPlant, removePlant, addAction, doneTask, history, archivedEntries} = usePlants(data, setData)
|
||||
|
||||
return <UserContext.Provider value={[user, setUser]}>
|
||||
<PlantsContext.Provider value={{plants, addPlant, editPlant, removePlant, addAction, doneTask, history}}>
|
||||
<PlantsContext.Provider value={{plants, addPlant, editPlant, removePlant, addAction, doneTask, history, archivedEntries}}>
|
||||
{children}
|
||||
</PlantsContext.Provider>
|
||||
</UserContext.Provider>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useEffect, useState } from "preact/hooks"
|
||||
import {route} from "preact-router";
|
||||
import {actionId} from "../utilities/actions";
|
||||
|
||||
const usePlants = (data, setData) => {
|
||||
const [plants, setPlants] = useState([])
|
||||
@@ -42,32 +43,28 @@ const usePlants = (data, setData) => {
|
||||
setData({...data, plants: plants})
|
||||
}
|
||||
|
||||
const doneTask = (actionId) => {
|
||||
const doneTask = (action) => {
|
||||
let history = data.history ?? {}
|
||||
history[actionId] = new Date()
|
||||
setData({...data, history: history})
|
||||
notifyMe()
|
||||
history[action] = new Date()
|
||||
|
||||
let archived = data.archived ?? []
|
||||
archived.push({
|
||||
plantId: Number(action.split('-')[1]),
|
||||
action: actionId(action.split('-')[0]),
|
||||
time: (new Date()).toSQLDate()
|
||||
})
|
||||
|
||||
setData({...data, history: history, archived: archived})
|
||||
}
|
||||
|
||||
const history = () => data.history ?? {}
|
||||
|
||||
const notifyMe = () => {
|
||||
if (!('Notification' in window)) {
|
||||
alert('Ce navigateur ne prend pas en charge la notification de bureau')
|
||||
} else if (Notification.permission === 'granted') {
|
||||
// Si tout va bien, créons une notification
|
||||
const notification = new Notification('Salut toi!')
|
||||
} else if (Notification.permission !== 'denied') {
|
||||
Notification.requestPermission().then((permission) => {
|
||||
// Si l'utilisateur accepte, créons une notification
|
||||
if (permission === 'granted') {
|
||||
const notification = new Notification('Salut toi!')
|
||||
}
|
||||
})
|
||||
}
|
||||
const archivedEntries = (plantId) => {
|
||||
let archived = data.archived ?? []
|
||||
return archived.where('plantId', plantId)
|
||||
}
|
||||
|
||||
return {plants, addPlant, editPlant, removePlant, addAction, doneTask, history}
|
||||
return {plants, addPlant, editPlant, removePlant, addAction, doneTask, history, archivedEntries}
|
||||
}
|
||||
|
||||
export default usePlants
|
||||
10
src/index.js
10
src/index.js
@@ -20,4 +20,14 @@ Date.prototype.toSQLDate = function() {
|
||||
return `${this.getFullYear()}-${Number(this.getMonth() + 1).pad(2)}-${Number(this.getDate()).pad(2)}`
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter array on a field common of the objects
|
||||
* @param {string} field to filter
|
||||
* @param {string} search to filter by
|
||||
* @returns {*[]}
|
||||
*/
|
||||
Array.prototype.where = function(field, search) {
|
||||
return this.filter(item => item[field] === search)
|
||||
}
|
||||
|
||||
export default App;
|
||||
|
||||
@@ -30,7 +30,7 @@ export const Home = () => {
|
||||
</div>
|
||||
<div className="w-full sticky bottom-5 flex justify-end">
|
||||
<div onClick={() => setAddModal(true)}
|
||||
className="rounded-full w-16 h-16 flex items-center justify-center cursor-pointer bg-green-800 hover-bg-green-900 text-white">
|
||||
className="rounded-full shadow w-16 h-16 flex items-center justify-center cursor-pointer bg-green-800 hover-bg-green-900 text-white">
|
||||
Add +
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -9,24 +9,23 @@ import {getPicture, storePicture} from "../utilities/pictures"
|
||||
import {EditSVG, PlusSVG} from "../components/SVG"
|
||||
import {classNames} from "../utilities/classNames"
|
||||
import {PlantForm} from "../components/Plants"
|
||||
import {ACTION_TYPES, actionId} from "../utilities/actions"
|
||||
|
||||
const Plant = ({id}) => {
|
||||
|
||||
const [addModal, setAddModal] = useState(false)
|
||||
const [editModal, setEditModal] = useState(false)
|
||||
const [deleteModal, setDeleteModal] = useState(false)
|
||||
const {plants, editPlant, removePlant, addAction, doneTask, history} = useContext(PlantsContext)
|
||||
const {plants, editPlant, removePlant, addAction, doneTask, history, archivedEntries} = useContext(PlantsContext)
|
||||
const [plant, setPlant] = useState({})
|
||||
const archived = archivedEntries(Number(id))
|
||||
const [actionForm, setActionForm] = useState({})
|
||||
const [image, setImage] = useState(localStorage.getItem("image" + id) ?? '')
|
||||
const pictureName = 'picture-' + id
|
||||
const picture = useRef(null)
|
||||
|
||||
const action_types = ['watering', 'spraying', 'bathing']
|
||||
|
||||
useEffect(() => {
|
||||
const plantFind = plants.find(plant => plant.id === Number(id))
|
||||
console.log(plantFind, plantFind['indoor'])
|
||||
setPlant(plantFind)
|
||||
}, [])
|
||||
|
||||
@@ -38,7 +37,7 @@ const Plant = ({id}) => {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
if (!actionForm.action_type) {
|
||||
actionForm.action_type = action_types[0]
|
||||
actionForm.action_type = ACTION_TYPES[0]
|
||||
}
|
||||
addAction(plant, actionForm)
|
||||
setAddModal(false)
|
||||
@@ -78,14 +77,15 @@ const Plant = ({id}) => {
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
{plant.description && <p>{ plant.description }</p>}
|
||||
{/*{plant.hasAttribute('indoor') && <p>{ plant.indoor ? 'Indoor' : 'Outdoor' }</p>}*/}
|
||||
{plant.hasOwnProperty('indoor') && <span className="inline-block bg-green-500 text-white px-2 py-1 text-sm font-semibold rounded shadow my-2">{ plant.indoor ? 'Indoor' : 'Outdoor' }</span>}
|
||||
{plant.spot && <p>Spot: <span className="font-bold">{ plant.spot }</span></p>}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="flex items-center gap-2">
|
||||
<h2>Actions</h2>
|
||||
<SmallButton className="bg-blue-500 hover:bg-blue-700 mb-2 mt-5" onClick={() => setAddModal(true)}>
|
||||
<SmallButton className="bg-blue-500 hover:bg-blue-700 mb-2 mt-5"
|
||||
onClick={() => setAddModal(true)}>
|
||||
<PlusSVG className="w-4 h-4" />
|
||||
</SmallButton>
|
||||
</div>
|
||||
@@ -98,13 +98,17 @@ const Plant = ({id}) => {
|
||||
}
|
||||
return <div key={action.action_type} className="flex items-center gap-2 my-2">
|
||||
<span>
|
||||
<Button className={classNames(isDone ? "bg-green-500 hover:bg-green-700" : "bg-blue-500 hover:bg-blue-700")} onClick={() => doneTask(action.id)}>Done</Button>
|
||||
<Button className={classNames(isDone ? "bg-green-500 hover:bg-green-700" : "bg-blue-500 hover:bg-blue-700")}
|
||||
onClick={() => doneTask(action.id)}>
|
||||
Done
|
||||
</Button>
|
||||
</span>
|
||||
<span className="capitalize font-bold">{action.action_type}</span>
|
||||
{Number(action.frequency) === 0
|
||||
? <span>when you want</span>
|
||||
: <span> every {action.frequency} days</span>}
|
||||
<span className="text-gray-500">last task {lastTask ? lastTask.toFrDate() : 'never'}</span>
|
||||
{/*<span>{ archived.where('action', actionId(action.action_type)).length }</span>*/}
|
||||
</div>
|
||||
})}
|
||||
</div>
|
||||
@@ -114,7 +118,14 @@ const Plant = ({id}) => {
|
||||
<ModalTitle>Add Action</ModalTitle>
|
||||
<form onSubmit={handleSubmit}>
|
||||
|
||||
<SelectField name="action_type" className="mb-2 mt-5" value={actionForm.action_type} options={action_types} onChange={(e) => setActionForm({ ...actionForm, action_type: e.target.value })}>Name</SelectField>
|
||||
<SelectField name="action_type"
|
||||
className="mb-2 mt-5"
|
||||
value={actionForm.action_type}
|
||||
options={ACTION_TYPES}
|
||||
onChange={(e) => setActionForm({ ...actionForm, action_type: e.target.value })}>
|
||||
Name
|
||||
</SelectField>
|
||||
|
||||
<InputField name="frequency"
|
||||
className="mb-5"
|
||||
type="number"
|
||||
@@ -125,7 +136,8 @@ const Plant = ({id}) => {
|
||||
</InputField>
|
||||
|
||||
|
||||
<Button type="submit" className="block w-full mt-5 mb-2 bg-green-800 hover:bg-green-900 text-white mx-auto px-2 py-1 shadow">
|
||||
<Button type="submit"
|
||||
className="block w-full mt-5 mb-2 bg-green-800 hover:bg-green-900 text-white mx-auto px-2 py-1 shadow">
|
||||
Add
|
||||
</Button>
|
||||
</form>
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { useContext, useEffect, useState } from "preact/hooks";
|
||||
import { PageLayout } from "../components/PageLayout";
|
||||
import { UserContext } from "../Contexts";
|
||||
import {PlantsContext, UserContext} from "../Contexts";
|
||||
|
||||
export default function Profile() {
|
||||
const [user, setUser] = useContext(UserContext)
|
||||
const {plants} = useContext(PlantsContext)
|
||||
const [darkMode, setDarkMode] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
@@ -29,5 +30,8 @@ export default function Profile() {
|
||||
onChange={() => handleChangeDarkMode()} />
|
||||
Dark Mode
|
||||
</label>
|
||||
<div className="mt-5">
|
||||
{plants.length} Plants
|
||||
</div>
|
||||
</PageLayout>
|
||||
}
|
||||
6
src/utilities/actions.js
Normal file
6
src/utilities/actions.js
Normal file
@@ -0,0 +1,6 @@
|
||||
|
||||
export const ACTION_TYPES = ['watering', 'spraying', 'bathing']
|
||||
|
||||
export const actionId = (action) => {
|
||||
return ACTION_TYPES.findIndex(item => item === action)
|
||||
}
|
||||
Reference in New Issue
Block a user