Initial commit
|
@ -0,0 +1,36 @@
|
||||||
|
module.exports = {
|
||||||
|
env: {
|
||||||
|
browser: true
|
||||||
|
},
|
||||||
|
extends: [
|
||||||
|
"plugin:react/recommended",
|
||||||
|
"plugin:@typescript-eslint/recommended",
|
||||||
|
"prettier/@typescript-eslint",
|
||||||
|
"plugin:prettier/recommended"
|
||||||
|
],
|
||||||
|
parser: "@typescript-eslint/parser",
|
||||||
|
parserOptions: {
|
||||||
|
ecmaFeatures: {
|
||||||
|
jsx: true
|
||||||
|
},
|
||||||
|
ecmaVersion: 2018,
|
||||||
|
sourceType: "module",
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
"react/no-unknown-property": ["error", { ignore: ["class"] }],
|
||||||
|
},
|
||||||
|
settings: {
|
||||||
|
react: {
|
||||||
|
pragma: "h",
|
||||||
|
version: "detect"
|
||||||
|
},
|
||||||
|
},
|
||||||
|
overrides: [
|
||||||
|
{
|
||||||
|
files: ["*.js"],
|
||||||
|
rules: {
|
||||||
|
"@typescript-eslint/explicit-function-return-type": "off",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
|
@ -0,0 +1,3 @@
|
||||||
|
node_modules
|
||||||
|
/build
|
||||||
|
/*.log
|
|
@ -0,0 +1,4 @@
|
||||||
|
package.json
|
||||||
|
package-lock.json
|
||||||
|
yarn.lock
|
||||||
|
build
|
|
@ -0,0 +1 @@
|
||||||
|
tabWidth: 2
|
|
@ -0,0 +1,19 @@
|
||||||
|
# guesstimate
|
||||||
|
|
||||||
|
## CLI Commands
|
||||||
|
* `npm install`: Installs dependencies
|
||||||
|
|
||||||
|
* `npm run start`: Runs `serve` or `dev`, depending on `NODE_ENV` value. Defaults to `dev server`
|
||||||
|
|
||||||
|
* `npm run dev`: Run a development, HMR server
|
||||||
|
|
||||||
|
* `npm run serve`: Run a production-like server
|
||||||
|
|
||||||
|
* `npm run build`: Production-ready build
|
||||||
|
|
||||||
|
* `npm run lint`: Pass TypeScript files using TSLint
|
||||||
|
|
||||||
|
* `npm run test`: Run Jest and [`preact-render-spy`](https://github.com/mzgoddard/preact-render-spy) for your tests
|
||||||
|
|
||||||
|
|
||||||
|
For detailed explanation on how things work, checkout the [CLI Readme](https://github.com/developit/preact-cli/blob/master/README.md).
|
|
@ -0,0 +1,33 @@
|
||||||
|
module.exports = {
|
||||||
|
transform: {
|
||||||
|
"^.+\\.tsx?$": "ts-jest"
|
||||||
|
},
|
||||||
|
verbose: true,
|
||||||
|
setupFiles: [
|
||||||
|
"<rootDir>/src/tests/__mocks__/browserMocks.js"
|
||||||
|
],
|
||||||
|
testURL: "http://localhost:8080",
|
||||||
|
moduleFileExtensions: [
|
||||||
|
"js",
|
||||||
|
"jsx",
|
||||||
|
"ts",
|
||||||
|
"tsx"
|
||||||
|
],
|
||||||
|
moduleDirectories: [
|
||||||
|
"node_modules"
|
||||||
|
],
|
||||||
|
testMatch: [
|
||||||
|
"**/__tests__/**/*.[jt]s?(x)",
|
||||||
|
"**/?(*.)(spec|test).[jt]s?(x)"
|
||||||
|
],
|
||||||
|
moduleNameMapper: {
|
||||||
|
"\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/src/tests/__mocks__/fileMock.js",
|
||||||
|
"\\.(css|less|scss)$": "identity-obj-proxy",
|
||||||
|
"^./style$": "identity-obj-proxy",
|
||||||
|
"^preact$": "<rootDir>/node_modules/preact/dist/preact.min.js",
|
||||||
|
"^react$": "preact-compat",
|
||||||
|
"^react-dom$": "preact-compat",
|
||||||
|
"^create-react-class$": "preact-compat/lib/create-react-class",
|
||||||
|
"^react-addons-css-transition-group$": "preact-css-transition-group"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
{
|
||||||
|
"private": true,
|
||||||
|
"name": "guesstimate",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"license": "MIT",
|
||||||
|
"scripts": {
|
||||||
|
"build": "preact build",
|
||||||
|
"serve": "sirv build --port 8080 --cors --single",
|
||||||
|
"dev": "preact watch",
|
||||||
|
"lint": "eslint src/**/*.{js,jsx,ts,tsx}",
|
||||||
|
"test": "jest ./tests"
|
||||||
|
},
|
||||||
|
"husky": {
|
||||||
|
"hooks": {
|
||||||
|
"pre-commit": "lint-staged"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"lint-staged": {
|
||||||
|
"*.{css,md,scss}": "prettier --write",
|
||||||
|
"*.{js,jsx,ts,tsx}": "eslint --fix"
|
||||||
|
},
|
||||||
|
"eslintIgnore": [
|
||||||
|
"build/*"
|
||||||
|
],
|
||||||
|
"dependencies": {
|
||||||
|
"@types/bs58": "^4.0.1",
|
||||||
|
"bs58": "^4.0.1",
|
||||||
|
"bulma": "^0.8.2",
|
||||||
|
"preact": "^10.3.1",
|
||||||
|
"preact-jsx-chai": "^3.0.0",
|
||||||
|
"preact-markup": "^2.0.0",
|
||||||
|
"preact-render-to-string": "^5.1.4",
|
||||||
|
"preact-router": "^3.2.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/jest": "^25.1.2",
|
||||||
|
"@types/webpack-env": "^1.15.1",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^2.19.0",
|
||||||
|
"@typescript-eslint/parser": "^2.19.0",
|
||||||
|
"css-loader": "^1.0.1",
|
||||||
|
"eslint": "^6.8.0",
|
||||||
|
"eslint-config-prettier": "^6.10.0",
|
||||||
|
"eslint-plugin-prettier": "^3.1.2",
|
||||||
|
"eslint-plugin-react": "^7.18.3",
|
||||||
|
"husky": "^4.2.1",
|
||||||
|
"identity-obj-proxy": "^3.0.0",
|
||||||
|
"jest": "^25.1.0",
|
||||||
|
"lint-staged": "^10.0.7",
|
||||||
|
"node-sass": "^4.13.1",
|
||||||
|
"preact-cli": "^3.0.0-next.19",
|
||||||
|
"preact-render-spy": "^1.3.0",
|
||||||
|
"prettier": "^1.19.1",
|
||||||
|
"sass-loader": "^8.0.2",
|
||||||
|
"sirv-cli": "^1.0.0-next.3",
|
||||||
|
"ts-jest": "^25.2.0",
|
||||||
|
"ts-loader": "^6.2.1",
|
||||||
|
"typescript": "^3.7.5",
|
||||||
|
"typings-for-css-modules-loader": "^1.7.0"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
import { resolve } from "path";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
/**
|
||||||
|
* Function that mutates the original webpack config.
|
||||||
|
* Supports asynchronous changes when a promise is returned (or it's an async function).
|
||||||
|
*
|
||||||
|
* @param {object} config - original webpack config.
|
||||||
|
* @param {object} env - options passed to the CLI.
|
||||||
|
* @param {WebpackConfigHelpers} helpers - object with useful helpers for working with the webpack config.
|
||||||
|
* @param {object} options - this is mainly relevant for plugins (will always be empty in the config), default to an empty object
|
||||||
|
**/
|
||||||
|
webpack(config, env, helpers, options) {
|
||||||
|
// Switch css-loader for typings-for-css-modules-loader, which is a wrapper
|
||||||
|
// that automatically generates .d.ts files for loaded CSS
|
||||||
|
helpers.getLoadersByName(config, "css-loader").forEach(({ loader }) => {
|
||||||
|
loader.loader = "typings-for-css-modules-loader";
|
||||||
|
loader.options = Object.assign(loader.options, {
|
||||||
|
camelCase: true,
|
||||||
|
banner:
|
||||||
|
"// This file is automatically generated from your CSS. Any edits will be overwritten.",
|
||||||
|
namedExport: true,
|
||||||
|
silent: true
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Use any `index` file, not just index.js
|
||||||
|
config.resolve.alias["preact-cli-entrypoint"] = resolve(
|
||||||
|
process.cwd(),
|
||||||
|
"src",
|
||||||
|
"index"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"presets": [
|
||||||
|
["preact-cli/babel", { "modules": "commonjs" }]
|
||||||
|
]
|
||||||
|
}
|
After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 50 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 626 B |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 8.8 KiB |
|
@ -0,0 +1,33 @@
|
||||||
|
import { FunctionalComponent, h } from "preact";
|
||||||
|
import { Route, Router, RouterOnChangeArgs } from "preact-router";
|
||||||
|
|
||||||
|
import Home from "../routes/home";
|
||||||
|
import Project from "../routes/project";
|
||||||
|
import NotFoundPage from '../routes/notfound';
|
||||||
|
import Header from "./header";
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
if ((module as any).hot) {
|
||||||
|
// tslint:disable-next-line:no-var-requires
|
||||||
|
require("preact/debug");
|
||||||
|
}
|
||||||
|
|
||||||
|
const App: FunctionalComponent = () => {
|
||||||
|
let currentUrl: string;
|
||||||
|
const handleRoute = (e: RouterOnChangeArgs) => {
|
||||||
|
currentUrl = e.url;
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div id="app">
|
||||||
|
<Header />
|
||||||
|
<Router onChange={handleRoute}>
|
||||||
|
<Route path="/" component={Home} />
|
||||||
|
<Route path="/p/:uuid" component={Project} />
|
||||||
|
<NotFoundPage default />
|
||||||
|
</Router>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default App;
|
|
@ -0,0 +1,28 @@
|
||||||
|
import { FunctionalComponent, h } from "preact";
|
||||||
|
import { Link } from "preact-router/match";
|
||||||
|
import * as style from "./style.css";
|
||||||
|
|
||||||
|
const Header: FunctionalComponent = () => {
|
||||||
|
return (
|
||||||
|
<div class="container">
|
||||||
|
<div class="columns">
|
||||||
|
<div class="column">
|
||||||
|
<nav class="navbar" role="navigation" aria-label="main navigation">
|
||||||
|
<div class="navbar-brand">
|
||||||
|
<a class="navbar-item" href="/">
|
||||||
|
<h1 class="title is-size-4">⏱️ Guesstimate</h1>
|
||||||
|
</a>
|
||||||
|
<a role="button" class="navbar-burger" aria-label="menu" aria-expanded="false">
|
||||||
|
<span aria-hidden="true"></span>
|
||||||
|
<span aria-hidden="true"></span>
|
||||||
|
<span aria-hidden="true"></span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Header;
|
|
@ -0,0 +1,3 @@
|
||||||
|
// This file is automatically generated from your CSS. Any edits will be overwritten.
|
||||||
|
export const header: string;
|
||||||
|
export const active: string;
|
|
@ -0,0 +1,4 @@
|
||||||
|
import { JSX } from "preact";
|
||||||
|
|
||||||
|
export = JSX;
|
||||||
|
export as namespace JSX;
|
|
@ -0,0 +1,89 @@
|
||||||
|
import { Project } from "../models/project";
|
||||||
|
import { Task, TaskID, EstimationConfidence } from "../models/task";
|
||||||
|
|
||||||
|
export interface Action {
|
||||||
|
type: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ProjectReducerActions =
|
||||||
|
AddTaskAction |
|
||||||
|
RemoveTaskAction |
|
||||||
|
UpdateTaskEstimation
|
||||||
|
|
||||||
|
export function projectReducer(project: Project, action: ProjectReducerActions): Project {
|
||||||
|
switch(action.type) {
|
||||||
|
case ADD_TASK:
|
||||||
|
const task = { ...(action as AddTaskAction).task };
|
||||||
|
return {
|
||||||
|
...project,
|
||||||
|
tasks: {
|
||||||
|
...project.tasks,
|
||||||
|
[task.id]: task,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
case REMOVE_TASK:
|
||||||
|
action = action as RemoveTaskAction;
|
||||||
|
const tasks = { ...project.tasks };
|
||||||
|
delete tasks[action.id];
|
||||||
|
return {
|
||||||
|
...project,
|
||||||
|
tasks
|
||||||
|
};
|
||||||
|
case UPDATE_TASK_ESTIMATION:
|
||||||
|
action = action as UpdateTaskEstimation;
|
||||||
|
const estimations = {
|
||||||
|
...project.tasks[action.id].estimations,
|
||||||
|
[(action as UpdateTaskEstimation).confidence]: (action as UpdateTaskEstimation).value
|
||||||
|
};
|
||||||
|
if (estimations.likely <= estimations.optimistic) {
|
||||||
|
estimations.likely = estimations.optimistic + 1;
|
||||||
|
}
|
||||||
|
if (estimations.pessimistic <= estimations.likely) {
|
||||||
|
estimations.pessimistic = estimations.likely + 1;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...project,
|
||||||
|
tasks: {
|
||||||
|
...project.tasks,
|
||||||
|
[action.id]: {
|
||||||
|
...project.tasks[action.id],
|
||||||
|
estimations: estimations,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return project;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AddTaskAction extends Action {
|
||||||
|
task: Task
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ADD_TASK = "ADD_TASK";
|
||||||
|
|
||||||
|
export function addTask(task: Task): AddTaskAction {
|
||||||
|
return { type: ADD_TASK, task };
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RemoveTaskAction extends Action {
|
||||||
|
id: TaskID
|
||||||
|
}
|
||||||
|
|
||||||
|
export const REMOVE_TASK = "REMOVE_TASK";
|
||||||
|
|
||||||
|
export function removeTask(id: TaskID): RemoveTaskAction {
|
||||||
|
return { type: REMOVE_TASK, id };
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UpdateTaskEstimation extends Action {
|
||||||
|
id: TaskID
|
||||||
|
confidence: string
|
||||||
|
value: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export const UPDATE_TASK_ESTIMATION = "UPDATE_TASK_ESTIMATION";
|
||||||
|
|
||||||
|
export function updateTaskEstimation(id: TaskID, confidence: EstimationConfidence, value: number): UpdateTaskEstimation {
|
||||||
|
return { type: UPDATE_TASK_ESTIMATION, id, confidence, value };
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
import "./style/index.scss";
|
||||||
|
import "bulma/bulma.sass";
|
||||||
|
import App from "./components/app.tsx";
|
||||||
|
|
||||||
|
export default App;
|
|
@ -0,0 +1,21 @@
|
||||||
|
{
|
||||||
|
"name": "guesstimate",
|
||||||
|
"short_name": "guesstimate",
|
||||||
|
"start_url": "/",
|
||||||
|
"display": "standalone",
|
||||||
|
"orientation": "portrait",
|
||||||
|
"background_color": "#fff",
|
||||||
|
"theme_color": "#673ab8",
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": "/assets/icons/android-chrome-192x192.png",
|
||||||
|
"type": "image/png",
|
||||||
|
"sizes": "192x192"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/assets/icons/android-chrome-512x512.png",
|
||||||
|
"type": "image/png",
|
||||||
|
"sizes": "512x512"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
import { TaskCategory, CategoryID } from "./task";
|
||||||
|
|
||||||
|
export interface TaskCategoriesIndex {
|
||||||
|
[id: string]: TaskCategory
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Params {
|
||||||
|
taskCategories: TaskCategoriesIndex
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DefaultTaskCategories = {
|
||||||
|
"7e92266f-0a7b-4728-8322-5fe05ff3b929": {
|
||||||
|
id: "7e92266f-0a7b-4728-8322-5fe05ff3b929",
|
||||||
|
label: "Développement"
|
||||||
|
},
|
||||||
|
"508a0925-a664-4426-8d40-6974156f0f00": {
|
||||||
|
id: "508a0925-a664-4426-8d40-6974156f0f00",
|
||||||
|
label: "Conduite de projet"
|
||||||
|
},
|
||||||
|
"7aab4d66-072e-4cc8-aae8-b62edd3237a8": {
|
||||||
|
id: "7aab4d66-072e-4cc8-aae8-b62edd3237a8",
|
||||||
|
label: "Recette"
|
||||||
|
},
|
||||||
|
};
|
|
@ -0,0 +1,30 @@
|
||||||
|
import { Project } from "./project";
|
||||||
|
import { Task, TaskCategory, TaskID } from './task';
|
||||||
|
import { Params, DefaultTaskCategories } from "./params";
|
||||||
|
import { uuidV4 } from "../util/uuid";
|
||||||
|
|
||||||
|
export type ProjectID = string;
|
||||||
|
|
||||||
|
export interface Project {
|
||||||
|
id: ProjectID
|
||||||
|
label: string
|
||||||
|
description: string
|
||||||
|
tasks: Tasks
|
||||||
|
params: Params
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Tasks {
|
||||||
|
[id: string]: Task
|
||||||
|
}
|
||||||
|
|
||||||
|
export function newProject(): Project {
|
||||||
|
return {
|
||||||
|
id: uuidV4(),
|
||||||
|
label: "",
|
||||||
|
description: "",
|
||||||
|
tasks: {},
|
||||||
|
params: {
|
||||||
|
taskCategories: DefaultTaskCategories,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
import { uuidV4 } from "../util/uuid"
|
||||||
|
|
||||||
|
export type TaskID = string
|
||||||
|
|
||||||
|
export enum EstimationConfidence {
|
||||||
|
Optimistic = "optimistic",
|
||||||
|
Likely = "likely",
|
||||||
|
Pessimistic = "pessimistic"
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Task {
|
||||||
|
id: TaskID
|
||||||
|
label: string
|
||||||
|
category: CategoryID
|
||||||
|
estimations: { [confidence in EstimationConfidence]: number }
|
||||||
|
}
|
||||||
|
|
||||||
|
export type CategoryID = string
|
||||||
|
|
||||||
|
export interface TaskCategory {
|
||||||
|
id: CategoryID
|
||||||
|
label: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export function newTask(label: string, category: CategoryID): Task {
|
||||||
|
return {
|
||||||
|
id: uuidV4(),
|
||||||
|
label,
|
||||||
|
category,
|
||||||
|
estimations: {
|
||||||
|
[EstimationConfidence.Optimistic]: 0,
|
||||||
|
[EstimationConfidence.Likely]: 0,
|
||||||
|
[EstimationConfidence.Pessimistic]: 0,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
import { FunctionalComponent, h } from "preact";
|
||||||
|
import * as style from "./style.css";
|
||||||
|
import { route } from 'preact-router';
|
||||||
|
import { base58UUID } from '../../util/uuid';
|
||||||
|
|
||||||
|
const Home: FunctionalComponent = () => {
|
||||||
|
|
||||||
|
const openNewProject = () => {
|
||||||
|
const uuid = base58UUID();
|
||||||
|
route(`/p/${uuid}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div class={`container ${style.home}`}>
|
||||||
|
<div class="columns">
|
||||||
|
<div class="column">
|
||||||
|
<div class="buttons is-right">
|
||||||
|
<button class="button is-primary"
|
||||||
|
onClick={openNewProject}>
|
||||||
|
<strong>+</strong> Nouveau projet
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="panel">
|
||||||
|
<p class="panel-heading">
|
||||||
|
Mes projets
|
||||||
|
</p>
|
||||||
|
<div class="panel-block">
|
||||||
|
<p class="control has-icons-left">
|
||||||
|
<input class="input" type="text" placeholder="Search" />
|
||||||
|
<span class="icon is-left">🔍</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<a class="panel-block">
|
||||||
|
<span class="panel-icon">🗒️</span>
|
||||||
|
Projet #1
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Home;
|
|
@ -0,0 +1,3 @@
|
||||||
|
.home {
|
||||||
|
height: 100%;
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
// This file is automatically generated from your CSS. Any edits will be overwritten.
|
||||||
|
export const home: string;
|
|
@ -0,0 +1,15 @@
|
||||||
|
import { FunctionalComponent, h } from "preact";
|
||||||
|
import { Link } from 'preact-router/match';
|
||||||
|
import * as style from "./style.css";
|
||||||
|
|
||||||
|
const Notfound: FunctionalComponent = () => {
|
||||||
|
return (
|
||||||
|
<div class={style.notfound}>
|
||||||
|
<h1>Error 404</h1>
|
||||||
|
<p>That page doesn't exist.</p>
|
||||||
|
<Link href="/"><h4>Back to Home</h4></Link>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Notfound;
|
|
@ -0,0 +1,2 @@
|
||||||
|
// This file is automatically generated from your CSS. Any edits will be overwritten.
|
||||||
|
export {};
|
|
@ -0,0 +1,37 @@
|
||||||
|
import { FunctionalComponent, h } from "preact";
|
||||||
|
import * as style from "./style.css";
|
||||||
|
import { Project } from "../../models/project";
|
||||||
|
|
||||||
|
export interface FinancialPreviewProps {
|
||||||
|
project: Project
|
||||||
|
}
|
||||||
|
|
||||||
|
const FinancialPreview: FunctionalComponent<FinancialPreviewProps> = ({ project }) => {
|
||||||
|
return (
|
||||||
|
<div class="table-container">
|
||||||
|
<table class="table is-bordered is-striped is-fullwidth">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th colSpan={2}>Prévisionnel financier</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Temps</th>
|
||||||
|
<th>Coût</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>Maximum</td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Minimum</td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default FinancialPreview;
|
|
@ -0,0 +1,61 @@
|
||||||
|
import { FunctionalComponent, h } from "preact";
|
||||||
|
import { useReducer } 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 { Task, TaskID, EstimationConfidence } from "../../models/task";
|
||||||
|
|
||||||
|
const Project: FunctionalComponent = () => {
|
||||||
|
const [project, dispatch] = useReducer(projectReducer, newProject());
|
||||||
|
|
||||||
|
const onTaskAdd = (task: Task) => {
|
||||||
|
dispatch(addTask(task));
|
||||||
|
};
|
||||||
|
|
||||||
|
const onTaskRemove = (taskId: TaskID) => {
|
||||||
|
dispatch(removeTask(taskId));
|
||||||
|
}
|
||||||
|
|
||||||
|
const onEstimationChange = (taskId: TaskID, confidence: EstimationConfidence, value: number) => {
|
||||||
|
dispatch(updateTaskEstimation(taskId, confidence, value));
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div class={`container ${style.estimation}`}>
|
||||||
|
<div class="tabs">
|
||||||
|
<ul>
|
||||||
|
<li class="is-active">
|
||||||
|
<a>
|
||||||
|
<span class="icon is-small">📋</span>
|
||||||
|
Estimation
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{/* <li>
|
||||||
|
<a disabled>
|
||||||
|
<span class="icon is-small">⚙️</span>
|
||||||
|
Paramètres
|
||||||
|
</a>
|
||||||
|
</li> */}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="columns">
|
||||||
|
<div class="column is-9">
|
||||||
|
<TaskTable
|
||||||
|
project={project}
|
||||||
|
onTaskAdd={onTaskAdd}
|
||||||
|
onTaskRemove={onTaskRemove}
|
||||||
|
onEstimationChange={onEstimationChange} />
|
||||||
|
</div>
|
||||||
|
<div class="column is-3">
|
||||||
|
<TimePreview project={project} />
|
||||||
|
<FinancialPreview project={project} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Project;
|
|
@ -0,0 +1,16 @@
|
||||||
|
.estimation {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.noTasks {
|
||||||
|
text-align: center !important;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.noBorder {
|
||||||
|
border: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mainColumn {
|
||||||
|
width: 100%;
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
// This file is automatically generated from your CSS. Any edits will be overwritten.
|
||||||
|
export const estimation: string;
|
||||||
|
export const noTasks: string;
|
||||||
|
export const noBorder: string;
|
||||||
|
export const mainColumn: string;
|
|
@ -0,0 +1,174 @@
|
||||||
|
import { FunctionalComponent, h } from "preact";
|
||||||
|
import { useState, useEffect } from "preact/hooks";
|
||||||
|
import * as style from "./style.css";
|
||||||
|
import { Project } from "../../models/project";
|
||||||
|
import { newTask, Task, TaskID, EstimationConfidence } from "../../models/task";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export interface TaskTableProps {
|
||||||
|
project: Project
|
||||||
|
onTaskAdd: (task: Task) => void
|
||||||
|
onTaskRemove: (taskId: TaskID) => void
|
||||||
|
onEstimationChange: (taskId: TaskID, confidence: EstimationConfidence, value: number) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
export type EstimationTotals = { [confidence in EstimationConfidence]: number }
|
||||||
|
|
||||||
|
const TaskTable: FunctionalComponent<TaskTableProps> = ({ project, onTaskAdd, onEstimationChange, onTaskRemove }) => {
|
||||||
|
|
||||||
|
const defaultTaskCategory = Object.keys(project.params.taskCategories)[0];
|
||||||
|
const [ task, setTask ] = useState(newTask("", defaultTaskCategory));
|
||||||
|
const [ totals, setTotals ] = useState({
|
||||||
|
[EstimationConfidence.Optimistic]: 0,
|
||||||
|
[EstimationConfidence.Likely]: 0,
|
||||||
|
[EstimationConfidence.Pessimistic]: 0,
|
||||||
|
} as EstimationTotals);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
let optimistic = 0;
|
||||||
|
let likely = 0;
|
||||||
|
let pessimistic = 0;
|
||||||
|
|
||||||
|
Object.values(project.tasks).forEach(t => {
|
||||||
|
optimistic += t.estimations.optimistic;
|
||||||
|
likely += t.estimations.likely;
|
||||||
|
pessimistic += t.estimations.pessimistic;
|
||||||
|
});
|
||||||
|
|
||||||
|
setTotals({ optimistic, likely, pessimistic });
|
||||||
|
}, [project.tasks]);
|
||||||
|
|
||||||
|
const onTaskLabelChange = (evt: Event) => {
|
||||||
|
const value = (evt.currentTarget as HTMLInputElement).value;
|
||||||
|
setTask({...task, label: value});
|
||||||
|
};
|
||||||
|
|
||||||
|
const onTaskCategoryChange = (evt: Event) => {
|
||||||
|
const value = (evt.currentTarget as HTMLInputElement).value;
|
||||||
|
setTask({...task, category: value});
|
||||||
|
};
|
||||||
|
|
||||||
|
const onAddTaskClick = (evt: Event) => {
|
||||||
|
onTaskAdd(task);
|
||||||
|
setTask(newTask("", defaultTaskCategory));
|
||||||
|
};
|
||||||
|
|
||||||
|
const onTaskRemoveClick = (taskId: TaskID, evt: Event) => {
|
||||||
|
onTaskRemove(taskId);
|
||||||
|
};
|
||||||
|
|
||||||
|
const withEstimationChange = (confidence: EstimationConfidence, taskID: TaskID, evt: Event) => {
|
||||||
|
const textValue = (evt.currentTarget as HTMLInputElement).value;
|
||||||
|
const value = parseFloat(textValue);
|
||||||
|
onEstimationChange(taskID, confidence, value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onOptimisticChange = withEstimationChange.bind(null, EstimationConfidence.Optimistic);
|
||||||
|
const onLikelyChange = withEstimationChange.bind(null, EstimationConfidence.Likely);
|
||||||
|
const onPessimisticChange = withEstimationChange.bind(null, EstimationConfidence.Pessimistic);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div class="table-container">
|
||||||
|
<table class="table is-bordered is-striped is-hoverable is-fullwidth">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class={style.noBorder} rowSpan={2}></th>
|
||||||
|
<th class={style.mainColumn} rowSpan={2}>Tâche</th>
|
||||||
|
<th rowSpan={2}>Catégorie</th>
|
||||||
|
<th colSpan={3}>Estimation</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Optimiste</th>
|
||||||
|
<th>Probable</th>
|
||||||
|
<th>Pessimiste</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{
|
||||||
|
Object.values(project.tasks).map(t => {
|
||||||
|
const category = project.params.taskCategories[t.category];
|
||||||
|
const categoryLabel = category ? category.label : '???';
|
||||||
|
return (
|
||||||
|
<tr key={`taks-${t.id}`}>
|
||||||
|
<td class="is-narrow">
|
||||||
|
<button
|
||||||
|
onClick={onTaskRemoveClick.bind(null, t.id)}
|
||||||
|
class="button is-danger is-small is-outlined">
|
||||||
|
🗑️
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
<td class={style.mainColumn}>{t.label}</td>
|
||||||
|
<td>{ categoryLabel }</td>
|
||||||
|
<td>
|
||||||
|
<input class="input" type="number" value={t.estimations.optimistic}
|
||||||
|
min={0}
|
||||||
|
onChange={onOptimisticChange.bind(null, t.id)} />
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<input class="input" type="number" value={t.estimations.likely}
|
||||||
|
min={0}
|
||||||
|
onChange={onLikelyChange.bind(null, t.id)} />
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<input class="input" type="number" value={t.estimations.pessimistic}
|
||||||
|
min={0}
|
||||||
|
onChange={onPessimisticChange.bind(null, t.id)} />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
{
|
||||||
|
Object.keys(project.tasks).length === 0 ?
|
||||||
|
<tr>
|
||||||
|
<td class={style.noBorder}></td>
|
||||||
|
<td class={style.noTasks} colSpan={5}>Aucune tâche pour l'instant.</td>
|
||||||
|
</tr> :
|
||||||
|
null
|
||||||
|
}
|
||||||
|
</tbody>
|
||||||
|
<tfoot>
|
||||||
|
<tr>
|
||||||
|
<td class={style.noBorder}></td>
|
||||||
|
<td colSpan={2}>
|
||||||
|
<div class="field has-addons">
|
||||||
|
<p class="control is-expanded">
|
||||||
|
<input class="input" type="text" placeholder="Nouvelle tâche"
|
||||||
|
value={task.label} onChange={onTaskLabelChange} />
|
||||||
|
</p>
|
||||||
|
<p class="control">
|
||||||
|
<span class="select">
|
||||||
|
<select onChange={onTaskCategoryChange} value={task.category}>
|
||||||
|
{
|
||||||
|
Object.values(project.params.taskCategories).map(tc => {
|
||||||
|
return (
|
||||||
|
<option key={`task-category-${tc.id}`} value={tc.id}>{tc.label}</option>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</select>
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
<p class="control">
|
||||||
|
<a class="button is-primary" onClick={onAddTaskClick}>
|
||||||
|
Ajouter
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<th colSpan={3}>Total</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colSpan={3} class={style.noBorder}></td>
|
||||||
|
<td>{totals.optimistic}</td>
|
||||||
|
<td>{totals.likely}</td>
|
||||||
|
<td>{totals.pessimistic}</td>
|
||||||
|
</tr>
|
||||||
|
</tfoot>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TaskTable;
|
|
@ -0,0 +1,59 @@
|
||||||
|
import { FunctionalComponent, h } from "preact";
|
||||||
|
import { Project } from "../../models/project";
|
||||||
|
import { Task } from "../../models/task";
|
||||||
|
import { useState, useEffect } from "preact/hooks";
|
||||||
|
import { getProjectWeightedMean, getProjectStandardDeviation } from "../../util/stat";
|
||||||
|
|
||||||
|
export interface TimePreviewProps {
|
||||||
|
project: Project
|
||||||
|
}
|
||||||
|
|
||||||
|
const TimePreview: FunctionalComponent<TimePreviewProps> = ({ project }) => {
|
||||||
|
const [ estimations, setEstimations ] = useState({
|
||||||
|
p99: { e: '0', sd: '0' },
|
||||||
|
p90: { e: '0', sd: '0' },
|
||||||
|
p68: { e: '0', sd: '0' },
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const projectWeightedMean = getProjectWeightedMean(project).toFixed(2);
|
||||||
|
const projectStandardDeviation = getProjectStandardDeviation(project);
|
||||||
|
setEstimations({
|
||||||
|
p99: { e: projectWeightedMean, sd: (projectStandardDeviation * 3).toFixed(2) },
|
||||||
|
p90: { e: projectWeightedMean, sd: (projectStandardDeviation * 1.645).toFixed(2) },
|
||||||
|
p68: { e: projectWeightedMean, sd: (projectStandardDeviation).toFixed(2) },
|
||||||
|
})
|
||||||
|
}, [project.tasks]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div class="table-container">
|
||||||
|
<table class="table is-bordered is-striped is-fullwidth">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th colSpan={2}>Prévisionnel temps</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Niveau de confiance</th>
|
||||||
|
<th>Estimation</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>>= 99.7%</td>
|
||||||
|
<td>{`${estimations.p99.e} ± ${estimations.p99.sd} j/h`}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>>= 90%</td>
|
||||||
|
<td>{`${estimations.p90.e} ± ${estimations.p90.sd} j/h`}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>>= 68%</td>
|
||||||
|
<td>{`${estimations.p68.e} ± ${estimations.p68.sd} j/h`}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TimePreview;
|
|
@ -0,0 +1,2 @@
|
||||||
|
// This file is automatically generated from your CSS. Any edits will be overwritten.
|
||||||
|
export const app: string;
|
|
@ -0,0 +1,21 @@
|
||||||
|
// Mock Browser API's which are not supported by JSDOM, e.g. ServiceWorker, LocalStorage
|
||||||
|
/**
|
||||||
|
* An example how to mock localStorage is given below 👇
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
// Mocks localStorage
|
||||||
|
const localStorageMock = (function() {
|
||||||
|
let store = {};
|
||||||
|
|
||||||
|
return {
|
||||||
|
getItem: (key) => store[key] || null,
|
||||||
|
setItem: (key, value) => store[key] = value.toString(),
|
||||||
|
clear: () => store = {}
|
||||||
|
};
|
||||||
|
|
||||||
|
})();
|
||||||
|
|
||||||
|
Object.defineProperty(window, 'localStorage', {
|
||||||
|
value: localStorageMock
|
||||||
|
}); */
|
|
@ -0,0 +1,3 @@
|
||||||
|
// This fixed an error related to the CSS and loading gif breaking my Jest test
|
||||||
|
// See https://facebook.github.io/jest/docs/en/webpack.html#handling-static-assets
|
||||||
|
export default "test-file-stub";
|
|
@ -0,0 +1,12 @@
|
||||||
|
import { h } from "preact";
|
||||||
|
// See: https://github.com/mzgoddard/preact-render-spy
|
||||||
|
import { shallow } from "preact-render-spy";
|
||||||
|
import Header from "../components/header";
|
||||||
|
|
||||||
|
describe("Initial Test of the Header", () => {
|
||||||
|
test("Header renders 3 nav items", () => {
|
||||||
|
const context = shallow(<Header />);
|
||||||
|
expect(context.find("h1").text()).toBe("Preact App");
|
||||||
|
expect(context.find("Link").length).toBe(3);
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,24 @@
|
||||||
|
import { Task } from "../models/task";
|
||||||
|
import { Project } from "../models/project";
|
||||||
|
|
||||||
|
export function getTaskWeightedMean(t: Task): number {
|
||||||
|
return (t.estimations.optimistic + (4*t.estimations.likely) + t.estimations.pessimistic) / 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getTaskStandardDeviation(t: Task): number {
|
||||||
|
return (t.estimations.pessimistic - t.estimations.optimistic) / 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getProjectWeightedMean(p : Project): number {
|
||||||
|
return Object.values(p.tasks).reduce((sum: number, t: Task) => {
|
||||||
|
sum += getTaskWeightedMean(t);
|
||||||
|
return sum;
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getProjectStandardDeviation(p : Project): number {
|
||||||
|
return Math.sqrt(Object.values(p.tasks).reduce((sum: number, t: Task) => {
|
||||||
|
sum += Math.pow(getTaskStandardDeviation(t), 2);
|
||||||
|
return sum;
|
||||||
|
}, 0));
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
import bs58 from 'bs58';
|
||||||
|
|
||||||
|
const hex: string[] = [];
|
||||||
|
|
||||||
|
for (var i = 0; i < 256; i++) {
|
||||||
|
hex[i] = (i < 16 ? '0' : '') + (i).toString(16);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function uuidV4(): string {
|
||||||
|
const r = crypto.getRandomValues(new Uint8Array(16));
|
||||||
|
|
||||||
|
r[6] = r[6] & 0x0f | 0x40;
|
||||||
|
r[8] = r[8] & 0x3f | 0x80;
|
||||||
|
|
||||||
|
return (
|
||||||
|
hex[r[0]] +
|
||||||
|
hex[r[1]] +
|
||||||
|
hex[r[2]] +
|
||||||
|
hex[r[3]] +
|
||||||
|
"-" +
|
||||||
|
hex[r[4]] +
|
||||||
|
hex[r[5]] +
|
||||||
|
"-" +
|
||||||
|
hex[r[6]] +
|
||||||
|
hex[r[7]] +
|
||||||
|
"-" +
|
||||||
|
hex[r[8]] +
|
||||||
|
hex[r[9]] +
|
||||||
|
"-" +
|
||||||
|
hex[r[10]] +
|
||||||
|
hex[r[11]] +
|
||||||
|
hex[r[12]] +
|
||||||
|
hex[r[13]] +
|
||||||
|
hex[r[14]] +
|
||||||
|
hex[r[15]]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function toUTF8Bytes(str: string): number[] {
|
||||||
|
var utf8 = unescape(encodeURIComponent(str));
|
||||||
|
|
||||||
|
var arr: number[] = [];
|
||||||
|
for (var i = 0; i < utf8.length; i++) {
|
||||||
|
arr.push(utf8.charCodeAt(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
return arr
|
||||||
|
}
|
||||||
|
|
||||||
|
export function base58UUID(): string {
|
||||||
|
const uuid = uuidV4();
|
||||||
|
return bs58.encode(toUTF8Bytes(uuid));
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
/* Basic Options */
|
||||||
|
"target": "ES5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'. */
|
||||||
|
"module": "ESNext", /* Specify module code generation: 'none', commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
|
||||||
|
// "lib": [], /* Specify library files to be included in the compilation: */
|
||||||
|
"allowJs": true, /* Allow javascript files to be compiled. */
|
||||||
|
// "checkJs": true, /* Report errors in .js files. */
|
||||||
|
"jsx": "react", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
|
||||||
|
"jsxFactory": "h", /* Specify the JSX factory function to use when targeting react JSX emit, e.g. React.createElement or h. */
|
||||||
|
// "declaration": true, /* Generates corresponding '.d.ts' file. */
|
||||||
|
// "sourceMap": true, /* Generates corresponding '.map' file. */
|
||||||
|
// "outFile": "./", /* Concatenate and emit output to single file. */
|
||||||
|
// "outDir": "./", /* Redirect output structure to the directory. */
|
||||||
|
// "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
|
||||||
|
// "removeComments": true, /* Do not emit comments to output. */
|
||||||
|
// "noEmit": true, /* Do not emit outputs. */
|
||||||
|
// "importHelpers": true, /* Import emit helpers from 'tslib'. */
|
||||||
|
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
|
||||||
|
// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
|
||||||
|
|
||||||
|
/* Strict Type-Checking Options */
|
||||||
|
"strict": true, /* Enable all strict type-checking options. */
|
||||||
|
// "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
|
||||||
|
// "strictNullChecks": true, /* Enable strict null checks. */
|
||||||
|
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
|
||||||
|
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
|
||||||
|
|
||||||
|
/* Additional Checks */
|
||||||
|
// "noUnusedLocals": true, /* Report errors on unused locals. */
|
||||||
|
// "noUnusedParameters": true, /* Report errors on unused parameters. */
|
||||||
|
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
|
||||||
|
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
|
||||||
|
|
||||||
|
/* Module Resolution Options */
|
||||||
|
"moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
|
||||||
|
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
|
||||||
|
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
|
||||||
|
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
|
||||||
|
// "typeRoots": [], /* List of folders to include type definitions from. */
|
||||||
|
// "types": [], /* Type declaration files to be included in compilation. */
|
||||||
|
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
|
||||||
|
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
|
||||||
|
|
||||||
|
/* Source Map Options */
|
||||||
|
// "sourceRoot": "./", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
|
||||||
|
// "mapRoot": "./", /* Specify the location where debugger should locate map files instead of generated locations. */
|
||||||
|
// "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
|
||||||
|
// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
|
||||||
|
|
||||||
|
/* Experimental Options */
|
||||||
|
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
|
||||||
|
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
|
||||||
|
"esModuleInterop": true
|
||||||
|
},
|
||||||
|
"include": ["src/**/*.tsx", "src/**/*.ts"]
|
||||||
|
}
|