diff --git a/Makefile b/Makefile
index 890d7a9..7920b20 100644
--- a/Makefile
+++ b/Makefile
@@ -1,5 +1,11 @@
DOKKU_URL := dokku@dev.lookingfora.name:guesstimate
+watch:
+ npm run dev
+
+build:
+ npm run build
+
dokku-build:
docker build \
-t guesstimate-dokku:latest \
@@ -10,4 +16,4 @@ dokku-run:
dokku-deploy:
$(if $(shell git config remote.dokku.url),, git remote add dokku $(DOKKU_URL))
- git push -f dokku $(shell git rev-parse HEAD):master
\ No newline at end of file
+ git push -f dokku $(shell git rev-parse HEAD):refs/heads/master
\ No newline at end of file
diff --git a/src/components/app.tsx b/src/components/app.tsx
index a17d527..e09a41e 100644
--- a/src/components/app.tsx
+++ b/src/components/app.tsx
@@ -23,7 +23,7 @@ const App: FunctionalComponent = () => {
-
+
diff --git a/src/hooks/use-local-storage.ts b/src/hooks/use-local-storage.ts
new file mode 100644
index 0000000..10d86ba
--- /dev/null
+++ b/src/hooks/use-local-storage.ts
@@ -0,0 +1,37 @@
+import { useState } from "preact/hooks";
+
+export function useLocalStorage(key: string, initialValue: T) {
+ // State to store our value
+ // Pass initial state function to useState so logic is only executed once
+ const [storedValue, setStoredValue] = useState(() => {
+ try {
+ // Get from local storage by key
+ const item = window.localStorage.getItem(key);
+ // Parse stored json or if none return initialValue
+ return item ? JSON.parse(item) : initialValue;
+ } catch (error) {
+ // If error also return initialValue
+ console.error(error);
+ return initialValue;
+ }
+ });
+
+ // Return a wrapped version of useState's setter function that ...
+ // ... persists the new value to localStorage.
+ const setValue = (value: T) => {
+ try {
+ // Allow value to be a function so we have same API as useState
+ const valueToStore =
+ value instanceof Function ? value(storedValue) : value;
+ // Save state
+ setStoredValue(valueToStore);
+ // Save to local storage
+ window.localStorage.setItem(key, JSON.stringify(valueToStore));
+ } catch (error) {
+ // A more advanced implementation would handle the error case
+ console.error(error);
+ }
+ };
+
+ return [storedValue, setValue];
+ }
\ No newline at end of file
diff --git a/src/hooks/project-reducer.ts b/src/hooks/use-project-reducer.ts
similarity index 94%
rename from src/hooks/project-reducer.ts
rename to src/hooks/use-project-reducer.ts
index f56da82..ac5a841 100644
--- a/src/hooks/project-reducer.ts
+++ b/src/hooks/use-project-reducer.ts
@@ -1,5 +1,6 @@
import { Project } from "../models/project";
import { Task, TaskID, EstimationConfidence } from "../models/task";
+import { useReducer } from "preact/hooks";
export interface Action {
type: string
@@ -10,6 +11,10 @@ export type ProjectReducerActions =
RemoveTaskAction |
UpdateTaskEstimation
+export function useProjectReducer(project: Project) {
+ return useReducer(projectReducer, project);
+}
+
export function projectReducer(project: Project, action: ProjectReducerActions): Project {
switch(action.type) {
case ADD_TASK:
diff --git a/src/hooks/use-stored-project-list.ts b/src/hooks/use-stored-project-list.ts
new file mode 100644
index 0000000..42a5531
--- /dev/null
+++ b/src/hooks/use-stored-project-list.ts
@@ -0,0 +1,35 @@
+import {Project} from "../models/project";
+import { useState } from "preact/hooks";
+import { ProjectStorageKeyPrefix } from "../util/storage";
+
+export function loadStoredProjects(): Project[] {
+ const projects: Project[] = [];
+
+ Object.keys(window.localStorage).forEach(key => {
+ if (key.startsWith(ProjectStorageKeyPrefix)) {
+ try {
+ const data = window.localStorage.getItem(key);
+ if (data) {
+ const project = JSON.parse(data);
+ projects.push(project);
+ }
+ } catch(err) {
+ console.error(err);
+ }
+ }
+ });
+
+ return projects
+}
+
+export function useStoredProjectList(): [Project[], () => void] {
+ const [ projects, setProjects ] = useState(() => {
+ return loadStoredProjects();
+ });
+
+ const refresh = () => {
+ setProjects(loadStoredProjects());
+ };
+
+ return [ projects, refresh];
+}
\ No newline at end of file
diff --git a/src/models/project.ts b/src/models/project.ts
index b98e98d..843ae40 100644
--- a/src/models/project.ts
+++ b/src/models/project.ts
@@ -16,9 +16,9 @@ export interface Tasks {
[id: string]: Task
}
-export function newProject(): Project {
+export function newProject(id?: string): Project {
return {
- id: uuidV4(),
+ id: id ? id : uuidV4(),
label: "",
description: "",
tasks: {},
diff --git a/src/routes/home/index.tsx b/src/routes/home/index.tsx
index 616499a..32d4781 100644
--- a/src/routes/home/index.tsx
+++ b/src/routes/home/index.tsx
@@ -2,8 +2,10 @@ import { FunctionalComponent, h } from "preact";
import * as style from "./style.css";
import { route } from 'preact-router';
import { base58UUID } from '../../util/uuid';
+import { useStoredProjectList } from "../../hooks/use-stored-project-list";
const Home: FunctionalComponent = () => {
+ const [ projects, refreshProjects ] = useStoredProjectList();
const openNewProject = () => {
const uuid = base58UUID();
@@ -24,16 +26,27 @@ const Home: FunctionalComponent = () => {
Mes projets
- */}
+ {
+ projects.map(p => (
+
+ 🗒️
+ { p.label ? p.label : "Projet sans nom" }
+
+ ))
+ }
+ {
+ projects.length === 0 ?
+
+
Aucun project pour l'instant.
+
:
+ null
+ }
diff --git a/src/routes/home/style.css b/src/routes/home/style.css
index b63a3c1..70b3b78 100644
--- a/src/routes/home/style.css
+++ b/src/routes/home/style.css
@@ -1,3 +1,9 @@
.home {
height: 100%;
+}
+
+.noProjects {
+ width: 100%;
+ text-align: center;
+ font-style: italic;
}
\ No newline at end of file
diff --git a/src/routes/home/style.css.d.ts b/src/routes/home/style.css.d.ts
index b4b2672..1fd05dd 100644
--- a/src/routes/home/style.css.d.ts
+++ b/src/routes/home/style.css.d.ts
@@ -1,2 +1,3 @@
// This file is automatically generated from your CSS. Any edits will be overwritten.
export const home: string;
+export const noProjects: string;
diff --git a/src/routes/project/index.tsx b/src/routes/project/index.tsx
index 5210b6f..d91062e 100644
--- a/src/routes/project/index.tsx
+++ b/src/routes/project/index.tsx
@@ -1,15 +1,23 @@
import { FunctionalComponent, h } from "preact";
-import { useReducer } from "preact/hooks";
+import { useEffect } from "preact/hooks";
import * as style from "./style.css";
import { newProject } from "../../models/project";
import TaskTable from "./tasks-table";
import TimePreview from "./time-preview";
import FinancialPreview from "./financial-preview";
-import { projectReducer, addTask, updateTaskEstimation, removeTask } from "../../hooks/project-reducer";
+import { useProjectReducer, addTask, updateTaskEstimation, removeTask } from "../../hooks/use-project-reducer";
import { Task, TaskID, EstimationConfidence } from "../../models/task";
+import { getProjectStorageKey } from "../../util/storage";
+import { useLocalStorage } from "../../hooks/use-local-storage";
-const Project: FunctionalComponent = () => {
- const [project, dispatch] = useReducer(projectReducer, newProject());
+export interface ProjectProps {
+ projectId: string
+}
+
+const Project: FunctionalComponent = ({ projectId }) => {
+ const projectStorageKey = getProjectStorageKey(projectId);
+ const [ storedProject, storeProject ] = useLocalStorage(projectStorageKey, newProject(projectId));
+ const [ project, dispatch ] = useProjectReducer(storedProject);
const onTaskAdd = (task: Task) => {
dispatch(addTask(task));
@@ -23,8 +31,18 @@ const Project: FunctionalComponent = () => {
dispatch(updateTaskEstimation(taskId, confidence, value));
};
+ // Save project in local storage on change
+ useEffect(()=> {
+ storeProject(project);
+ }, [project]);
+
return (
+
+ {project.label ? project.label : "Projet sans nom"}
+
+ 🖋️
+
-
diff --git a/src/routes/project/time-preview.tsx b/src/routes/project/time-preview.tsx
index afa19ef..3c41384 100644
--- a/src/routes/project/time-preview.tsx
+++ b/src/routes/project/time-preview.tsx
@@ -51,6 +51,13 @@ const TimePreview: FunctionalComponent = ({ project }) => {
{`${estimations.p68.e} ± ${estimations.p68.sd} j/h`} |
+
+
+
+ ❓ Estimation à 3 points
+ |
+
+
);
diff --git a/src/util/storage.ts b/src/util/storage.ts
new file mode 100644
index 0000000..757f5a3
--- /dev/null
+++ b/src/util/storage.ts
@@ -0,0 +1,8 @@
+import { useState } from "preact/hooks/src";
+import { ProjectID } from "../models/project";
+
+export const ProjectStorageKeyPrefix = "project-";
+
+export function getProjectStorageKey(id: ProjectID): string {
+ return `${ProjectStorageKeyPrefix}${id}`
+}
\ No newline at end of file