├── .env
├── .eslintrc.js
├── .gitignore
├── .storybook
├── main.js
└── preview.js
├── README.md
├── README_CN.md
├── README_DE.md
├── README_ES.md
├── README_FR.md
├── README_IN.md
├── README_JP.md
├── README_RU.md
├── babel.config.json
├── jest.config.js
├── jsconfig.json
├── package.json
├── public
├── CNAME
├── description.gif
├── favicon.ico
├── github.svg
├── hn.png
├── index.html
├── logo.svg
├── logo192.png
├── logo512.png
├── manifest.json
├── readme_logo.png
├── readme_logo_cn.png
├── readme_logo_de.png
├── readme_logo_es.png
├── readme_logo_fr.png
├── readme_logo_in.png
├── readme_logo_jp.png
├── readme_logo_ru.png
├── robots.txt
├── showcase.html
├── showcase_cn.html
├── showcase_de.html
├── showcase_es.html
├── showcase_fr.html
├── showcase_in.html
├── showcase_jp.html
└── showcase_ru.html
├── src
├── Introduction.stories.mdx
├── TinyManager
│ ├── assets
│ │ ├── locales
│ │ │ ├── cn
│ │ │ │ └── translations.json
│ │ │ ├── de
│ │ │ │ └── translations.json
│ │ │ ├── en
│ │ │ │ └── translations.json
│ │ │ ├── es
│ │ │ │ └── translations.json
│ │ │ ├── fr
│ │ │ │ └── translations.json
│ │ │ ├── in
│ │ │ │ └── translations.json
│ │ │ ├── jp
│ │ │ │ └── translations.json
│ │ │ └── ru
│ │ │ │ └── translations.json
│ │ └── quotes
│ │ │ └── index.js
│ ├── components
│ │ ├── CircularProgressWithLabel
│ │ │ ├── CircularProgressWithLabel.js
│ │ │ ├── CircularProgressWithLabel.stories.js
│ │ │ └── index.js
│ │ ├── ConfirmationDialog
│ │ │ ├── ConfirmationDialog.js
│ │ │ ├── ConfirmationDialog.stories.js
│ │ │ └── index.js
│ │ ├── IconButton
│ │ │ ├── IconButton.js
│ │ │ ├── IconButton.stories.js
│ │ │ └── index.js
│ │ ├── Loader
│ │ │ ├── Loader.js
│ │ │ ├── Loader.stories.js
│ │ │ └── index.js
│ │ ├── OutlinedSelect
│ │ │ ├── OutlinedSelect.js
│ │ │ └── index.js
│ │ ├── Paper
│ │ │ ├── Paper.js
│ │ │ ├── Paper.stories.js
│ │ │ └── index.js
│ │ ├── ProjectCard
│ │ │ ├── ProjectCard.js
│ │ │ ├── ProjectCard.stories.js
│ │ │ └── index.js
│ │ ├── ProjectForm
│ │ │ ├── ProjectForm.js
│ │ │ ├── ProjectForm.stories.js
│ │ │ └── index.js
│ │ ├── TaskCard
│ │ │ ├── TaskCard.js
│ │ │ ├── TaskCard.stories.js
│ │ │ └── index.js
│ │ ├── TaskForm
│ │ │ ├── TaskForm.js
│ │ │ ├── TaskForm.stories.js
│ │ │ └── index.js
│ │ ├── Todo
│ │ │ ├── Todo.js
│ │ │ ├── Todo.stories.js
│ │ │ └── index.js
│ │ ├── TodoForm
│ │ │ ├── TodoForm.js
│ │ │ ├── TodoForm.stories.js
│ │ │ └── index.js
│ │ ├── TodoList
│ │ │ ├── TodoList.js
│ │ │ ├── TodoList.stories.js
│ │ │ └── index.js
│ │ ├── Topbar
│ │ │ └── index.js
│ │ └── utils.js
│ ├── containers
│ │ ├── Content
│ │ │ └── index.js
│ │ ├── Home
│ │ │ └── index.js
│ │ ├── Notes
│ │ │ └── index.js
│ │ ├── Projects
│ │ │ ├── ProjectForm.js
│ │ │ ├── ProjectList.js
│ │ │ ├── ProjectView.js
│ │ │ ├── TaskForm.js
│ │ │ └── index.js
│ │ ├── Todos
│ │ │ ├── TodoFormDialog.js
│ │ │ └── index.js
│ │ └── Topbar
│ │ │ └── index.js
│ ├── decorators
│ │ └── index.js
│ ├── hooks
│ │ └── useDialog.js
│ ├── index.js
│ ├── providers
│ │ ├── SnackbarProvider.js
│ │ ├── ThemeProvider.js
│ │ └── TranslationProvider.js
│ ├── services
│ │ ├── DbService.js
│ │ ├── DbService.spec.js
│ │ ├── LocalStorageService.js
│ │ ├── LocalStorageService.spec.js
│ │ ├── QuoteService.js
│ │ ├── QuoteService.spec.js
│ │ ├── TinyManagerAPI.js
│ │ └── Utils.js
│ └── types
│ │ └── index.js
├── __snapshots__
│ └── storybook.spec.js.snap
├── index.css
├── index.js
├── serviceWorker.js
├── setupTests.js
└── storybook.spec.js
└── yarn.lock
/.env:
--------------------------------------------------------------------------------
1 | PORT=3050
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | env: {
3 | browser: true,
4 | commonjs: true,
5 | jest: true,
6 | es6: true,
7 | },
8 | extends: [
9 | "eslint:recommended",
10 | "plugin:react/recommended",
11 | "plugin:react-hooks/recommended",
12 | ],
13 | plugins: ["react"],
14 | parser: "@babel/eslint-parser",
15 | parserOptions: {
16 | ecmaFeatures: {
17 | jsx: true,
18 | },
19 | },
20 | settings: {
21 | react: {
22 | version: "detect",
23 | },
24 | },
25 | };
26 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/.storybook/main.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | "stories": [
3 | "../src/**/*.stories.mdx",
4 | "../src/**/*.stories.@(js|jsx|ts|tsx)"
5 | ],
6 | "addons": [
7 | "@storybook/addon-links",
8 | "@storybook/addon-essentials",
9 | "@storybook/preset-create-react-app"
10 | ]
11 | }
--------------------------------------------------------------------------------
/.storybook/preview.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ThemeProvider from "../src/TinyManager/providers/ThemeProvider";
3 |
4 | export const parameters = {
5 | actions: { argTypesRegex: "^on[A-Z].*" },
6 | };
7 |
8 | export const globalTypes = {
9 | darkMode: {
10 | name: "Dark mode",
11 | description: "Dark mode view for component",
12 | defaultValue: false,
13 | toolbar: {
14 | items: [
15 | { icon: "circle", value: true, title: "Dark" },
16 | { icon: "circlehollow", value: false, title: "Light" },
17 | ],
18 | },
19 | },
20 | };
21 |
22 | const withThemeProvider = (Story, context) => {
23 | const darkMode = context.globals.darkMode;
24 | return (
25 |
26 |
27 |
28 | );
29 | };
30 |
31 | export const decorators = [withThemeProvider];
32 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | #
6 |
7 |
8 | A simple offline project manager for your pet projects.
9 |
10 |
11 |
12 |
13 |
14 |
15 | [](https://nishantpainter.github.io/tiny-manager) [](http://makeapullrequest.com) [](https://github.com/nishantpainter/tiny-manager/blob/main/license.txt) [](https://github.com/nishantpainter/tiny-manager/commits/master) [](https://github.com/nishantpainter/tiny-manager/issues)
16 |
17 | [English](https://github.com/nishantpainter/tiny-manager/blob/master/README.md) - [Français](https://github.com/nishantpainter/tiny-manager/blob/master/README_FR.md) - [Español](https://github.com/nishantpainter/tiny-manager/blob/master/README_ES.md) - [Pусский](https://github.com/nishantpainter/tiny-manager/blob/master/README_RU.md) - [Deutsch](https://github.com/nishantpainter/tiny-manager/blob/master/README_DE.md) - [हिंदी](https://github.com/nishantpainter/tiny-manager/blob/master/README_IN.md) - [中文](https://github.com/nishantpainter/tiny-manager/blob/master/README_CN.md) - [日本語](https://github.com/nishantpainter/tiny-manager/blob/master/README_JP.md)
18 |
19 | # Table of Contents
20 |
21 | - **[Introduction](#introduction)**
22 | - **[Features](#features)**
23 | - **[Dark Mode](#dark-mode)**
24 | - **[Localization](#localization)**
25 | - **[Offline Usage](#offline-usage)**
26 | - **[Development](#development)**
27 | - **[Privacy](#privacy)**
28 |
29 | ## Introduction
30 |
31 | Tiny Manager is an offline first simple application that assists you in managing your pet projects. Along with project management it allows mundane management using todos and a simple notepad application, all at one place.
32 |
33 | ## Features
34 |
35 | A few of the things you can do with Tiny Manager:
36 |
37 | * Add, Edit, Save Notes
38 | * Add, Edit, Delete Todos
39 | * Pet Project management
40 | * Project completion status
41 | * Filtering and sorting
42 | * Issues priority setting
43 | * Dark Mode
44 | * Localization Support
45 | * Offline capable
46 | * Offline local storage
47 |
48 | ## Dark Mode
49 |
50 | Dark mode helps you quickly turn the screen to dark ( while using during night). Toolbar offers you icon button that serve as an on/off switch for dark mode.
51 |
52 | ## Localization
53 |
54 | Tiny Manager offers local support for language including :
55 |
56 | - English
57 | - Français
58 | - Español
59 | - Pусский
60 | - Deutsch
61 | - हिंदी
62 | - 中文
63 | - 日本語
64 |
65 | ## Offline Usage
66 |
67 | The application is registered with service workers and behaves as a progressive web application ([PWA](https://en.wikipedia.org/wiki/Progressive_web_application)). For offline usage you can select the **Add To Home** option, while accessing application in browser, to install the application locally to your mobile devices.
68 |
69 | ## Development
70 |
71 | The application is scaffolded using create-react-app ([CRA](https://create-react-app.dev/docs/getting-started/)). You can clone the [Tiny Manager](https://github.com/nishantpainter/tiny-manager) repository for custom development.
72 |
73 | To run the application locally on your machine you can follow the steps below :
74 |
75 | - Using npm
76 |
77 | ```
78 | 1: git clone https://github.com/nishantpainter/tiny-manager.git
79 | 2: cd tiny-manager
80 | 3: npm install
81 | 4: npm run build
82 | 5: sudo npm install -g serve
83 | 6: serve -s build
84 | ```
85 |
86 | - Using yarn
87 |
88 | ```
89 | 1: git clone https://github.com/nishantpainter/tiny-manager.git
90 | 2: cd tiny-manager
91 | 3: yarn install
92 | 4: yarn build
93 | 5: sudo npm install -g serve
94 | 6: serve -s build
95 | ```
96 |
97 | ## Privacy
98 |
99 | The application makes use of local storage for persisting your work data and does not store any Tiny Manager data on any sort of server. The application uses Google analytics to get an overview of the application usage.
100 |
--------------------------------------------------------------------------------
/README_CN.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | #
6 |
7 |
8 | 一个简单的离线项目管理器,适合您的宠物项目。
9 |
10 |
11 |
12 |
13 |
14 |
15 | [](https://nishantpainter.github.io/tiny-manager) [](http://makeapullrequest.com) [](https://github.com/nishantpainter/tiny-manager/blob/main/license.txt) [](https://github.com/nishantpainter/tiny-manager/commits/master) [](https://github.com/nishantpainter/tiny-manager/issues)
16 |
17 | [English](https://github.com/nishantpainter/tiny-manager/blob/master/README.md) - [Français](https://github.com/nishantpainter/tiny-manager/blob/master/README_FR.md) - [Español](https://github.com/nishantpainter/tiny-manager/blob/master/README_ES.md) - [Pусский](https://github.com/nishantpainter/tiny-manager/blob/master/README_RU.md) - [Deutsch](https://github.com/nishantpainter/tiny-manager/blob/master/README_DE.md) - [हिंदी](https://github.com/nishantpainter/tiny-manager/blob/master/README_IN.md) - [中文](https://github.com/nishantpainter/tiny-manager/blob/master/README_CN.md) - [日本語](https://github.com/nishantpainter/tiny-manager/blob/master/README_JP.md)
18 |
19 | # 目录
20 |
21 | - **[介绍](#介绍)**
22 | - **[特征](#特征)**
23 | - **[深色模式](#深色模式)**
24 | - **[本土化](#本土化)**
25 | - **[离线使用](#离线使用)**
26 | - **[发展](#发展)**
27 | - **[隐私](#隐私)**
28 |
29 | ## 介绍
30 |
31 | Tiny Manager 是一款离线的第一个简单应用程序,可帮助您管理您的宠物项目。除了项目管理之外,它还允许使用待办事项和简单的记事本应用程序在一个地方进行日常管理。
32 |
33 | ## 特征
34 |
35 | 您可以使用 Tiny Manager 执行以下操作:
36 |
37 | * 添加、编辑、保存注释
38 | * 添加、编辑、删除待办事项
39 | * 宠物项目管理
40 | * 项目完成情况
41 | * 过滤和排序
42 | * 问题优先级设置
43 | * 深色模式
44 | * 本地化支持
45 | * 离线能力
46 | * 离线本地存储
47 |
48 | ## 深色模式
49 |
50 | 深色模式可帮助您快速将屏幕调暗(夜间使用时)。工具栏为您提供图标按钮,用作暗模式的开/关开关。
51 |
52 | ## 本土化
53 |
54 | Tiny Manager 提供本地语言支持,包括:
55 |
56 | - English
57 | - Français
58 | - Español
59 | - Pусский
60 | - Deutsch
61 | - हिंदी
62 | - 中文
63 | - 日本語
64 |
65 | ## 离线使用
66 |
67 | 该应用程序已向 Service Worker 注册,并表现为渐进式 Web 应用程序 ([PWA](https://en.wikipedia.org/wiki/Progressive_web_application)). 对于离线使用,您可以在浏览器中访问应用程序时选择**添加到主页**选项,以将应用程序本地安装到您的移动设备。
68 |
69 | ## 发展
70 |
71 | 该应用程序是使用 create-react-app 搭建的 ([CRA](https://create-react-app.dev/docs/getting-started/)). 您可以克隆 [Tiny Manager](https://github.com/nishantpainter/tiny-manager) 用于定制开发的存储库.
72 |
73 | 要在您的计算机上本地运行该应用程序,您可以按照以下步骤操作:
74 |
75 | - 使用 npm
76 |
77 | ```
78 | 1: git clone https://github.com/nishantpainter/tiny-manager.git
79 | 2: cd tiny-manager
80 | 3: npm install
81 | 4: npm run build
82 | 5: sudo npm install -g serve
83 | 6: serve -s build
84 | ```
85 |
86 | - 使用 yarn
87 |
88 | ```
89 | 1: git clone https://github.com/nishantpainter/tiny-manager.git
90 | 2: cd tiny-manager
91 | 3: yarn install
92 | 4: yarn build
93 | 5: sudo npm install -g serve
94 | 6: serve -s build
95 | ```
96 |
97 | ## 隐私
98 |
99 | 该应用程序利用本地存储来保存您的工作数据,并且不会在任何类型的服务器上存储任何 Tiny Manager 数据。该应用程序使用 Google 分析来获取应用程序使用情况的概述。
100 |
--------------------------------------------------------------------------------
/README_DE.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | #
6 |
7 |
8 | Ein einfacher Offline-Projektmanager für Ihre Lieblingsprojekte.
9 |
10 |
11 |
12 |
13 |
14 |
15 | [](https://nishantpainter.github.io/tiny-manager) [](http://makeapullrequest.com) [](https://github.com/nishantpainter/tiny-manager/blob/main/license.txt) [](https://github.com/nishantpainter/tiny-manager/commits/master) [](https://github.com/nishantpainter/tiny-manager/issues)
16 |
17 | [English](https://github.com/nishantpainter/tiny-manager/blob/master/README.md) - [Français](https://github.com/nishantpainter/tiny-manager/blob/master/README_FR.md) - [Español](https://github.com/nishantpainter/tiny-manager/blob/master/README_ES.md) - [Pусский](https://github.com/nishantpainter/tiny-manager/blob/master/README_RU.md) - [Deutsch](https://github.com/nishantpainter/tiny-manager/blob/master/README_DE.md) - [हिंदी](https://github.com/nishantpainter/tiny-manager/blob/master/README_IN.md) - [中文](https://github.com/nishantpainter/tiny-manager/blob/master/README_CN.md) - [日本語](https://github.com/nishantpainter/tiny-manager/blob/master/README_JP.md)
18 |
19 | # Inhaltsverzeichnis
20 |
21 | - **[Einführung](#einführung)**
22 | - **[Merkmale](#merkmale)**
23 | - **[Dunkler Modus](#dunkler-modus)**
24 | - **[Lokalisierung](#lokalisierung)**
25 | - **[Offline-Nutzung](#offline-nutzung)**
26 | - **[Entwicklung](#entwicklung)**
27 | - **[Privatsphäre](#privatsphäre)**
28 |
29 | ## Einführung
30 |
31 | Tiny Manager ist eine einfache Offline-Anwendung, die Sie bei der Verwaltung Ihrer Lieblingsprojekte unterstützt. Zusammen mit dem Projektmanagement ermöglicht es die alltägliche Verwaltung mithilfe von Todos und einer einfachen Notizblockanwendung, alles an einem Ort.
32 |
33 | ## Merkmale
34 |
35 | Einige der Dinge, die Sie mit Tiny Manager tun können:
36 |
37 | * Notizen hinzufügen, bearbeiten, speichern
38 | * Aufgaben hinzufügen, bearbeiten, löschen
39 | * Projektmanagement für Haustiere
40 | * Projektabschlussstatus
41 | * Filtern und Sortieren
42 | * Prioritätseinstellung für Probleme
43 | * Dunkler Modus
44 | * Lokalisierungsunterstützung
45 | * Offlinefähig
46 | * Lokaler Offline-Speicher
47 |
48 | ## Dunkler Modus
49 |
50 | Der Dunkelmodus hilft Ihnen, den Bildschirm schnell dunkel zu machen (während der Verwendung nachts). Die Symbolleiste bietet Ihnen eine Symbolschaltfläche, die als Ein-/Ausschalter für den Dunkelmodus dient.
51 |
52 | ## Lokalisierung
53 |
54 | Tiny Manager bietet lokale Unterstützung für Sprachen, darunter:
55 |
56 | - English
57 | - Français
58 | - Español
59 | - Pусский
60 | - Deutsch
61 | - हिंदी
62 | - 中文
63 | - 日本語
64 |
65 | ## Offline-Nutzung
66 |
67 | Die Anwendung wird bei Servicemitarbeitern registriert und verhält sich wie eine progressive Webanwendung ([PWA](https://en.wikipedia.org/wiki/Progressive_web_application)). Für die Offline-Nutzung können Sie beim Zugriff auf die Anwendung im Browser die Option **Zur Startseite hinzufügen** auswählen, um die Anwendung lokal auf Ihren Mobilgeräten zu installieren.
68 |
69 | ## Entwicklung
70 |
71 | Die Anwendung wird mithilfe von create-react-app als Gerüst erstellt ([CRA](https://create-react-app.dev/docs/getting-started/)). Sie können das klonen [Tiny Manager](https://github.com/nishantpainter/tiny-manager) repository für kundenspezifische Entwicklung.
72 |
73 | Um die Anwendung lokal auf Ihrem Computer auszuführen, können Sie die folgenden Schritte ausführen:
74 |
75 | - Verwendung von npm
76 |
77 | ```
78 | 1: git clone https://github.com/nishantpainter/tiny-manager.git
79 | 2: cd tiny-manager
80 | 3: npm install
81 | 4: npm run build
82 | 5: sudo npm install -g serve
83 | 6: serve -s build
84 | ```
85 |
86 | - Verwendung von yarn
87 |
88 | ```
89 | 1: git clone https://github.com/nishantpainter/tiny-manager.git
90 | 2: cd tiny-manager
91 | 3: yarn install
92 | 4: yarn build
93 | 5: sudo npm install -g serve
94 | 6: serve -s build
95 | ```
96 |
97 | ## Privatsphäre
98 |
99 | Die Anwendung nutzt den lokalen Speicher zur Beibehaltung Ihrer Arbeitsdaten und speichert keine Tiny Manager-Daten auf irgendeinem Server. Die Anwendung nutzt Google Analytics, um einen Überblick über die Anwendungsnutzung zu erhalten.
100 |
--------------------------------------------------------------------------------
/README_ES.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | #
6 |
7 |
8 | Un simple administrador de proyectos fuera de línea para sus proyectos favoritos.
9 |
10 |
11 |
12 |
13 |
14 |
15 | [](https://nishantpainter.github.io/tiny-manager) [](http://makeapullrequest.com) [](https://github.com/nishantpainter/tiny-manager/blob/main/license.txt) [](https://github.com/nishantpainter/tiny-manager/commits/master) [](https://github.com/nishantpainter/tiny-manager/issues)
16 |
17 | [English](https://github.com/nishantpainter/tiny-manager/blob/master/README.md) - [Français](https://github.com/nishantpainter/tiny-manager/blob/master/README_FR.md) - [Español](https://github.com/nishantpainter/tiny-manager/blob/master/README_ES.md) - [Pусский](https://github.com/nishantpainter/tiny-manager/blob/master/README_RU.md) - [Deutsch](https://github.com/nishantpainter/tiny-manager/blob/master/README_DE.md) - [हिंदी](https://github.com/nishantpainter/tiny-manager/blob/master/README_IN.md) - [中文](https://github.com/nishantpainter/tiny-manager/blob/master/README_CN.md) - [日本語](https://github.com/nishantpainter/tiny-manager/blob/master/README_JP.md)
18 |
19 | # Tabla de contenido
20 |
21 | - **[Introducción](#introducción)**
22 | - **[Características](#características)**
23 | - **[Modo oscuro](#modo-oscuro)**
24 | - **[Localización](#localización)**
25 | - **[Uso fuera de línea](#uso-fuera-de-línea)**
26 | - **[Desarrollo](#desarrollo)**
27 | - **[Privacidad](#privacidad)**
28 |
29 | ## Introducción
30 |
31 | Tiny Manager es una primera aplicación simple fuera de línea que lo ayuda a administrar sus proyectos favoritos. Junto con la gestión de proyectos, permite la gestión mundana utilizando todos y una aplicación de bloc de notas simple, todo en un solo lugar.
32 |
33 | ## Características
34 |
35 | Algunas de las cosas que puede hacer con Tiny Manager:
36 |
37 | * Agregar, editar, guardar notas
38 | * Agregar, editar, eliminar todos
39 | * Gestión de proyectos de mascotas
40 | * Estado de finalización del proyecto
41 | * Filtrado y clasificación
42 | * Establecimiento de prioridades de problemas
43 | * Modo oscuro
44 | * Soporte de localización
45 | * Capacidad sin conexión
46 | * Almacenamiento local sin conexión
47 |
48 | ## Modo oscuro
49 |
50 | El modo oscuro lo ayuda a oscurecer rápidamente la pantalla (mientras se usa durante la noche). La barra de herramientas le ofrece un botón de icono que sirve como interruptor de encendido/apagado para el modo oscuro.
51 |
52 | ## Localización
53 |
54 | Tiny Manager ofrece soporte local para idiomas que incluyen:
55 |
56 | - English
57 | - Français
58 | - Español
59 | - Pусский
60 | - Deutsch
61 | - हिंदी
62 | - 中文
63 | - 日本語
64 |
65 | ## Uso fuera de línea
66 |
67 | La aplicación está registrada con los trabajadores del servicio y se comporta como una aplicación web progresiva ([PWA](https://en.wikipedia.org/wiki/Progressive_web_application)). Para el uso sin conexión, puede seleccionar la opción **Agregar a Inicio**, mientras accede a la aplicación en el navegador, para instalar la aplicación localmente en sus dispositivos móviles.
68 |
69 | ## Desarrollo
70 |
71 | La aplicación está estructurada usando create-react-app ([CRA](https://create-react-app.dev/docs/getting-started/)). Puedes clonar el [Tiny Manager](https://github.com/nishantpainter/tiny-manager) repositorio para desarrollo personalizado.
72 |
73 | Para ejecutar la aplicación localmente en su máquina, puede seguir los pasos a continuación:
74 |
75 | - Usando npm
76 |
77 | ```
78 | 1: git clone https://github.com/nishantpainter/tiny-manager.git
79 | 2: cd tiny-manager
80 | 3: npm install
81 | 4: npm run build
82 | 5: sudo npm install -g serve
83 | 6: serve -s build
84 | ```
85 |
86 | - Usando yarn
87 |
88 | ```
89 | 1: git clone https://github.com/nishantpainter/tiny-manager.git
90 | 2: cd tiny-manager
91 | 3: yarn install
92 | 4: yarn build
93 | 5: sudo npm install -g serve
94 | 6: serve -s build
95 | ```
96 |
97 | ## Privacidad
98 |
99 | La aplicación utiliza el almacenamiento local para conservar los datos de su trabajo y no almacena ningún dato de Tiny Manager en ningún tipo de servidor. La aplicación utiliza Google Analytics para obtener una visión general del uso de la aplicación.
--------------------------------------------------------------------------------
/README_FR.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | #
6 |
7 |
8 | Un gestionnaire de projet hors ligne simple pour vos projets favoris.
9 |
10 |
11 |
12 |
13 |
14 |
15 | [](https://nishantpainter.github.io/tiny-manager) [](http://makeapullrequest.com) [](https://github.com/nishantpainter/tiny-manager/blob/main/license.txt) [](https://github.com/nishantpainter/tiny-manager/commits/master) [](https://github.com/nishantpainter/tiny-manager/issues)
16 |
17 | [English](https://github.com/nishantpainter/tiny-manager/blob/master/README.md) - [Français](https://github.com/nishantpainter/tiny-manager/blob/master/README_FR.md) - [Español](https://github.com/nishantpainter/tiny-manager/blob/master/README_ES.md) - [Pусский](https://github.com/nishantpainter/tiny-manager/blob/master/README_RU.md) - [Deutsch](https://github.com/nishantpainter/tiny-manager/blob/master/README_DE.md) - [हिंदी](https://github.com/nishantpainter/tiny-manager/blob/master/README_IN.md) - [中文](https://github.com/nishantpainter/tiny-manager/blob/master/README_CN.md) - [日本語](https://github.com/nishantpainter/tiny-manager/blob/master/README_JP.md)
18 |
19 | # Table des matières
20 |
21 | - **[Introduction](#introduction)**
22 | - **[Caractéristiques](#caractéristiques)**
23 | - **[Mode sombre](#mode-sombre)**
24 | - **[Localisation](#localisation)**
25 | - **[Utilisation hors ligne](#utilisation-hors-ligne)**
26 | - **[Développement](#développement)**
27 | - **[Confidentialité](#confidentialité)**
28 |
29 | ## Introduction
30 |
31 | Tiny Manager est une première application simple hors ligne qui vous aide à gérer vos projets favoris. Parallèlement à la gestion de projet, il permet une gestion banale à l'aide de tâches et d'une simple application de bloc-notes, le tout au même endroit.
32 |
33 | ## Caractéristiques
34 |
35 | A few of the things you can do with Tiny Manager:
36 |
37 | * Ajouter, modifier, enregistrer des notes
38 | * Ajouter, modifier, supprimer des tâches
39 | * Gestion de projets pour animaux de compagnie
40 | * Statut d'achèvement du projet
41 | * Filtrage et tri
42 | * Établit la priorité des problèmes
43 | * Mode sombre
44 | * Prise en charge de la localisation
45 | * Capable hors ligne
46 | * Stockage local hors ligne
47 |
48 | ## Mode sombre
49 |
50 | Le mode sombre vous aide à rendre rapidement l'écran sombre (lors de l'utilisation pendant la nuit). La barre d'outils vous offre un bouton d'icône qui sert d'interrupteur marche/arrêt pour le mode sombre.
51 |
52 | ## Localisation
53 |
54 | Tiny Manager offre un support local pour la langue, notamment :
55 |
56 | - English
57 | - Français
58 | - Español
59 | - Pусский
60 | - Deutsch
61 | - हिंदी
62 | - 中文
63 | - 日本語
64 |
65 | ## Utilisation hors ligne
66 |
67 | L'application est enregistrée auprès des service workers et se comporte comme une application web progressive ([PWA](https://en.wikipedia.org/wiki/Progressive_web_application)). Pour une utilisation hors ligne, vous pouvez sélectionner l'option ** Ajouter à la maison **, tout en accédant à l'application dans le navigateur, pour installer l'application localement sur vos appareils mobiles.
68 |
69 | ## Développement
70 |
71 | L'application est échafaudée à l'aide de create-react-app ([CRA](https://create-react-app.dev/docs/getting-started/)). Vous pouvez cloner le [Tiny Manager](https://github.com/nishantpainter/tiny-manager) référentiel pour le développement personnalisé.
72 |
73 | Pour exécuter l'application localement sur votre machine vous pouvez suivre les étapes ci-dessous :
74 |
75 | - Utiliser npm
76 |
77 | ```
78 | 1: git clone https://github.com/nishantpainter/tiny-manager.git
79 | 2: cd tiny-manager
80 | 3: npm install
81 | 4: npm run build
82 | 5: sudo npm install -g serve
83 | 6: serve -s build
84 | ```
85 |
86 | - Utiliser yarn
87 |
88 | ```
89 | 1: git clone https://github.com/nishantpainter/tiny-manager.git
90 | 2: cd tiny-manager
91 | 3: yarn install
92 | 4: yarn build
93 | 5: sudo npm install -g serve
94 | 6: serve -s build
95 | ```
96 |
97 | ## Confidentialité
98 |
99 | L'application utilise le stockage local pour conserver vos données de travail et ne stocke aucune donnée Tiny Manager sur aucun type de serveur. L'application utilise Google Analytics pour obtenir un aperçu de l'utilisation de l'application.
--------------------------------------------------------------------------------
/README_IN.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | #
6 |
7 |
8 | आपके पसंदीदा प्रोजेक्ट के लिए एक सरल ऑफ़लाइन प्रोजेक्ट प्रबंधक।
9 |
10 |
11 |
12 |
13 |
14 |
15 | [](https://nishantpainter.github.io/tiny-manager) [](http://makeapullrequest.com) [](https://github.com/nishantpainter/tiny-manager/blob/main/license.txt) [](https://github.com/nishantpainter/tiny-manager/commits/master) [](https://github.com/nishantpainter/tiny-manager/issues)
16 |
17 | [English](https://github.com/nishantpainter/tiny-manager/blob/master/README.md) - [Français](https://github.com/nishantpainter/tiny-manager/blob/master/README_FR.md) - [Español](https://github.com/nishantpainter/tiny-manager/blob/master/README_ES.md) - [Pусский](https://github.com/nishantpainter/tiny-manager/blob/master/README_RU.md) - [Deutsch](https://github.com/nishantpainter/tiny-manager/blob/master/README_DE.md) - [हिंदी](https://github.com/nishantpainter/tiny-manager/blob/master/README_IN.md) - [中文](https://github.com/nishantpainter/tiny-manager/blob/master/README_CN.md) - [日本語](https://github.com/nishantpainter/tiny-manager/blob/master/README_JP.md)
18 |
19 | # विषयसूची
20 |
21 | - **[परिचय](#परिचय)**
22 | - **[विशेषताएँ](#विशेषताएँ)**
23 | - **[डार्क मोड](#डार्क-मोड)**
24 | - **[स्थानीयकरण](#स्थानीयकरण)**
25 | - **[ऑफ़लाइन उपयोग](#ऑफ़लाइन-उपयोग)**
26 | - **[विकास](#विकास)**
27 | - **[गोपनीयता](#गोपनीयता)**
28 |
29 | ## परिचय
30 |
31 | Tiny Manager एक ऑफ़लाइन पहला सरल एप्लिकेशन है जो आपकी पसंदीदा परियोजनाओं को प्रबंधित करने में आपकी सहायता करता है। परियोजना प्रबंधन के साथ-साथ यह एक ही स्थान पर कार्य और एक साधारण नोटपैड एप्लिकेशन का उपयोग करके सांसारिक प्रबंधन की अनुमति देता है।
32 |
33 | ## विशेषताएँ
34 |
35 | कुछ चीजें जो आप Tiny Manager के साथ कर सकते हैं:
36 |
37 | * नोट्स जोड़ें, संपादित करें, सहेजें
38 | * टोडो जोड़ें, संपादित करें, हटाएं
39 | * पालतू परियोजना प्रबंधन
40 | * परियोजना पूर्ण होने की स्थिति
41 | * फ़िल्टरिंग और सॉर्टिंग
42 | * प्राथमिकता सेटिंग जारी करता है
43 | * डार्क मोड
44 | * स्थानीयकरण समर्थन
45 | * ऑफ़लाइन सक्षम
46 | * ऑफ़लाइन स्थानीय भंडारण
47 |
48 | ## डार्क मोड
49 |
50 | डार्क मोड आपको स्क्रीन को जल्दी से डार्क करने में मदद करता है (रात के दौरान उपयोग करते समय)। टूलबार आपको आइकन बटन प्रदान करता है जो डार्क मोड के लिए ऑन/ऑफ स्विच के रूप में काम करता है।
51 | ## स्थानीयकरण
52 |
53 | Tiny Manager भाषा के लिए स्थानीय समर्थन प्रदान करता है जिसमें शामिल हैं:
54 |
55 | - English
56 | - Français
57 | - Español
58 | - Pусский
59 | - Deutsch
60 | - हिंदी
61 | - 中文
62 | - 日本語
63 |
64 | ## ऑफ़लाइन-उपयोग
65 |
66 | एप्लिकेशन सेवा कर्मियों के साथ पंजीकृत है और एक प्रगतिशील वेब एप्लिकेशन के रूप में व्यवहार करता है ([PWA](https://en.wikipedia.org/wiki/Progressive_web_application)). ऑफ़लाइन उपयोग के लिए आप अपने मोबाइल उपकरणों पर स्थानीय रूप से एप्लिकेशन इंस्टॉल करने के लिए ब्राउज़र में एप्लिकेशन एक्सेस करते समय **होम में जोड़ें** विकल्प का चयन कर सकते हैं।
67 |
68 | ## विकास
69 |
70 | एप्लिकेशन को create-react-app का उपयोग करके तैयार किया गया है ([CRA](https://create-react-app.dev/docs/getting-started/)). आप क्लोन कर सकते हैं [Tiny Manager](https://github.com/nishantpainter/tiny-manager) कस्टम विकास के लिए भंडार।
71 |
72 | अपनी मशीन पर एप्लिकेशन को स्थानीय रूप से चलाने के लिए आप नीचे दिए गए चरणों का पालन कर सकते हैं:
73 |
74 | - npm का उपयोग करना
75 |
76 | ```
77 | 1: git clone https://github.com/nishantpainter/tiny-manager.git
78 | 2: cd tiny-manager
79 | 3: npm install
80 | 4: npm run build
81 | 5: sudo npm install -g serve
82 | 6: serve -s build
83 | ```
84 |
85 | - yarn का उपयोग करना
86 |
87 | ```
88 | 1: git clone https://github.com/nishantpainter/tiny-manager.git
89 | 2: cd tiny-manager
90 | 3: yarn install
91 | 4: yarn build
92 | 5: sudo npm install -g serve
93 | 6: serve -s build
94 | ```
95 |
96 | ## गोपनीयता
97 |
98 | एप्लिकेशन आपके कार्य डेटा को बनाए रखने के लिए स्थानीय भंडारण का उपयोग करता है और किसी भी प्रकार के सर्वर पर किसी भी Tiny Manager डेटा को संग्रहीत नहीं करता है। एप्लिकेशन उपयोग का अवलोकन प्राप्त करने के लिए एप्लिकेशन Google एनालिटिक्स का उपयोग करता है।
99 |
--------------------------------------------------------------------------------
/README_JP.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | #
6 |
7 |
8 | あなたの大切なプロジェクトのためのシンプルなオフラインプロジェクトマネージャー。
9 |
10 |
11 |
12 |
13 |
14 |
15 | [](https://nishantpainter.github.io/tiny-manager) [](http://makeapullrequest.com) [](https://github.com/nishantpainter/tiny-manager/blob/main/license.txt) [](https://github.com/nishantpainter/tiny-manager/commits/master) [](https://github.com/nishantpainter/tiny-manager/issues)
16 |
17 | [English](https://github.com/nishantpainter/tiny-manager/blob/master/README.md) - [Français](https://github.com/nishantpainter/tiny-manager/blob/master/README_FR.md) - [Español](https://github.com/nishantpainter/tiny-manager/blob/master/README_ES.md) - [Pусский](https://github.com/nishantpainter/tiny-manager/blob/master/README_RU.md) - [Deutsch](https://github.com/nishantpainter/tiny-manager/blob/master/README_DE.md) - [हिंदी](https://github.com/nishantpainter/tiny-manager/blob/master/README_IN.md) - [中文](https://github.com/nishantpainter/tiny-manager/blob/master/README_CN.md) - [日本語](https://github.com/nishantpainter/tiny-manager/blob/master/README_JP.md)
18 |
19 | # 目次
20 |
21 | - **[序章](#序章)**
22 | - **[特徴](#特徴)**
23 | - **[ダークモード](#ダークモード)**
24 | - **[ローカリゼーション](#ローカリゼーション)**
25 | - **[オフラインでの使用](#オフラインでの使用)**
26 | - **[発達](#発達)**
27 | - **[プライバシー](#プライバシー)**
28 |
29 | ## 序章
30 |
31 | Tiny Manager は、ペット プロジェクトの管理を支援する、オフライン初のシンプルなアプリケーションです。プロジェクト管理に加えて、Todo とシンプルなメモ帳アプリケーションを使用した日常的な管理をすべて 1 か所で行うことができます。
32 |
33 | ## 特徴
34 |
35 | Tiny Manager でできることのいくつかは次のとおりです。
36 |
37 | * メモの追加、編集、保存
38 | * Todoの追加、編集、削除
39 | * ペットプロジェクト管理
40 | ※プロジェクトの完了状況
41 | * フィルタリングと並べ替え
42 | * 優先順位の設定を発行します
43 | * ダークモード
44 | * ローカリゼーションのサポート
45 | * オフライン対応
46 | * オフラインのローカルストレージ
47 |
48 | ## ダークモード
49 |
50 | ダーク モードを使用すると、(夜間の使用中に) 画面をすばやく暗くすることができます。ツールバーには、ダーク モードのオン/オフ スイッチとして機能するアイコン ボタンが表示されます。
51 |
52 | ## ローカリゼーション
53 |
54 | Tiny Manager は、次のような言語のローカル サポートを提供します。
55 |
56 | - English
57 | - Français
58 | - Español
59 | - Pусский
60 | - Deutsch
61 | - हिंदी
62 | - 中文
63 | - 日本語
64 |
65 | ## オフラインでの使用
66 |
67 | アプリケーションは Service Worker に登録され、プログレッシブ Web アプリケーションとして動作します。 ([PWA](https://en.wikipedia.org/wiki/Progressive_web_application)). オフラインで使用する場合は、ブラウザでアプリケーションにアクセスしているときに **ホームに追加** オプションを選択して、アプリケーションをモバイル デバイスにローカルにインストールできます。
68 |
69 | ## 発達
70 |
71 | アプリケーションは create-react-app を使用してスキャフォールディングされます ([CRA](https://create-react-app.dev/docs/getting-started/)). クローンを作成できます [Tiny Manager](https://github.com/nishantpainter/tiny-manager) カスタム開発用のリポジトリ.
72 |
73 | アプリケーションをマシン上でローカルに実行するには、次の手順に従います。
74 |
75 | - 使用する npm
76 |
77 | ```
78 | 1: git clone https://github.com/nishantpainter/tiny-manager.git
79 | 2: cd tiny-manager
80 | 3: npm install
81 | 4: npm run build
82 | 5: sudo npm install -g serve
83 | 6: serve -s build
84 | ```
85 |
86 | - 使用する yarn
87 |
88 | ```
89 | 1: git clone https://github.com/nishantpainter/tiny-manager.git
90 | 2: cd tiny-manager
91 | 3: yarn install
92 | 4: yarn build
93 | 5: sudo npm install -g serve
94 | 6: serve -s build
95 | ```
96 |
97 | ## プライバシー
98 |
99 | このアプリケーションは、作業データを永続化するためにローカル ストレージを利用し、いかなる種類のサーバーにも Tiny Manager データを保存しません。アプリケーションは Google アナリティクスを使用して、アプリケーションの使用状況の概要を取得します。
100 |
--------------------------------------------------------------------------------
/README_RU.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | #
6 |
7 |
8 | Простой автономный менеджер проектов для ваших любимых проектов.
9 |
10 |
11 |
12 |
13 |
14 |
15 | [](https://nishantpainter.github.io/tiny-manager) [](http://makeapullrequest.com) [](https://github.com/nishantpainter/tiny-manager/blob/main/license.txt) [](https://github.com/nishantpainter/tiny-manager/commits/master) [](https://github.com/nishantpainter/tiny-manager/issues)
16 |
17 | [English](https://github.com/nishantpainter/tiny-manager/blob/master/README.md) - [Français](https://github.com/nishantpainter/tiny-manager/blob/master/README_FR.md) - [Español](https://github.com/nishantpainter/tiny-manager/blob/master/README_ES.md) - [Pусский](https://github.com/nishantpainter/tiny-manager/blob/master/README_RU.md) - [Deutsch](https://github.com/nishantpainter/tiny-manager/blob/master/README_DE.md) - [हिंदी](https://github.com/nishantpainter/tiny-manager/blob/master/README_IN.md) - [中文](https://github.com/nishantpainter/tiny-manager/blob/master/README_CN.md) - [日本語](https://github.com/nishantpainter/tiny-manager/blob/master/README_JP.md)
18 |
19 | # Оглавление
20 |
21 | - **[Введение](#введение)**
22 | - **[Функции](#функции)**
23 | - **[Темный режим](#темный-режим)**
24 | - **[Локализация](#локализация)**
25 | - **[Использование в автономном режиме](#использование-в-автономном-режиме)**
26 | - **[Разработка](#разработка)**
27 | - **[Конфиденциальность](#конфиденциальность)**
28 |
29 | ## Введение
30 |
31 | Tiny Manager — это первое простое автономное приложение, которое помогает вам управлять вашими любимыми проектами. Наряду с управлением проектами он позволяет управлять рутинными задачами с помощью списка задач и простого приложения «Блокнот» — и все это в одном месте.
32 |
33 | ## Функции
34 |
35 | Несколько вещей, которые вы можете сделать с Tiny Manager:
36 |
37 | * Добавлять, редактировать, сохранять заметки
38 | * Добавить, изменить, удалить Todos
39 | * Управление домашними животными
40 | * Статус завершения проекта
41 | * Фильтрация и сортировка
42 | * Проблемы с установкой приоритета
43 | * Темный режим
44 | * Поддержка локализации
45 | * Автономный режим
46 | * Оффлайн локальное хранилище
47 |
48 | ## Темный режим
49 |
50 | Темный режим помогает быстро сделать экран темным (при использовании ночью). Панель инструментов предлагает вам кнопку со значком, которая служит переключателем для темного режима.
51 |
52 | ## Локализация
53 |
54 | Tiny Manager предлагает локальную поддержку языка, включая:
55 |
56 | - English
57 | - Français
58 | - Español
59 | - Pусский
60 | - Deutsch
61 | - हिंदी
62 | - 中文
63 | - 日本語
64 |
65 | ## Использование в автономном режиме
66 |
67 | Приложение зарегистрировано сервис-воркерами и ведет себя как прогрессивное веб-приложение. ([PWA](https://en.wikipedia.org/wiki/Progressive_web_application)). Для автономного использования вы можете выбрать опцию **Добавить на главную** при доступе к приложению в браузере, чтобы установить приложение локально на свои мобильные устройства.
68 |
69 | ## Разработка
70 |
71 | Приложение создается с помощью create-реагировать-приложение ([CRA](https://create-react-app.dev/docs/getting-started/)). Вы можете клонировать [Tiny Manager](https://github.com/nishantpainter/tiny-manager) репозиторий для пользовательской разработки.
72 |
73 | Чтобы запустить приложение локально на вашем компьютере, вы можете выполнить следующие шаги:
74 |
75 | - С использованием npm
76 |
77 | ```
78 | 1: git clone https://github.com/nishantpainter/tiny-manager.git
79 | 2: cd tiny-manager
80 | 3: npm install
81 | 4: npm run build
82 | 5: sudo npm install -g serve
83 | 6: serve -s build
84 | ```
85 |
86 | - С использованием yarn
87 |
88 | ```
89 | 1: git clone https://github.com/nishantpainter/tiny-manager.git
90 | 2: cd tiny-manager
91 | 3: yarn install
92 | 4: yarn build
93 | 5: sudo npm install -g serve
94 | 6: serve -s build
95 | ```
96 |
97 | ## конфиденциальность
98 |
99 | Приложение использует локальное хранилище для сохранения ваших рабочих данных и не хранит данные Tiny Manager на каком-либо сервере. Приложение использует аналитику Google, чтобы получить обзор использования приложения.
--------------------------------------------------------------------------------
/babel.config.json:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["@babel/env", "@babel/preset-react"]
3 | }
4 |
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | rootDir: "src",
3 | moduleFileExtensions: ["js", "json"],
4 | moduleDirectories: ["node_modules", ""],
5 | };
6 |
--------------------------------------------------------------------------------
/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "baseUrl": "src"
4 | },
5 | "include": ["src"]
6 | }
7 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "tiny-manager-client",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@material-ui/core": "^4.11.0",
7 | "@material-ui/icons": "^4.9.1",
8 | "@testing-library/jest-dom": "^4.2.4",
9 | "@testing-library/react": "^9.3.2",
10 | "@testing-library/user-event": "^7.1.2",
11 | "dexie": "^3.0.2",
12 | "formik": "^2.1.5",
13 | "i18next": "^20.2.2",
14 | "jspdf": "^2.3.1",
15 | "lodash.debounce": "^4.0.8",
16 | "moment": "^2.29.1",
17 | "notistack": "^1.0.7",
18 | "react": "^16.13.1",
19 | "react-dom": "^16.13.1",
20 | "react-i18next": "^11.8.14",
21 | "react-router-dom": "^5.2.0",
22 | "react-scripts": "3.4.3"
23 | },
24 | "scripts": {
25 | "start": "react-scripts start",
26 | "build": "react-scripts build",
27 | "test": "jest --watchAll",
28 | "eject": "react-scripts eject",
29 | "storybook": "start-storybook -p 6006 -s public",
30 | "build-storybook": "build-storybook -s public",
31 | "lint": "eslint src/TinyManager/**",
32 | "predeploy": "npm run build",
33 | "deploy": "gh-pages -d build"
34 | },
35 | "eslintConfig": {
36 | "extends": "react-app"
37 | },
38 | "browserslist": {
39 | "production": [
40 | ">0.2%",
41 | "not dead",
42 | "not op_mini all"
43 | ],
44 | "development": [
45 | "last 1 chrome version",
46 | "last 1 firefox version",
47 | "last 1 safari version"
48 | ]
49 | },
50 | "homepage": ".",
51 | "devDependencies": {
52 | "@babel/core": "^7.11.5",
53 | "@babel/eslint-parser": "^7.11.5",
54 | "@babel/preset-env": "^7.11.5",
55 | "@storybook/addon-actions": "^6.0.20",
56 | "@storybook/addon-essentials": "^6.0.20",
57 | "@storybook/addon-links": "^6.0.20",
58 | "@storybook/addon-storyshots": "^6.0.21",
59 | "@storybook/node-logger": "^6.0.20",
60 | "@storybook/preset-create-react-app": "^3.1.4",
61 | "@storybook/react": "^6.0.20",
62 | "babel-jest": "24.9.0",
63 | "babel-loader": "^8.1.0",
64 | "eslint": "^6.6.0",
65 | "eslint-plugin-react": "^7.20.6",
66 | "eslint-plugin-react-hooks": "^4.1.2",
67 | "faker": "^4.1.0",
68 | "gh-pages": "^3.1.0",
69 | "prettier": "^2.1.1",
70 | "react-is": "^16.13.1",
71 | "react-test-renderer": "^16.13.1",
72 | "uuid": "^8.3.0"
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/public/CNAME:
--------------------------------------------------------------------------------
1 | tinymanager.js.org
--------------------------------------------------------------------------------
/public/description.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nishantpainter/tiny-manager/a46a028bce72ef3998e7f5039703495a20793c2f/public/description.gif
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nishantpainter/tiny-manager/a46a028bce72ef3998e7f5039703495a20793c2f/public/favicon.ico
--------------------------------------------------------------------------------
/public/github.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/public/hn.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nishantpainter/tiny-manager/a46a028bce72ef3998e7f5039703495a20793c2f/public/hn.png
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
14 |
18 |
19 |
23 |
24 |
25 |
26 |
27 |
31 |
35 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
49 |
53 |
57 |
58 |
59 |
60 | Tiny Manager
61 |
65 |
66 |
67 |
71 |
80 |
81 |
82 | You need to enable JavaScript to run this app.
83 |
84 |
85 |
86 |
--------------------------------------------------------------------------------
/public/logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Created with Fabric.js 5.3.0
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nishantpainter/tiny-manager/a46a028bce72ef3998e7f5039703495a20793c2f/public/logo192.png
--------------------------------------------------------------------------------
/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nishantpainter/tiny-manager/a46a028bce72ef3998e7f5039703495a20793c2f/public/logo512.png
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "Tiny Manager",
3 | "name": "Tiny Manager",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "32x32",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#3f51b5",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/public/readme_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nishantpainter/tiny-manager/a46a028bce72ef3998e7f5039703495a20793c2f/public/readme_logo.png
--------------------------------------------------------------------------------
/public/readme_logo_cn.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nishantpainter/tiny-manager/a46a028bce72ef3998e7f5039703495a20793c2f/public/readme_logo_cn.png
--------------------------------------------------------------------------------
/public/readme_logo_de.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nishantpainter/tiny-manager/a46a028bce72ef3998e7f5039703495a20793c2f/public/readme_logo_de.png
--------------------------------------------------------------------------------
/public/readme_logo_es.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nishantpainter/tiny-manager/a46a028bce72ef3998e7f5039703495a20793c2f/public/readme_logo_es.png
--------------------------------------------------------------------------------
/public/readme_logo_fr.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nishantpainter/tiny-manager/a46a028bce72ef3998e7f5039703495a20793c2f/public/readme_logo_fr.png
--------------------------------------------------------------------------------
/public/readme_logo_in.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nishantpainter/tiny-manager/a46a028bce72ef3998e7f5039703495a20793c2f/public/readme_logo_in.png
--------------------------------------------------------------------------------
/public/readme_logo_jp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nishantpainter/tiny-manager/a46a028bce72ef3998e7f5039703495a20793c2f/public/readme_logo_jp.png
--------------------------------------------------------------------------------
/public/readme_logo_ru.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nishantpainter/tiny-manager/a46a028bce72ef3998e7f5039703495a20793c2f/public/readme_logo_ru.png
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/public/showcase_cn.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | 小经理
7 |
8 |
9 |
10 |
11 |
15 |
16 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
31 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
46 |
50 |
51 |
52 |
56 |
65 |
66 |
70 |
231 |
232 |
233 |
250 |
251 |
252 |
253 | English -
255 | Français -
257 | Español -
259 | Pусский -
261 | Deutsch -
263 | हिंदी -
265 | 中文 -
267 | 日本語
268 |
269 |
270 |
271 |
272 | 一个简单的离线项目管理器,适合您的宠物项目。
273 |
274 |
275 |
276 |
277 | 小经理
283 | 是一个离线的第一个简单的应用程序,可以帮助您管理您的
284 | 宠物项目。除了项目管理之外,它还可以进行日常管理
285 | 使用待办事项和简单的记事本应用程序,全部集中在一处。
286 |
287 |
捆绑了基本功能,可以快速轻松地管理 你的项目。
288 |
立即开始使用
294 |
295 |
296 |
306 |
315 |
320 |
321 | Star
328 |
329 |
330 |
333 |
334 |
335 |
336 |
特征
337 |
您可以使用 小经理 执行以下操作:
338 |
339 | 添加、编辑、保存笔记
340 | 添加、编辑、删除待办事项
341 | 宠物项目管理
342 | 项目完成情况
343 | 过滤和排序
344 | 问题优先级设置
345 | 深色模式
346 | 本地化支持
347 | 离线功能
348 | 离线本地存储
349 |
350 |
351 |
352 |
隐私和使用
353 |
354 | 该应用程序利用本地存储来保存您的工作
355 | 数据,并且不会在任何类型的服务器上存储任何 小经理 数据。
356 | 该应用程序使用 Google 分析来获取 应用程序的使用。
357 |
358 |
359 |
360 |
反馈或建议
361 |
请提交您对任何改进、错误的反馈或建议 并修复。
362 |
现在提交
369 |
370 |
371 |
380 |
381 |
382 |
--------------------------------------------------------------------------------
/public/showcase_jp.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | 小さなマネージャー
7 |
8 |
9 |
13 |
17 |
18 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
32 |
36 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
50 |
54 |
58 |
59 |
60 |
64 |
73 |
74 |
78 |
239 |
240 |
241 |
258 |
259 |
260 |
261 | English -
263 | Français -
265 | Español -
267 | Pусский -
269 | Deutsch -
271 | हिंदी -
273 | 中文 -
275 | 日本語
276 |
277 |
278 |
279 |
280 | あなたの大切なプロジェクトのためのシンプルなオフラインプロジェクトマネージャー。
282 |
283 |
284 |
285 |
286 |
287 | 小さなマネージャー
293 | は、オフラインでの最初のシンプルなアプリケーションであり、
294 | ペットプロジェクト。プロジェクト管理と併せて日常的な管理も可能にします
295 | Todo とシンプルなメモ帳アプリケーションをすべて 1 か所で使用できます。
296 |
297 |
298 | 重要な機能がバンドルされているため、迅速かつ簡単に管理できます。
299 | あなたのプロジェクト。
300 |
301 |
今すぐ使い始める
307 |
308 |
309 |
319 |
328 |
333 |
334 | Star
341 |
342 |
343 |
346 |
347 |
348 |
349 |
特徴
350 |
小さなマネージャーでできることのいくつか:
351 |
352 | メモの追加、編集、保存
353 | Todoの追加、編集、削除
354 | ペットプロジェクト管理
355 | プロジェクトの完了状況
356 | フィルタリングと並べ替え
357 | 課題の優先度設定
358 | ダークモード
359 | ローカリゼーションのサポート
360 | オフライン対応
361 | オフラインのローカルストレージ
362 |
363 |
364 |
365 |
プライバシーと使用法
366 |
367 | アプリケーションは作業内容を保存するためにローカル
368 | ストレージを利用します。
369 | データを保存し、いかなる種類の小さなマネージャー
370 | データも保存しません。 サーバ。このアプリケーションは Google
371 | アナリティクスを使用して、 アプリケーションの使用状況。
372 |
373 |
374 |
375 |
フィードバックまたは提案
376 |
377 | 改善やバグについてのフィードバックや提案をお送りください。
378 | そして修正します。
379 |
380 |
今提出
387 |
388 |
389 |
397 |
398 |
399 |
--------------------------------------------------------------------------------
/src/Introduction.stories.mdx:
--------------------------------------------------------------------------------
1 | import { Meta } from "@storybook/addon-docs/blocks";
2 |
3 |
4 |
5 | # Tiny Manager
6 |
7 | A management tool for your personal projects.
8 | [**Github Repo**](https://github.com/nishantpainter/tiny-manager)
9 |
--------------------------------------------------------------------------------
/src/TinyManager/assets/locales/cn/translations.json:
--------------------------------------------------------------------------------
1 | {
2 | "Add New Project": "添加新项目",
3 | "Add New Task": "添加新任务",
4 | "Add Todo": "添加待办事项",
5 | "Add": "添加",
6 | "All": "全部",
7 | "Cancel": "取消",
8 | "Clear Note": "清除笔记",
9 | "Completed": "完全的",
10 | "Confirm": "确认",
11 | "Created": "已创建",
12 | "Default Notes": "默认备注",
13 | "Delete All Projects": "删除所有项目",
14 | "Delete All Tasks": "删除所有任务",
15 | "Delete All": "删除所有",
16 | "Delete Project": "删除项目",
17 | "Delete project and related tasks ?": "删除项目和相关任务?",
18 | "Description": "描述",
19 | "Do you want to remove all the projects ?": "您要删除所有项目吗?",
20 | "Do you want to remove all the tasks ?": "您要删除所有任务吗?",
21 | "Download Note": "下载说明",
22 | "Edit Project": "编辑项目",
23 | "Edit Task": "编辑任务",
24 | "Edit Todo": "编辑待办事项",
25 | "Filter By": "过滤",
26 | "High": "高的",
27 | "Home": "家",
28 | "Low": "低的",
29 | "Medium": "中等的",
30 | "Name is required.": "姓名是必填项。",
31 | "Name": "姓名",
32 | "New Project": "新项目",
33 | "New Task": "新任务",
34 | "No available projects.": "没有可用的项目。",
35 | "No available tasks.": "没有可用的任务。",
36 | "Note": "笔记",
37 | "Notes will be stored locally on the browser and will be persisted.": "笔记将本地存储在浏览器上,并将被持久化。",
38 | "Notes": "笔记",
39 | "Nothing to be completed. Enjoy your day.": "没有什么可以完成的。 祝您愉快。",
40 | "PDF": "PDF",
41 | "Pending": "待办的",
42 | "Percentage": "百分比",
43 | "Priority": "优先",
44 | "Progress": "进步",
45 | "Projects": "项目",
46 | "Save": "节省",
47 | "Sort By": "排序方式",
48 | "TXT": "TXT",
49 | "Tiny Manager": "小经理",
50 | "Title is required.": "标题是必需的。",
51 | "Title": "标题",
52 | "Todos": "待办事项",
53 | "Updated": "更新",
54 | "You can enter your notes here...": "你可以在这里输入你的笔记..."
55 | }
56 |
--------------------------------------------------------------------------------
/src/TinyManager/assets/locales/de/translations.json:
--------------------------------------------------------------------------------
1 | {
2 | "Add New Project": "Neues Projekt hinzufügen",
3 | "Add New Task": "Neue Aufgabe hinzufügen",
4 | "Add Todo": "Aufgaben hinzufügen",
5 | "Add": "Hinzufügen",
6 | "All": "Alle",
7 | "Cancel": "Absagen",
8 | "Clear Note": "Hinweis löschen",
9 | "Completed": "Abgeschlossen",
10 | "Confirm": "Bestätigen",
11 | "Created": "Erstellt",
12 | "Default Notes": "Standardnotizen",
13 | "Delete All Projects": "Alle Projekte löschen",
14 | "Delete All Tasks": "Alle Aufgaben löschen",
15 | "Delete All": "Alles löschen",
16 | "Delete Project": "Projekt löschen",
17 | "Delete project and related tasks ?": "Projekt und zugehörige Aufgaben löschen ?",
18 | "Description": "Beschreibung",
19 | "Do you want to remove all the projects ?": "Möchten Sie alle Projekte entfernen?",
20 | "Do you want to remove all the tasks ?": "Möchten Sie alle Aufgaben entfernen?",
21 | "Download Note": "Hinweis herunterladen",
22 | "Edit Project": "Projekt bearbeiten",
23 | "Edit Task": "Aufgabe bearbeiten",
24 | "Edit Todo": "Aufgaben bearbeiten",
25 | "Filter By": "Filtern nach",
26 | "High": "Hoch",
27 | "Home": "Heim",
28 | "Low": "Niedrig",
29 | "Medium": "Mittel",
30 | "Name is required.": "Name ist erforderlich.",
31 | "Name": "Name",
32 | "New Project": "Neues Projekt",
33 | "New Task": "Neue Aufgabe",
34 | "No available projects.": "Keine verfügbaren Projekte.",
35 | "No available tasks.": "Keine verfügbaren Aufgaben.",
36 | "Note": "Notiz",
37 | "Notes will be stored locally on the browser and will be persisted.": "Notizen werden lokal im Browser gespeichert und bleiben erhalten.",
38 | "Notes": "Anmerkungen",
39 | "Nothing to be completed. Enjoy your day.": "Nichts zu vervollständigen. Genieße deinen Tag.",
40 | "PDF": "PDF",
41 | "Pending": "Ausstehend",
42 | "Percentage": "Prozentsatz",
43 | "Priority": "Priorität",
44 | "Progress": "Fortschritt",
45 | "Projects": "Projekte",
46 | "Save": "Speichern",
47 | "Sort By": "Sortieren nach",
48 | "TXT": "TXT",
49 | "Tiny Manager": "Winziger Manager",
50 | "Title is required.": "Titel ist erforderlich.",
51 | "Title": "Titel",
52 | "Todos": "Tods",
53 | "Updated": "Aktualisiert",
54 | "You can enter your notes here...": "Hier können Sie Ihre Notizen eingeben..."
55 | }
56 |
--------------------------------------------------------------------------------
/src/TinyManager/assets/locales/en/translations.json:
--------------------------------------------------------------------------------
1 | {
2 | "Add New Project": "Add New Project",
3 | "Add New Task": "Add New Task",
4 | "Add Todo": "Add Todo",
5 | "Add": "Add",
6 | "All": "All",
7 | "Cancel": "Cancel",
8 | "Clear Note": "Clear Note",
9 | "Completed": "Completed",
10 | "Confirm": "Confirm",
11 | "Created": "Created",
12 | "Default Notes": "Default Notes",
13 | "Delete All Projects": "Delete All Projects",
14 | "Delete All Tasks": "Delete All Tasks",
15 | "Delete All": "Delete All",
16 | "Delete Project": "Delete Project",
17 | "Delete project and related tasks ?": "Delete project and related tasks ?",
18 | "Description": "Description",
19 | "Do you want to remove all the projects ?": "Do you want to remove all the projects ?",
20 | "Do you want to remove all the tasks ?": "Do you want to remove all the tasks ?",
21 | "Download Note": "Download Note",
22 | "Edit Project": "Edit Project",
23 | "Edit Task": "Edit Task",
24 | "Edit Todo": "Edit Todo",
25 | "Filter By": "Filter By",
26 | "High": "High",
27 | "Home": "Home",
28 | "Low": "Low",
29 | "Medium": "Medium",
30 | "Name is required.": "Name is required.",
31 | "Name": "Name",
32 | "New Project": "New Project",
33 | "New Task": "New Task",
34 | "No available projects.": "No available projects.",
35 | "No available tasks.": "No available tasks.",
36 | "Note": "Note",
37 | "Notes will be stored locally on the browser and will be persisted.": "Notes will be stored locally on the browser and will be persisted.",
38 | "Notes": "Notes",
39 | "Nothing to be completed. Enjoy your day.": "Nothing to be completed. Enjoy your day.",
40 | "PDF": "PDF",
41 | "Pending": "Pending",
42 | "Percentage": "Percentage",
43 | "Priority": "Priority",
44 | "Progress": "Progress",
45 | "Projects": "Projects",
46 | "Save": "Save",
47 | "Sort By": "Sort By",
48 | "TXT": "TXT",
49 | "Tiny Manager": "Tiny Manager",
50 | "Title is required.": "Title is required.",
51 | "Title": "Title",
52 | "Todos": "Todos",
53 | "Updated": "Updated",
54 | "You can enter your notes here...": "You can enter your notes here..."
55 | }
56 |
--------------------------------------------------------------------------------
/src/TinyManager/assets/locales/es/translations.json:
--------------------------------------------------------------------------------
1 | {
2 | "Add New Project": "Agregar nuevo proyecto",
3 | "Add New Task": "Agregar nueva tarea",
4 | "Add Todo": "Añadir todo",
5 | "Add": "Agregar",
6 | "All": "Todos",
7 | "Cancel": "Cancelar",
8 | "Clear Note": "Nota clara",
9 | "Completed": "Terminado",
10 | "Confirm": "Confirmar",
11 | "Created": "Creado",
12 | "Default Notes": "Notas predeterminadas",
13 | "Delete All Projects": "Eliminar todos los proyectos",
14 | "Delete All Tasks": "Eliminar todas las tareas",
15 | "Delete All": "Eliminar todos",
16 | "Delete Project": "Eliminar proyecto",
17 | "Delete project and related tasks ?": "¿Eliminar proyecto y tareas relacionadas?",
18 | "Description": "Descripción",
19 | "Do you want to remove all the projects ?": "¿Quieres eliminar todos los proyectos?",
20 | "Do you want to remove all the tasks ?": "¿Quieres eliminar todas las tareas?",
21 | "Download Note": "Descargar Nota",
22 | "Edit Project": "Editar proyecto",
23 | "Edit Task": "Editar tarea",
24 | "Edit Todo": "Editar Todo",
25 | "Filter By": "Filtrado por",
26 | "High": "Alto",
27 | "Home": "Hogar",
28 | "Low": "Bajo",
29 | "Medium": "Medio",
30 | "Name is required.": "Se requiere el nombre.",
31 | "Name": "Nombre",
32 | "New Project": "Nuevo proyecto",
33 | "New Task": "Nueva tarea",
34 | "No available projects.": "No hay proyectos disponibles.",
35 | "No available tasks.": "No hay tareas disponibles.",
36 | "Note": "Nota",
37 | "Notes will be stored locally on the browser and will be persisted.": "Las notas se almacenarán localmente en el navegador y se conservarán.",
38 | "Notes": "notas",
39 | "Nothing to be completed. Enjoy your day.": "Nada por completar. Disfruta tu día.",
40 | "PDF": "PDF",
41 | "Pending": "Pendiente",
42 | "Percentage": "Porcentaje",
43 | "Priority": "Prioridad",
44 | "Progress": "Progreso",
45 | "Projects": "Proyectos",
46 | "Save": "Ahorrar",
47 | "Sort By": "Ordenar por",
48 | "TXT": "TXT",
49 | "Tiny Manager": "Pequeño gerente",
50 | "Title is required.": "Se requiere título.",
51 | "Title": "Título",
52 | "Todos": "Todos",
53 | "Updated": "Actualizado",
54 | "You can enter your notes here...": "Puede ingresar sus notas aquí..."
55 | }
56 |
--------------------------------------------------------------------------------
/src/TinyManager/assets/locales/fr/translations.json:
--------------------------------------------------------------------------------
1 | {
2 | "Add New Project": "Ajouter un nouveau projet",
3 | "Add New Task": "Ajouter une nouvelle tâche",
4 | "Add Todo": "Ajouter une tâche",
5 | "Add": "Ajouter",
6 | "All": "Tout",
7 | "Cancel": "Annuler",
8 | "Clear Note": "Remarque claire",
9 | "Completed": "Complété",
10 | "Confirm": "Confirmer",
11 | "Created": "Établi",
12 | "Default Notes": "Remarques par défaut",
13 | "Delete All Projects": "Supprimer tous les projets",
14 | "Delete All Tasks": "Supprimer toutes les tâches",
15 | "Delete All": "Supprimer tout",
16 | "Delete Project": "Supprimer le projet",
17 | "Delete project and related tasks ?": "Supprimer le projet et les tâches associées ?",
18 | "Description": "La description",
19 | "Do you want to remove all the projects ?": "Voulez-vous supprimer tous les projets ?",
20 | "Do you want to remove all the tasks ?": "Voulez-vous supprimer toutes les tâches ?",
21 | "Download Note": "Télécharger la note",
22 | "Edit Project": "Modifier le projet",
23 | "Edit Task": "Modifier la tâche",
24 | "Edit Todo": "Modifier la tâche",
25 | "Filter By": "Filtrer par",
26 | "High": "Haut",
27 | "Home": "Maison",
28 | "Low": "Bas",
29 | "Medium": "Moyen",
30 | "Name is required.": "Le nom est requis.",
31 | "Name": "Nom",
32 | "New Project": "Nouveau projet",
33 | "New Task": "Nouvelle tâche",
34 | "No available projects.": "Aucun projet disponible.",
35 | "No available tasks.": "Aucune tâche disponible.",
36 | "Note": "Noter",
37 | "Notes will be stored locally on the browser and will be persisted.": "Les notes seront stockées localement sur le navigateur et seront conservées.",
38 | "Notes": "Remarques",
39 | "Nothing to be completed. Enjoy your day.": "Rien à compléter. Profitez de votre journée.",
40 | "PDF": "PDF",
41 | "Pending": "En attente",
42 | "Percentage": "Pourcentage",
43 | "Priority": "Priorité",
44 | "Progress": "Progrès",
45 | "Projects": "Projets",
46 | "Save": "Sauvegarder",
47 | "Sort By": "Trier par",
48 | "TXT": "TXT",
49 | "Tiny Manager": "Petit gestionnaire",
50 | "Title is required.": "Le titre est requis.",
51 | "Title": "Titre",
52 | "Todos": "À faire",
53 | "Updated": "Actualisé",
54 | "You can enter your notes here...": "Vous pouvez entrer vos notes ici..."
55 | }
56 |
--------------------------------------------------------------------------------
/src/TinyManager/assets/locales/in/translations.json:
--------------------------------------------------------------------------------
1 | {
2 | "Add New Project": "नया प्रोजेक्ट जोड़ें",
3 | "Add New Task": "नया कार्य जोड़ें",
4 | "Add Todo": "टोडो जोड़ें",
5 | "Add": "जोड़ें",
6 | "All": "सभी",
7 | "Cancel": "रद्द करना",
8 | "Clear Note": "नोट साफ़ करें",
9 | "Completed": "पूरा हुआ",
10 | "Confirm": "पुष्टि करें",
11 | "Created": "बनाया था",
12 | "Default Notes": "डिफ़ॉल्ट नोट्स",
13 | "Delete All Projects": "सभी प्रोजेक्ट हटाएं",
14 | "Delete All Tasks": "सभी कार्य हटाएं",
15 | "Delete All": "सभी हटा दो",
16 | "Delete Project": "प्रोजेक्ट हटाएं",
17 | "Delete project and related tasks ?": "प्रोजेक्ट और संबंधित कार्य हटाएं?",
18 | "Description": "विवरण",
19 | "Do you want to remove all the projects ?": "क्या आप सभी प्रोजेक्ट हटाना चाहते हैं?",
20 | "Do you want to remove all the tasks ?": "क्या आप सभी कार्यों को हटाना चाहते हैं?",
21 | "Download Note": "नोट डाउनलोड करें",
22 | "Edit Project": "परियोजना संपादित करें",
23 | "Edit Task": "कार्य संपादित करें",
24 | "Edit Todo": "कार्य संपादित करें",
25 | "Filter By": "छनित करें",
26 | "High": "उच्च",
27 | "Home": "होम",
28 | "Low": "कम",
29 | "Medium": "मध्यम",
30 | "Name is required.": "नाम आवश्यक है।",
31 | "Name": "नाम",
32 | "New Project": "नया काम",
33 | "New Task": "नया कार्य",
34 | "No available projects.": "कोई प्रोजेक्ट उपलब्ध नहीं है.",
35 | "No available tasks.": "कोई उपलब्ध कार्य नहीं।",
36 | "Note": "टिप्पणी",
37 | "Notes will be stored locally on the browser and will be persisted.": "नोट्स स्थानीय रूप से ब्राउज़र पर संग्रहीत किए जाएंगे और बने रहेंगे।",
38 | "Notes": "टिप्पणियाँ",
39 | "Nothing to be completed. Enjoy your day.": "कुछ भी पूरा नहीं करना है। अपने दिन का आनंद लें।",
40 | "PDF": "PDF",
41 | "Pending": "लंबित",
42 | "Percentage": "प्रतिशत",
43 | "Priority": "वरीयता",
44 | "Progress": "प्रगति",
45 | "Projects": "परियोजनाओं",
46 | "Save": "बचाना",
47 | "Sort By": "क्रमबद्ध करें",
48 | "TXT": "TXT",
49 | "Tiny Manager": "छोटे प्रबंधक",
50 | "Title is required.": "शीर्षक की आवश्यकता है।",
51 | "Title": "शीर्षक",
52 | "Todos": "सब",
53 | "Updated": "अद्यतन",
54 | "You can enter your notes here...": "आप अपने नोट्स यहां दर्ज कर सकते हैं..."
55 | }
56 |
--------------------------------------------------------------------------------
/src/TinyManager/assets/locales/jp/translations.json:
--------------------------------------------------------------------------------
1 | {
2 | "Add New Project": "新しいプロジェクトを追加",
3 | "Add New Task": "新しいタスクを追加",
4 | "Add Todo": "追加 - Todo",
5 | "Add": "追加",
6 | "All": "全て",
7 | "Cancel": "キャンセル",
8 | "Clear Note": "クリアノート",
9 | "Completed": "完了",
10 | "Confirm": "確認",
11 | "Created": "作成した",
12 | "Default Notes": "デフォルトのメモ",
13 | "Delete All Projects": "すべてのプロジェクトを削除",
14 | "Delete All Tasks": "すべてのタスクを削除",
15 | "Delete All": "すべて削除",
16 | "Delete Project": "プロジェクトを削除",
17 | "Delete project and related tasks ?": "プロジェクトと関連タスクを削除しますか?",
18 | "Description": "説明",
19 | "Do you want to remove all the projects ?": "すべてのプロジェクトを削除しますか?",
20 | "Do you want to remove all the tasks ?": "すべてのタスクを削除しますか?",
21 | "Download Note": "注をダウンロード",
22 | "Edit Project": "プロジェクトの編集",
23 | "Edit Task": "タスクの編集",
24 | "Edit Todo": "Todoを編集する",
25 | "Filter By": "フィルター",
26 | "High": "高い",
27 | "Home": "家",
28 | "Low": "低い",
29 | "Medium": "中くらい",
30 | "Name is required.": "名前が必要です。",
31 | "Name": "名前",
32 | "New Project": "新しい計画",
33 | "New Task": "新しい仕事",
34 | "No available projects.": "利用可能なプロジェクトはありません。",
35 | "No available tasks.": "利用可能なタスクはありません。",
36 | "Note": "ノート",
37 | "Notes will be stored locally on the browser and will be persisted.": "メモはブラウザにローカルに保存され、保持されます。",
38 | "Notes": "ノート",
39 | "Nothing to be completed. Enjoy your day.": "完了するものはありません。 一日をお楽しみください。",
40 | "PDF": "PDF",
41 | "Pending": "保留中",
42 | "Percentage": "パーセンテージ",
43 | "Priority": "優先順位",
44 | "Progress": "進捗",
45 | "Projects": "プロジェクト",
46 | "Save": "保存",
47 | "Sort By": "並び替え",
48 | "TXT": "TXT",
49 | "Tiny Manager": "小さなマネージャー",
50 | "Title is required.": "タイトルが必要です。",
51 | "Title": "題名",
52 | "Todos": "Todos",
53 | "Updated": "更新しました",
54 | "You can enter your notes here...": "ここにメモを入力できます..."
55 | }
56 |
--------------------------------------------------------------------------------
/src/TinyManager/assets/locales/ru/translations.json:
--------------------------------------------------------------------------------
1 | {
2 | "Add New Project": "Добавить новый проект",
3 | "Add New Task": "Добавить новую задачу",
4 | "Add Todo": "Добавить дело",
5 | "Add": "Добавлять",
6 | "All": "Все",
7 | "Cancel": "Отмена",
8 | "Clear Note": "Очистить примечание",
9 | "Completed": "Завершенный",
10 | "Confirm": "Подтверждать",
11 | "Created": "Созданный",
12 | "Default Notes": "Примечания по умолчанию",
13 | "Delete All Projects": "Удалить все проекты",
14 | "Delete All Tasks": "Удалить все задачи",
15 | "Delete All": "Удалить все",
16 | "Delete Project": "Удалить проект",
17 | "Delete project and related tasks ?": "Удалить проект и связанные с ним задачи?",
18 | "Description": "Описание",
19 | "Do you want to remove all the projects ?": "Вы хотите удалить все проекты?",
20 | "Do you want to remove all the tasks ?": "Вы хотите удалить все задачи?",
21 | "Download Note": "Скачать примечание",
22 | "Edit Project": "Редактировать проект",
23 | "Edit Task": "Изменить задачу",
24 | "Edit Todo": "Изменить дело",
25 | "Filter By": "Сортировать по",
26 | "High": "Высокая",
27 | "Home": "Дом",
28 | "Low": "Низкий",
29 | "Medium": "Середина",
30 | "Name is required.": "Требуется имя.",
31 | "Name": "Имя",
32 | "New Project": "Новый проект",
33 | "New Task": "Новое задание",
34 | "No available projects.": "Нет доступных проектов.",
35 | "No available tasks.": "Нет доступных задач.",
36 | "Note": "Примечание",
37 | "Notes will be stored locally on the browser and will be persisted.": "Заметки будут храниться локально в браузере и будут сохраняться.",
38 | "Notes": "Заметки",
39 | "Nothing to be completed. Enjoy your day.": "Ничего не нужно завершать. Хорошего вам дня.",
40 | "PDF": "PDF",
41 | "Pending": "В ожидании",
42 | "Percentage": "Процент",
43 | "Priority": "приоритет",
44 | "Progress": "Прогресс",
45 | "Projects": "Проекты",
46 | "Save": "Сохранять",
47 | "Sort By": "Сортировать по",
48 | "TXT": "TXT",
49 | "Tiny Manager": "Крошечный менеджер",
50 | "Title is required.": "Заголовок обязателен.",
51 | "Title": "Заголовок",
52 | "Todos": "Todos",
53 | "Updated": "Обновлено",
54 | "You can enter your notes here...": "Здесь вы можете оставить свои заметки..."
55 | }
56 |
--------------------------------------------------------------------------------
/src/TinyManager/components/CircularProgressWithLabel/CircularProgressWithLabel.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 | import { makeStyles } from "@material-ui/core/styles";
4 | import Box from "@material-ui/core/Box";
5 | import CircularProgress from "@material-ui/core/CircularProgress";
6 | import Typography from "@material-ui/core/Typography";
7 |
8 | const useStyles = makeStyles(() => ({
9 | progress: {
10 | fontSize: "0.60rem",
11 | },
12 | }));
13 |
14 | function CircularProgressWithLabel(props) {
15 | const classes = useStyles();
16 |
17 | return (
18 |
19 |
20 |
30 | {`${Math.round(props.value)}%`}
35 |
36 |
37 | );
38 | }
39 |
40 | CircularProgressWithLabel.propTypes = {
41 | /**
42 | * The value of the progress indicator.
43 | * Value between 0 and 100.
44 | */
45 | value: PropTypes.number.isRequired,
46 | };
47 |
48 | CircularProgressWithLabel.defaultProps = {
49 | value: 0,
50 | };
51 |
52 | export default CircularProgressWithLabel;
53 |
--------------------------------------------------------------------------------
/src/TinyManager/components/CircularProgressWithLabel/CircularProgressWithLabel.stories.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | import CircularProgressWithLabel from "./CircularProgressWithLabel";
4 |
5 | export default {
6 | title: "TinyManager/CircularProgressWithLabel",
7 | component: CircularProgressWithLabel,
8 | };
9 |
10 | const Template = (args) => ;
11 |
12 | export const Default = Template.bind({});
13 | Default.args = {
14 | value: 50,
15 | };
16 |
--------------------------------------------------------------------------------
/src/TinyManager/components/CircularProgressWithLabel/index.js:
--------------------------------------------------------------------------------
1 | import CircularProgressWithLabel from "./CircularProgressWithLabel";
2 |
3 | export default CircularProgressWithLabel;
4 |
--------------------------------------------------------------------------------
/src/TinyManager/components/ConfirmationDialog/ConfirmationDialog.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 | import Button from "@material-ui/core/Button";
4 | import Dialog from "@material-ui/core/Dialog";
5 | import DialogActions from "@material-ui/core/DialogActions";
6 | import DialogContent from "@material-ui/core/DialogContent";
7 | import DialogTitle from "@material-ui/core/DialogTitle";
8 |
9 | import { identity } from "../utils";
10 |
11 | function ConfirmationDialog(props) {
12 | const {
13 | title,
14 | content,
15 | open,
16 | translate,
17 | onClose,
18 | onConfirm,
19 | onCancel,
20 | } = props;
21 |
22 | const handleCancel = onCancel || onClose;
23 |
24 | return (
25 |
26 | {title}
27 | {content}
28 |
29 | {translate("Cancel")}
30 |
31 | {translate("Confirm")}
32 |
33 |
34 |
35 | );
36 | }
37 |
38 | ConfirmationDialog.propTypes = {
39 | title: PropTypes.any,
40 | content: PropTypes.any,
41 | open: PropTypes.bool,
42 | translate: PropTypes.func,
43 | onClose: PropTypes.func,
44 | onConfirm: PropTypes.func,
45 | onCancel: PropTypes.func,
46 | };
47 |
48 | ConfirmationDialog.defaultProps = {
49 | translate: identity,
50 | };
51 |
52 | export default ConfirmationDialog;
53 |
--------------------------------------------------------------------------------
/src/TinyManager/components/ConfirmationDialog/ConfirmationDialog.stories.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | import ConfirmationDialog from "./ConfirmationDialog";
4 | import { identity, noop } from "../utils";
5 |
6 | export default {
7 | title: "TinyManager/ConfirmationDialog",
8 | component: ConfirmationDialog,
9 | };
10 |
11 | const Template = (args) => ;
12 |
13 | export const Default = Template.bind({});
14 | Default.args = {
15 | open: true,
16 | title: "Confirmation Dialog",
17 | content: "Do you want to continue?",
18 | onClose: noop,
19 | onCancel: noop,
20 | onConfirm: noop,
21 | translate: identity,
22 | };
23 |
--------------------------------------------------------------------------------
/src/TinyManager/components/ConfirmationDialog/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./ConfirmationDialog";
2 |
--------------------------------------------------------------------------------
/src/TinyManager/components/IconButton/IconButton.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 | import MuiIconButton from "@material-ui/core/IconButton";
4 |
5 | import FilterListIcon from "@material-ui/icons/FilterList";
6 | import EditIcon from "@material-ui/icons/Edit";
7 | import AttachFile from "@material-ui/icons/AttachFile";
8 | import InvertColors from "@material-ui/icons/InvertColors";
9 | import Delete from "@material-ui/icons/Delete";
10 | import Language from "@material-ui/icons/Language";
11 | import GitHub from "@material-ui/icons/GitHub";
12 |
13 | const icons = {
14 | filter: FilterListIcon,
15 | edit: EditIcon,
16 | attachment: AttachFile,
17 | invertColors: InvertColors,
18 | delete: Delete,
19 | language: Language,
20 | github: GitHub,
21 | };
22 |
23 | function IconButton(props) {
24 | const { icon, ...rest } = props;
25 | const Icon = icons[icon];
26 | return (
27 |
28 |
29 |
30 | );
31 | }
32 |
33 | IconButton.propTypes = {
34 | /** Icon to be displayed */
35 | icon: PropTypes.string,
36 | };
37 |
38 | export default IconButton;
39 |
--------------------------------------------------------------------------------
/src/TinyManager/components/IconButton/IconButton.stories.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | import IconButton from "./IconButton";
4 |
5 | export default {
6 | title: "TinyManager/IconButton",
7 | component: IconButton,
8 | };
9 |
10 | const Template = (args) => ;
11 |
12 | export const Default = Template.bind({});
13 | Default.args = {
14 | icon: "filter",
15 | };
16 |
--------------------------------------------------------------------------------
/src/TinyManager/components/IconButton/index.js:
--------------------------------------------------------------------------------
1 | import IconButton from "./IconButton";
2 |
3 | export default IconButton;
4 |
--------------------------------------------------------------------------------
/src/TinyManager/components/Loader/Loader.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import clsx from "clsx";
3 | import PropTypes from "prop-types";
4 | import CircularProgress from "@material-ui/core/CircularProgress";
5 | import { makeStyles } from "@material-ui/core/styles";
6 |
7 | const useStyles = makeStyles((theme) => ({
8 | root: {
9 | height: "inherit",
10 | width: "inherit",
11 | position: "relative",
12 | display: "flex",
13 | justifyContent: "center",
14 | alignItems: "center",
15 | },
16 | top: {
17 | color: "transparent",
18 | },
19 | bottom: {
20 | color: theme.palette.primary.main,
21 | animationDuration: "250ms",
22 | position: "absolute",
23 | },
24 | }));
25 |
26 | function Loader(props) {
27 | const { className } = props;
28 |
29 | const classes = useStyles();
30 |
31 | return (
32 |
33 |
41 |
49 |
50 | );
51 | }
52 |
53 | Loader.propTypes = {
54 | /**
55 | * @ignore
56 | */
57 | className: PropTypes.string,
58 | };
59 |
60 | export default Loader;
61 |
--------------------------------------------------------------------------------
/src/TinyManager/components/Loader/Loader.stories.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | import Loader from "./Loader";
4 | import { withWrapper } from "TinyManager/decorators";
5 |
6 | export default {
7 | title: "TinyManager/Loader",
8 | component: Loader,
9 | decorators: [withWrapper],
10 | };
11 |
12 | const Template = (args) => ;
13 |
14 | export const Default = Template.bind({});
15 | Default.args = {};
16 |
--------------------------------------------------------------------------------
/src/TinyManager/components/Loader/index.js:
--------------------------------------------------------------------------------
1 | import Loader from "./Loader";
2 |
3 | export default Loader;
4 |
--------------------------------------------------------------------------------
/src/TinyManager/components/OutlinedSelect/OutlinedSelect.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 | import FormControl from "@material-ui/core/FormControl";
4 | import InputLabel from "@material-ui/core/InputLabel";
5 | import MenuItem from "@material-ui/core/MenuItem";
6 | import Select from "@material-ui/core/Select";
7 |
8 | function OutlinedSelect(props) {
9 | const { id, label, value, className, menu, disabled, onChange } = props;
10 |
11 | return (
12 |
13 | {label}
14 |
23 | {menu.map((item) => (
24 |
25 | {item.label}
26 |
27 | ))}
28 |
29 |
30 | );
31 | }
32 |
33 | OutlinedSelect.propTypes = {
34 | id: PropTypes.string,
35 | label: PropTypes.string,
36 | value: PropTypes.any,
37 | menu: PropTypes.array,
38 | className: PropTypes.string,
39 | disabled: PropTypes.bool,
40 | onChange: PropTypes.func,
41 | };
42 |
43 | export default OutlinedSelect;
44 |
--------------------------------------------------------------------------------
/src/TinyManager/components/OutlinedSelect/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./OutlinedSelect";
2 |
--------------------------------------------------------------------------------
/src/TinyManager/components/Paper/Paper.js:
--------------------------------------------------------------------------------
1 | import PropTypes from "prop-types";
2 | import MuiPaper from "@material-ui/core/Paper";
3 | import { withStyles } from "@material-ui/core/styles";
4 |
5 | const Paper = withStyles((theme) => ({
6 | root: {
7 | display: "flex",
8 | alignItems: "center",
9 | justifyContent: "center",
10 | cursor: "pointer",
11 | boxShadow: theme.custom.shadow[0],
12 | borderRadius: theme.spacing(0.5),
13 | padding: theme.spacing(),
14 | height: theme.spacing(15),
15 | },
16 | }))(MuiPaper);
17 |
18 | Paper.displayName = "Paper";
19 |
20 | Paper.propTypes = {
21 | /**
22 | * @ignore
23 | */
24 | className: PropTypes.string,
25 | };
26 |
27 | export default Paper;
28 |
--------------------------------------------------------------------------------
/src/TinyManager/components/Paper/Paper.stories.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | import Paper from "./Paper";
4 | import { withWrapper } from "TinyManager/decorators";
5 |
6 | export default {
7 | title: "TinyManager/Paper",
8 | component: Paper,
9 | decorators: [withWrapper],
10 | };
11 |
12 | const Template = (args) => ;
13 |
14 | export const Default = Template.bind({});
15 | Default.args = {};
16 |
--------------------------------------------------------------------------------
/src/TinyManager/components/Paper/index.js:
--------------------------------------------------------------------------------
1 | import Paper from './Paper';
2 |
3 | export default Paper;
--------------------------------------------------------------------------------
/src/TinyManager/components/ProjectCard/ProjectCard.js:
--------------------------------------------------------------------------------
1 | import React, { useCallback } from "react";
2 | import clsx from "clsx";
3 | import PropTypes from "prop-types";
4 | import Box from "@material-ui/core/Box";
5 | import LinearProgress from "@material-ui/core/LinearProgress";
6 | import Typography from "@material-ui/core/Typography";
7 | import { makeStyles } from "@material-ui/core/styles";
8 |
9 | import Paper from "../Paper";
10 | import IconButton from "../IconButton";
11 | import Types from "TinyManager/types";
12 |
13 | const useStyles = makeStyles(() => ({
14 | paper: {
15 | flexDirection: "column",
16 | alignItems: "flex-start",
17 | },
18 | fullWidth: {
19 | width: "100%",
20 | },
21 | }));
22 |
23 | function ProjectCard(props) {
24 | const {
25 | className,
26 | project,
27 | progress,
28 | onClick,
29 | onEdit,
30 | onDelete,
31 | showEditButton,
32 | showDeleteButton,
33 | } = props;
34 |
35 | const classes = useStyles();
36 |
37 | const handleClick = useCallback(
38 | (event) => {
39 | if (onClick) {
40 | onClick(event, project);
41 | }
42 | },
43 | [project, onClick]
44 | );
45 |
46 | const handleEdit = useCallback(
47 | (event) => {
48 | if (onEdit) {
49 | onEdit(event, project);
50 | }
51 | },
52 | [project, onEdit]
53 | );
54 |
55 | const handleDelete = useCallback(
56 | (event) => {
57 | if (onDelete) {
58 | onDelete(event, project);
59 | }
60 | },
61 | [project, onDelete]
62 | );
63 |
64 | return (
65 |
66 |
72 |
73 | {project.name}
74 |
75 |
76 | {showEditButton && }
77 | {showDeleteButton && (
78 |
79 | )}
80 |
81 |
82 |
89 | {project.description}
90 |
91 |
97 |
98 | );
99 | }
100 |
101 | ProjectCard.propTypes = {
102 | className: PropTypes.string,
103 | project: Types.ProjectType,
104 | progress: PropTypes.number,
105 | onClick: PropTypes.func,
106 | onEdit: PropTypes.func,
107 | onDelete: PropTypes.func,
108 | showEditButton: PropTypes.bool,
109 | showDeleteButton: PropTypes.bool,
110 | };
111 |
112 | ProjectCard.defaultProps = {
113 | project: {},
114 | progress: 0,
115 | showEditButton: false,
116 | };
117 |
118 | export default ProjectCard;
119 |
--------------------------------------------------------------------------------
/src/TinyManager/components/ProjectCard/ProjectCard.stories.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | import ProjectCard from "./ProjectCard";
4 | import { withWrapper } from "TinyManager/decorators";
5 |
6 | export default {
7 | title: "TinyManager/ProjectCard",
8 | component: ProjectCard,
9 | decorators: [withWrapper],
10 | };
11 |
12 | const Template = (args) => ;
13 |
14 | export const Default = Template.bind({});
15 | Default.args = {
16 | project: {
17 | name: "Tiny Manager",
18 | description: "Tiny manager for your self project",
19 | },
20 | };
21 |
22 | export const Progress = Template.bind({});
23 | Progress.args = {
24 | ...Default.args,
25 | progress: 100,
26 | };
27 |
28 | export const Actions = Template.bind({});
29 | Actions.args = {
30 | ...Default.args,
31 | showEditButton: true,
32 | showDeleteButton: true,
33 | };
34 |
--------------------------------------------------------------------------------
/src/TinyManager/components/ProjectCard/index.js:
--------------------------------------------------------------------------------
1 | import ProjectCard from "./ProjectCard";
2 |
3 | export default ProjectCard;
4 |
--------------------------------------------------------------------------------
/src/TinyManager/components/ProjectForm/ProjectForm.js:
--------------------------------------------------------------------------------
1 | import React, { useCallback } from "react";
2 | import Divider from "@material-ui/core/Divider";
3 | import PropTypes from "prop-types";
4 | import { makeStyles } from "@material-ui/core/styles";
5 | import TextField from "@material-ui/core/TextField";
6 | import Button from "@material-ui/core/Button";
7 | import Grid from "@material-ui/core/Grid";
8 | import Typography from "@material-ui/core/Typography";
9 |
10 | import Paper from "../Paper";
11 | import { ProjectType } from "TinyManager/types";
12 | import { identity } from "../utils";
13 |
14 | const useStyles = makeStyles((theme) => ({
15 | paper: {
16 | padding: theme.spacing(2),
17 | height: "auto",
18 | },
19 | }));
20 |
21 | function ProjectForm(props) {
22 | const {
23 | title,
24 | values,
25 | errors,
26 | disabled,
27 | translate,
28 | onSubmit,
29 | onChange,
30 | onCancel,
31 | } = props;
32 |
33 | const classes = useStyles();
34 |
35 | const handleChange = useCallback(
36 | (event) => {
37 | if (onChange) {
38 | onChange(event);
39 | }
40 | },
41 | [onChange]
42 | );
43 |
44 | const handleCancel = useCallback(
45 | (event) => {
46 | if (onCancel) {
47 | onCancel(event);
48 | }
49 | },
50 | [onCancel]
51 | );
52 |
53 | const handleSubmit = useCallback(
54 | (event) => {
55 | event.preventDefault();
56 | if (onSubmit) {
57 | onSubmit(event);
58 | }
59 | },
60 | [onSubmit]
61 | );
62 |
63 | return (
64 |
125 | );
126 | }
127 |
128 | ProjectForm.propTypes = {
129 | /**
130 | * Title of the form
131 | */
132 | title: PropTypes.string,
133 | /**
134 | * Values of the form
135 | */
136 | values: ProjectType,
137 | /**
138 | * Errors in the form
139 | */
140 | errors: ProjectType,
141 | /**
142 | * Disabled
143 | */
144 | disabled: PropTypes.bool,
145 | /**
146 | * Translator
147 | */
148 | translate: PropTypes.func,
149 | /**
150 | * Input on change handler
151 | */
152 | onChange: PropTypes.func,
153 | /**
154 | * Form on submit handler
155 | */
156 | onSubmit: PropTypes.func,
157 | /**
158 | * Form cancel handler
159 | */
160 | onCancel: PropTypes.func,
161 | };
162 |
163 | ProjectForm.defaultProps = {
164 | values: {},
165 | errors: {},
166 | disabled: false,
167 | title: "New Project",
168 | translate: identity,
169 | };
170 |
171 | export default ProjectForm;
172 |
--------------------------------------------------------------------------------
/src/TinyManager/components/ProjectForm/ProjectForm.stories.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | import ProjectForm from "./ProjectForm";
4 | import { withLargeWrapper } from "TinyManager/decorators";
5 |
6 | const argTypes = {
7 | values: {
8 | control: {
9 | type: null,
10 | },
11 | },
12 | errors: {
13 | control: {
14 | type: null,
15 | },
16 | },
17 | };
18 |
19 | export default {
20 | title: "TinyManager/ProjectForm",
21 | component: ProjectForm,
22 | argTypes,
23 | decorators: [withLargeWrapper],
24 | };
25 |
26 | const Template = (args) => ;
27 |
28 | export const Default = Template.bind({});
29 | Default.args = {};
30 |
31 | export const Disabled = Template.bind({});
32 | Disabled.args = {
33 | disabled: true,
34 | };
35 |
--------------------------------------------------------------------------------
/src/TinyManager/components/ProjectForm/index.js:
--------------------------------------------------------------------------------
1 | import ProjectForm from "./ProjectForm";
2 |
3 | export default ProjectForm;
4 |
--------------------------------------------------------------------------------
/src/TinyManager/components/TaskCard/TaskCard.js:
--------------------------------------------------------------------------------
1 | import React, { useCallback } from "react";
2 | import clsx from "clsx";
3 | import PropTypes from "prop-types";
4 | import { makeStyles, useTheme } from "@material-ui/core/styles";
5 | import Typography from "@material-ui/core/Typography";
6 | import IconButton from "@material-ui/core/IconButton";
7 | import Grid from "@material-ui/core/Grid";
8 | import Box from "@material-ui/core/Box";
9 |
10 | import DeleteIcon from "@material-ui/icons/Delete";
11 | import FlagIcon from "@material-ui/icons/Flag";
12 |
13 | import Paper from "../Paper";
14 | import CircularProgressWithLabel from "../CircularProgressWithLabel";
15 | import { TaskType } from "TinyManager/types/index";
16 |
17 | const useStyles = makeStyles((theme) => {
18 | const isDark = theme.palette.type === "dark";
19 | return {
20 | paper: {
21 | height: theme.spacing(7),
22 | alignItems: "center",
23 | },
24 | mediumPriority: {
25 | color: isDark && theme.palette.warning.light,
26 | backgroundColor: !isDark && theme.palette.warning.light,
27 | },
28 | highPriority: {
29 | color: isDark && theme.palette.error.light,
30 | backgroundColor: !isDark && theme.palette.error.light,
31 | },
32 | progress: {
33 | display: "flex",
34 | justifyContent: "flex-end",
35 | alignItems: "center",
36 | },
37 | completedFlag: {
38 | color: theme.palette.success.light,
39 | marginRight: theme.spacing(0.5),
40 | },
41 | };
42 | });
43 |
44 | const PRIORITY = {
45 | low: 0,
46 | medium: 1,
47 | high: 2,
48 | };
49 |
50 | function TaskCard(props) {
51 | const { task, onClick, onDelete, className } = props;
52 | const { priority = 0, progress, title } = task;
53 |
54 | const classes = useStyles();
55 | const theme = useTheme();
56 |
57 | const handleClick = useCallback(
58 | (event) => {
59 | if (onClick) {
60 | onClick(event, task);
61 | }
62 | },
63 | [onClick, task]
64 | );
65 |
66 | const handleDelete = useCallback(
67 | (event) => {
68 | if (onDelete) {
69 | onDelete(event, task);
70 | }
71 | },
72 | [onDelete, task]
73 | );
74 |
75 | const completed = progress === 100;
76 |
77 | return (
78 |
90 |
91 |
92 |
93 | {completed ? : null}
94 |
99 | {title}
100 |
101 |
102 |
103 |
110 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 | );
129 | }
130 |
131 | TaskCard.propTypes = {
132 | /**
133 | * Task details
134 | */
135 | task: TaskType,
136 | /**
137 | * Task click handler
138 | */
139 | onClick: PropTypes.func,
140 | /**
141 | * Task delete handler
142 | */
143 | onDelete: PropTypes.func,
144 | /**
145 | * @ignore
146 | */
147 | className: PropTypes.string,
148 | };
149 |
150 | TaskCard.defaultProps = {
151 | task: {},
152 | };
153 |
154 | export default TaskCard;
155 |
--------------------------------------------------------------------------------
/src/TinyManager/components/TaskCard/TaskCard.stories.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | import TaskCard from "./TaskCard";
4 | import { withWrapper } from "TinyManager/decorators";
5 |
6 | export default {
7 | title: "TinyManager/TaskCard",
8 | component: TaskCard,
9 | decorators: [withWrapper],
10 | argTypes: {
11 | task: {
12 | control: {
13 | type: null,
14 | },
15 | },
16 | },
17 | };
18 |
19 | const Template = (args) => ;
20 |
21 | export const Default = Template.bind({});
22 | Default.args = {
23 | task: {
24 | title: "Initate production",
25 | progress: 90,
26 | },
27 | };
28 |
29 | export const LowPriority = Template.bind({});
30 | LowPriority.args = {
31 | task: {
32 | ...Default.args.task,
33 | priority: 0,
34 | },
35 | };
36 |
37 | export const MediumPriority = Template.bind({});
38 | MediumPriority.args = {
39 | task: {
40 | ...Default.args.task,
41 | priority: 1,
42 | },
43 | };
44 |
45 | export const HighPriority = Template.bind({});
46 | HighPriority.args = {
47 | task: {
48 | ...Default.args.task,
49 | priority: 2,
50 | },
51 | };
52 |
53 | export const Completed = Template.bind({});
54 | Completed.args = {
55 | task: {
56 | ...Default.args.task,
57 | progress: 100,
58 | },
59 | };
60 |
--------------------------------------------------------------------------------
/src/TinyManager/components/TaskCard/index.js:
--------------------------------------------------------------------------------
1 | import TaskCard from "./TaskCard";
2 |
3 | export default TaskCard;
4 |
--------------------------------------------------------------------------------
/src/TinyManager/components/TaskForm/TaskForm.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import TextField from "@material-ui/core/TextField";
3 | import Button from "@material-ui/core/Button";
4 | import Grid from "@material-ui/core/Grid";
5 | import Typography from "@material-ui/core/Typography";
6 | import Divider from "@material-ui/core/Divider";
7 | import Select from "@material-ui/core/Select";
8 | import FormControl from "@material-ui/core/FormControl";
9 | import InputLabel from "@material-ui/core/InputLabel";
10 | import MenuItem from "@material-ui/core/MenuItem";
11 | import PropTypes from "prop-types";
12 | import { makeStyles } from "@material-ui/core/styles";
13 |
14 | import Paper from "../Paper";
15 | import { TaskType } from "TinyManager/types";
16 | import { identity, noop } from "../utils";
17 |
18 | const PRIORITIES = [
19 | {
20 | value: 0,
21 | label: "Low",
22 | },
23 | {
24 | value: 1,
25 | label: "Medium",
26 | },
27 | {
28 | value: 2,
29 | label: "High",
30 | },
31 | ];
32 |
33 | const PERCENTAGES = new Array(11)
34 | .fill(0)
35 | .map((_, i) => ({ value: i * 10, label: `${i * 10}%` }));
36 |
37 | const useStyles = makeStyles((theme) => ({
38 | paper: {
39 | padding: theme.spacing(2),
40 | height: "auto",
41 | },
42 | }));
43 |
44 | function TaskForm(props) {
45 | const {
46 | title,
47 | values,
48 | errors,
49 | disabled,
50 | translate,
51 | onSubmit,
52 | onChange,
53 | onCancel,
54 | } = props;
55 |
56 | const classes = useStyles();
57 | return (
58 |
164 | );
165 | }
166 |
167 | TaskForm.propTypes = {
168 | title: PropTypes.string,
169 | disabled: PropTypes.bool,
170 | values: TaskType,
171 | errors: TaskType,
172 | translate: PropTypes.func,
173 | onChange: PropTypes.func,
174 | onSubmit: PropTypes.func,
175 | onCancel: PropTypes.func,
176 | };
177 |
178 | TaskForm.defaultProps = {
179 | values: {},
180 | errors: {},
181 | disabled: false,
182 | title: "New Task",
183 | translate: identity,
184 | onChange: noop,
185 | onSubmit: (e) => {
186 | e.preventDefault();
187 | },
188 | onCancel: noop,
189 | };
190 |
191 | export default TaskForm;
192 |
--------------------------------------------------------------------------------
/src/TinyManager/components/TaskForm/TaskForm.stories.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | import TaskForm from "./TaskForm";
4 | import { withLargeWrapper } from "TinyManager/decorators";
5 |
6 | export default {
7 | title: "TinyManager/TaskForm",
8 | component: TaskForm,
9 | decorators: [withLargeWrapper],
10 | };
11 |
12 | const Template = (args) => {
13 | return ;
14 | };
15 |
16 | export const Default = Template.bind({});
17 | Default.args = {};
18 |
19 | export const Disabled = Template.bind({});
20 | Disabled.args = {
21 | disabled: true,
22 | };
23 |
--------------------------------------------------------------------------------
/src/TinyManager/components/TaskForm/index.js:
--------------------------------------------------------------------------------
1 | import TaskForm from "./TaskForm";
2 |
3 | export default TaskForm;
4 |
--------------------------------------------------------------------------------
/src/TinyManager/components/Todo/Todo.js:
--------------------------------------------------------------------------------
1 | import React, { useCallback } from "react";
2 | import PropTypes from "prop-types";
3 | import ListItem from "@material-ui/core/ListItem";
4 | import ListItemIcon from "@material-ui/core/ListItemIcon";
5 | import ListItemText from "@material-ui/core/ListItemText";
6 | import ListItemSecondaryAction from "@material-ui/core/ListItemSecondaryAction";
7 | import Checkbox from "@material-ui/core/Checkbox";
8 | import { makeStyles } from "@material-ui/core/styles";
9 |
10 | import IconButton from "TinyManager/components/IconButton";
11 | import { TodoType } from "TinyManager/types";
12 |
13 | const useStyles = makeStyles({
14 | item: {
15 | listStyle: "none",
16 | },
17 | });
18 |
19 | function Todo(props) {
20 | const { todo, divider, onClick, onCheck, onDelete } = props;
21 | const { title, completed } = todo;
22 |
23 | const classes = useStyles();
24 |
25 | const handleCheck = useCallback(
26 | (event) => {
27 | if (onCheck) {
28 | onCheck(event, todo);
29 | }
30 | },
31 | [onCheck, todo]
32 | );
33 |
34 | const handleDelete = useCallback(
35 | (event) => {
36 | if (onDelete) {
37 | onDelete(event, todo);
38 | }
39 | },
40 | [onDelete, todo]
41 | );
42 |
43 | const handleClick = useCallback(
44 | (event) => {
45 | if (onClick) {
46 | onClick(event, todo);
47 | }
48 | },
49 | [onClick, todo]
50 | );
51 |
52 | return (
53 |
59 |
60 |
67 |
68 |
75 |
76 |
82 |
83 |
84 | );
85 | }
86 |
87 | Todo.propTypes = {
88 | /**
89 | * Todo
90 | */
91 | todo: TodoType,
92 | /**
93 | * Divider beneath todo
94 | */
95 | divider: PropTypes.bool,
96 | /**
97 | * Todo on check handler
98 | */
99 | onCheck: PropTypes.func,
100 | /**
101 | * Todo on delete handler
102 | */
103 | onDelete: PropTypes.func,
104 | /**
105 | * Todo on click handler
106 | */
107 | onClick: PropTypes.func,
108 | };
109 |
110 | Todo.defaultProps = {
111 | todo: {},
112 | divider: true,
113 | };
114 |
115 | export default Todo;
116 |
--------------------------------------------------------------------------------
/src/TinyManager/components/Todo/Todo.stories.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | import Todo from "./Todo";
4 | import { withWrapper } from "TinyManager/decorators";
5 |
6 | const argTypes = {
7 | todo: {
8 | control: {
9 | type: null,
10 | },
11 | },
12 | divider: {
13 | control: {
14 | type: "boolean",
15 | },
16 | },
17 | };
18 |
19 | export default {
20 | title: "TinyManager/Todo",
21 | component: Todo,
22 | argTypes,
23 | decorators: [withWrapper],
24 | };
25 |
26 | const Template = (args) => {
27 | return ;
28 | };
29 |
30 | export const Default = Template.bind({});
31 | Default.args = {
32 | todo: { title: "Todo task" },
33 | };
34 |
35 | export const Completed = Template.bind({});
36 | Completed.args = {
37 | todo: { ...Default.args.todo, completed: true },
38 | };
39 |
--------------------------------------------------------------------------------
/src/TinyManager/components/Todo/index.js:
--------------------------------------------------------------------------------
1 | import Todo from "./Todo";
2 |
3 | export default Todo;
4 |
--------------------------------------------------------------------------------
/src/TinyManager/components/TodoForm/TodoForm.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 | import TextField from "@material-ui/core/TextField";
4 | import Grid from "@material-ui/core/Grid";
5 | import Button from "@material-ui/core/Button";
6 | import Typography from "@material-ui/core/Typography";
7 | import Divider from "@material-ui/core/Divider";
8 | import { makeStyles } from "@material-ui/core/styles";
9 |
10 | import { TodoType } from "TinyManager/types";
11 | import { identity, noop } from "../utils";
12 |
13 | const useStyles = makeStyles((theme) => ({
14 | form: {
15 | padding: theme.spacing(2),
16 | height: "auto",
17 | },
18 | }));
19 |
20 | function TodoForm(props) {
21 | const {
22 | title,
23 | values,
24 | errors,
25 | disabled,
26 | translate,
27 | onChange,
28 | onSubmit,
29 | onCancel,
30 | } = props;
31 |
32 | const classes = useStyles();
33 | return (
34 |
74 | );
75 | }
76 |
77 | TodoForm.propTypes = {
78 | title: PropTypes.string,
79 | values: TodoType,
80 | errors: TodoType,
81 | disabled: PropTypes.bool,
82 | translate: PropTypes.func,
83 | onChange: PropTypes.func,
84 | onSubmit: PropTypes.func,
85 | onCancel: PropTypes.func,
86 | };
87 |
88 | TodoForm.defaultProps = {
89 | title: "Add Todo",
90 | values: {},
91 | errors: {},
92 | disabled: false,
93 | translate: identity,
94 | onSubmit: (e) => e.preventDefault(),
95 | onChange: noop,
96 | onCancel: noop,
97 | };
98 |
99 | export default TodoForm;
100 |
--------------------------------------------------------------------------------
/src/TinyManager/components/TodoForm/TodoForm.stories.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | import TodoForm from "./TodoForm";
4 | import { TodoType } from "TinyManager/types";
5 | import { withLargeWrapper } from "TinyManager/decorators";
6 |
7 | const argTypes = {
8 | title: {
9 | label: "title",
10 | description: "Title of form",
11 | type: { name: "string" },
12 | control: {
13 | type: "string",
14 | },
15 | },
16 | disabled: {
17 | label: "disabled",
18 | description: "Disable state of form",
19 | type: {
20 | name: "boolean",
21 | },
22 | control: {
23 | type: "boolean",
24 | },
25 | },
26 | values: {
27 | label: "values",
28 | description: "Values of the form",
29 | type: TodoType,
30 | control: {
31 | type: null,
32 | },
33 | },
34 | errors: {
35 | label: "errors",
36 | description: "Errors of the form",
37 | type: TodoType,
38 | control: {
39 | type: null,
40 | },
41 | },
42 | onChange: {
43 | label: "onChange",
44 | description: "Form change handler",
45 | type: "func",
46 | control: {
47 | type: null,
48 | },
49 | },
50 | onCancel: {
51 | label: "onCancel",
52 | description: "Form cancel handler",
53 | type: "func",
54 | control: {
55 | type: null,
56 | },
57 | },
58 | onSubmit: {
59 | label: "onSubmit",
60 | description: "Form submit handler",
61 | type: "func",
62 | control: {
63 | type: null,
64 | },
65 | },
66 | };
67 |
68 | export default {
69 | title: "TinyManager/TodoForm",
70 | component: TodoForm,
71 | argTypes,
72 | decorators: [withLargeWrapper],
73 | };
74 |
75 | const Template = (args) => ;
76 |
77 | export const Default = Template.bind({});
78 | Default.args = {};
79 |
80 | export const Disabled = Template.bind({});
81 | Disabled.args = { disabled: true };
82 |
83 | export const Title = Template.bind({});
84 | Title.args = { title: "Make Coffee" };
85 |
86 | export const Error = Template.bind({});
87 | Error.args = { errors: { title: "Title is required" } };
88 |
--------------------------------------------------------------------------------
/src/TinyManager/components/TodoForm/index.js:
--------------------------------------------------------------------------------
1 | import TodoForm from "./TodoForm";
2 |
3 | export default TodoForm;
4 |
--------------------------------------------------------------------------------
/src/TinyManager/components/TodoList/TodoList.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 | import List from "@material-ui/core/List";
4 | import Typography from "@material-ui/core/Typography";
5 | import { makeStyles } from "@material-ui/core/styles";
6 |
7 | import Todo from "../Todo";
8 | import { TodoType } from "TinyManager/types";
9 | import { noop } from "../utils";
10 |
11 | const useStyles = makeStyles({
12 | list: {
13 | overflow: "auto",
14 | height: "90%",
15 | },
16 | });
17 |
18 | function TodoList(props) {
19 | const { todos, translate, onClick, onCheck, onDelete } = props;
20 |
21 | const classes = useStyles();
22 |
23 | if (todos.length === 0) {
24 | return (
25 |
26 | {translate("Nothing to be completed. Enjoy your day.")}
27 |
28 | );
29 | }
30 |
31 | return (
32 |
33 | {todos.map((todo) => (
34 |
41 | ))}
42 |
43 | );
44 | }
45 |
46 | TodoList.propTypes = {
47 | /**
48 | * List of todos
49 | */
50 | todos: PropTypes.arrayOf(TodoType),
51 | /**
52 | * Translate
53 | */
54 | translate: PropTypes.func,
55 | /**
56 | * Todo on click handler
57 | */
58 | onClick: PropTypes.func,
59 | /**
60 | * Todo on check handler
61 | */
62 | onCheck: PropTypes.func,
63 | /**
64 | * Todo on delete handler
65 | */
66 | onDelete: PropTypes.func,
67 | };
68 |
69 | TodoList.defaultProps = {
70 | todos: [],
71 | onClick: noop,
72 | onCheck: noop,
73 | };
74 |
75 | export default TodoList;
76 |
--------------------------------------------------------------------------------
/src/TinyManager/components/TodoList/TodoList.stories.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | import TodoList from "./TodoList";
4 | import { withWrapper } from "TinyManager/decorators";
5 |
6 | const argTypes = {
7 | todos: {
8 | name: "todos",
9 | description: "List of todo",
10 | type: {
11 | name: "array",
12 | },
13 | control: {
14 | type: null,
15 | },
16 | },
17 | onTodoClick: {
18 | name: "onTodoClick",
19 | description: "Todo click handler",
20 | type: {
21 | name: "function",
22 | },
23 | control: {
24 | type: null,
25 | },
26 | },
27 | };
28 |
29 | export default {
30 | title: "TinyManager/TodoList",
31 | component: TodoList,
32 | argTypes,
33 | decorators: [withWrapper],
34 | };
35 |
36 | const Template = (args) => {
37 | return ;
38 | };
39 |
40 | export const Default = Template.bind({});
41 | Default.args = {
42 | todos: new Array(10)
43 | .fill(0)
44 | .map((_, id) => ({ id, title: `Todo - ${id}`, completed: id % 2 === 0 })),
45 | };
46 |
47 | export const Empty = Template.bind({});
48 | Empty.args = {
49 | todos: [],
50 | };
51 |
--------------------------------------------------------------------------------
/src/TinyManager/components/TodoList/index.js:
--------------------------------------------------------------------------------
1 | import TodoList from "./TodoList";
2 |
3 | export default TodoList;
4 |
--------------------------------------------------------------------------------
/src/TinyManager/components/Topbar/index.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import { Link } from "react-router-dom";
3 | import PropTypes from "prop-types";
4 | import MuiAppBar from "@material-ui/core/AppBar";
5 | import Toolbar from "@material-ui/core/Toolbar";
6 | import Button from "@material-ui/core/Button";
7 | import Typography from "@material-ui/core/Typography";
8 | import Menu from "@material-ui/core/Menu";
9 | import MenuItem from "@material-ui/core/MenuItem";
10 | import MuiLink from "@material-ui/core/Link";
11 | import Box from "@material-ui/core/Box";
12 | import { makeStyles, withStyles } from "@material-ui/core/styles";
13 | import IconButton from "TinyManager/components/IconButton";
14 | import { identity } from "TinyManager/components/utils";
15 | import ListIcon from "@material-ui/icons/List";
16 |
17 | const AppBar = withStyles((theme) => ({
18 | root: {
19 | boxShadow: theme.custom.shadow[0],
20 | },
21 | }))(MuiAppBar);
22 |
23 | const useStyles = makeStyles((theme) => ({
24 | title: { flexGrow: 1, cursor: "pointer" },
25 | icon: {
26 | color: theme.palette.primary.main,
27 | },
28 | }));
29 |
30 | function DarkThemeButton(props) {
31 | const { onClick } = props;
32 | return (
33 |
39 | );
40 | }
41 |
42 | DarkThemeButton.propTypes = {
43 | onClick: PropTypes.func,
44 | };
45 |
46 | function GithubIconButton() {
47 | return (
48 |
55 | );
56 | }
57 |
58 | function LanguageSelection(props) {
59 | const { languages, onChange } = props;
60 |
61 | const [languageAnchorEl, setLanguageAnchorEl] = useState(null);
62 |
63 | const openLanguageSelection = (event) => {
64 | setLanguageAnchorEl(event.currentTarget);
65 | };
66 |
67 | const closeLanguageSelection = () => {
68 | setLanguageAnchorEl(null);
69 | };
70 |
71 | const handleLanguageChange = (language) => (event) => {
72 | if (onChange) {
73 | onChange(event, language);
74 | closeLanguageSelection();
75 | }
76 | };
77 |
78 | return (
79 | <>
80 |
87 |
94 | {languages.map(({ value, label }) => (
95 |
96 | {label}
97 |
98 | ))}
99 |
100 | >
101 | );
102 | }
103 |
104 | LanguageSelection.propTypes = {
105 | languages: PropTypes.array,
106 | onChange: PropTypes.func,
107 | };
108 |
109 | LanguageSelection.defaultProps = {
110 | languages: [],
111 | };
112 |
113 | function Topbar(props) {
114 | const {
115 | onToggleDarkMode,
116 | languages = [],
117 | onlanguageChange,
118 | translate,
119 | } = props;
120 |
121 | const classes = useStyles();
122 |
123 | return (
124 | <>
125 |
126 |
127 |
128 |
129 |
130 | {translate("Tiny Manager")}
131 |
132 |
133 |
134 |
135 | {translate("Home")}
136 |
137 |
138 |
139 | {translate("Projects")}
140 |
141 |
142 |
143 |
147 |
148 |
149 |
150 |
151 |
152 | >
153 | );
154 | }
155 |
156 | Topbar.propTypes = {
157 | onToggleDarkMode: PropTypes.func,
158 | onlanguageChange: PropTypes.func,
159 | languages: PropTypes.array,
160 | translate: PropTypes.func,
161 | };
162 |
163 | Topbar.defaultProps = {
164 | translate: identity,
165 | };
166 |
167 | export default Topbar;
168 |
--------------------------------------------------------------------------------
/src/TinyManager/components/utils.js:
--------------------------------------------------------------------------------
1 | function noop() {}
2 |
3 | function identity(value) {
4 | return value;
5 | }
6 |
7 | export { noop, identity };
8 |
--------------------------------------------------------------------------------
/src/TinyManager/containers/Content/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Switch, Route } from "react-router-dom";
3 | import { makeStyles } from "@material-ui/core/styles";
4 |
5 | import Topbar from "TinyManager/containers/Topbar";
6 | import Home from "TinyManager/containers/Home";
7 | import Projects from "TinyManager/containers/Projects";
8 |
9 | const useStyles = makeStyles((theme) => ({
10 | root: {
11 | padding: theme.spacing(1),
12 | height: "100%",
13 | },
14 | main: {
15 | display: "flex",
16 | height: `calc(100% - ${theme.spacing(8)}px)`, // $topbar height
17 | width: "100%",
18 | overflow: "auto",
19 | },
20 | }));
21 |
22 | function Content() {
23 | const classes = useStyles();
24 | return (
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 | );
35 | }
36 |
37 | export default Content;
38 |
--------------------------------------------------------------------------------
/src/TinyManager/containers/Home/index.js:
--------------------------------------------------------------------------------
1 | import React, { useCallback, useState } from "react";
2 | import Fade from "@material-ui/core/Fade";
3 | import Typography from "@material-ui/core/Typography";
4 | import Tabs from "@material-ui/core/Tabs";
5 | import Tab from "@material-ui/core/Tab";
6 | import FormControlLabel from "@material-ui/core/FormControlLabel";
7 | import Checkbox from "@material-ui/core/Checkbox";
8 | import { makeStyles } from "@material-ui/core/styles";
9 |
10 | import Notes from "TinyManager/containers/Notes";
11 | import Todos from "TinyManager/containers/Todos";
12 | import QuoteService from "TinyManager/services/QuoteService";
13 | import TinyManagerAPI from "TinyManager/services/TinyManagerAPI";
14 | import { useTranslation } from "TinyManager/providers/TranslationProvider";
15 |
16 | const TAB = {
17 | Todos: 0,
18 | Notes: 1,
19 | };
20 |
21 | const useStyles = makeStyles(() => ({
22 | container: {
23 | display: "flex",
24 | flex: 1,
25 | justifyContent: "center",
26 | },
27 | content: {
28 | width: "100%",
29 | height: "100%",
30 | display: "flex",
31 | flex: 1,
32 | flexDirection: "column",
33 | alignItems: "center",
34 | maxWidth: 500,
35 | },
36 | checkbox: {
37 | alignSelf: "flex-end",
38 | },
39 | }));
40 |
41 | const quote = QuoteService.getQuote();
42 |
43 | function Home() {
44 | const classes = useStyles();
45 | const { t } = useTranslation();
46 |
47 | const defaultNotes = TinyManagerAPI.fetchDefaultNotes();
48 |
49 | const [tab, setTab] = useState(defaultNotes ? TAB.Notes : TAB.Todos);
50 |
51 | const [checked, setChecked] = useState(defaultNotes);
52 |
53 | const handleChangeTab = useCallback((event, tab) => {
54 | setTab(tab);
55 | }, []);
56 |
57 | const handleChangeDefaultNotes = useCallback((event) => {
58 | const { checked } = event.target;
59 | setChecked(checked);
60 | TinyManagerAPI.updateDefaultNotes(checked);
61 | }, []);
62 |
63 | return (
64 |
65 |
66 |
67 |
68 |
69 | {quote}
70 |
71 |
72 |
73 |
80 | }
81 | label={t("Default Notes")}
82 | />
83 |
84 |
85 |
91 |
92 |
93 |
94 |
95 | {tab === TAB.Todos &&
}
96 | {tab === TAB.Notes &&
}
97 |
98 |
99 |
100 | );
101 | }
102 |
103 | export default Home;
104 |
--------------------------------------------------------------------------------
/src/TinyManager/containers/Notes/index.js:
--------------------------------------------------------------------------------
1 | import React, { useCallback, useState } from "react";
2 | import PropTypes from "prop-types";
3 | import { makeStyles } from "@material-ui/core/styles";
4 | import debounce from "lodash.debounce";
5 | import Box from "@material-ui/core/Box";
6 | import TextField from "@material-ui/core/TextField";
7 | import Typography from "@material-ui/core/Typography";
8 | import IconButton from "@material-ui/core/IconButton";
9 | import Menu from "@material-ui/core/Menu";
10 | import MenuItem from "@material-ui/core/MenuItem";
11 | import jsPDF from "jspdf";
12 |
13 | import DownloadIcon from "@material-ui/icons/SaveAlt";
14 | import ClearIcon from "@material-ui/icons/DeleteForever";
15 |
16 | import TinyManagerAPI from "TinyManager/services/TinyManagerAPI";
17 | import { useTranslation } from "TinyManager/providers/TranslationProvider";
18 | import { formatDate } from "TinyManager/services/Utils";
19 |
20 | const useStyles = makeStyles((theme) => ({
21 | container: {
22 | display: "flex",
23 | width: "100%",
24 | overflow: "hidden",
25 | flexDirection: "column",
26 | flex: 1,
27 | },
28 | action: {
29 | marginBottom: theme.spacing(1),
30 | },
31 | input: {
32 | display: "flex",
33 | overflow: "auto",
34 | flex: 1,
35 | },
36 | inputRoot: {
37 | flex: 1,
38 | },
39 | inputControl: {
40 | flex: 1,
41 | display: "flex",
42 | flexDirection: "column",
43 | overflow: "hidden",
44 | },
45 | caption: {
46 | display: "block",
47 | textAlign: "right",
48 | marginTop: theme.spacing(1),
49 | },
50 | }));
51 |
52 | function DownloadButton(props) {
53 | const { notes } = props;
54 |
55 | const { t } = useTranslation();
56 | const [anchorEl, setAnchorEl] = useState(null);
57 |
58 | const openMenu = useCallback((event) => {
59 | setAnchorEl(event.currentTarget);
60 | }, []);
61 |
62 | const closeMenu = useCallback(() => {
63 | setAnchorEl(null);
64 | }, []);
65 |
66 | const handleMenuItemClick = useCallback(
67 | (cb) => (event) => {
68 | closeMenu();
69 | cb(event);
70 | },
71 | [closeMenu]
72 | );
73 |
74 | return (
75 | <>
76 |
83 |
84 |
85 |
91 | downloadTxt(notes))}>
92 | {t("TXT")}
93 |
94 | downloadPdf(notes))}>
95 | {t("PDF")}
96 |
97 |
98 | >
99 | );
100 | }
101 |
102 | DownloadButton.propTypes = {
103 | notes: PropTypes.string,
104 | };
105 |
106 | const updateStorageNotes = debounce(TinyManagerAPI.updateNotes, 150);
107 |
108 | function fileName(extension = "txt") {
109 | return `Note-${formatDate()}.${extension}`;
110 | }
111 |
112 | function downloadTxt(string) {
113 | const url = window.URL.createObjectURL(new Blob([string]), {
114 | type: "text/plain",
115 | });
116 |
117 | const link = document.createElement("a");
118 | link.href = url;
119 | link.setAttribute("download", fileName());
120 | link.click();
121 | }
122 |
123 | function downloadPdf(string) {
124 | new jsPDF().text(string, 10, 10).save(fileName("pdf"));
125 | }
126 |
127 | function Notes() {
128 | const classes = useStyles();
129 | const { t } = useTranslation();
130 |
131 | const [notes, setNotes] = useState(TinyManagerAPI.fetchNotes());
132 |
133 | const handleChange = useCallback((event) => {
134 | const { value } = event.target;
135 | setNotes(value);
136 | updateStorageNotes(value);
137 | }, []);
138 |
139 | const handleClearNote = useCallback(() => {
140 | handleChange({ target: { value: "" } });
141 | }, [handleChange]);
142 |
143 | const handleKeyDown = useCallback(
144 | (event) => {
145 | const isSave = event.key === "s" && event.ctrlKey === true;
146 | if (isSave) {
147 | event.preventDefault();
148 | downloadTxt(notes);
149 | }
150 | },
151 | [notes]
152 | );
153 |
154 | return (
155 |
156 |
162 |
163 |
169 |
170 |
171 |
172 |
173 |
193 |
194 |
199 | *
200 | {t(
201 | "Notes will be stored locally on the browser and will be persisted."
202 | )}
203 |
204 |
205 | );
206 | }
207 |
208 | export default Notes;
209 |
--------------------------------------------------------------------------------
/src/TinyManager/containers/Projects/ProjectForm.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { useFormik } from "formik";
3 | import PropTypes from "prop-types";
4 | import { makeStyles } from "@material-ui/core/styles";
5 |
6 | import ProjectForm from "TinyManager/components/ProjectForm";
7 | import { ProjectType } from "TinyManager/types/index";
8 | import { merge } from "TinyManager/services/Utils";
9 | import { useTranslation } from "TinyManager/providers/TranslationProvider";
10 |
11 | const useStyles = makeStyles(() => ({
12 | container: {
13 | maxWidth: 500,
14 | marginRight: "auto",
15 | marginLeft: "auto",
16 | },
17 | }));
18 |
19 | function ProjectFormContainer(props) {
20 | const { onCancel, onSubmit, initialValues } = props;
21 |
22 | const classes = useStyles();
23 | const { t } = useTranslation();
24 |
25 | const {
26 | values,
27 | errors,
28 | isSubmitting,
29 | handleChange,
30 | handleSubmit,
31 | } = useFormik({
32 | initialValues: merge({ name: "", description: "" }, initialValues),
33 | validate: (values) => {
34 | const errors = {};
35 |
36 | if (!values.name) {
37 | errors.name = t("Name is required.");
38 | }
39 |
40 | return errors;
41 | },
42 | onSubmit: (values, { setSubmitting }) => {
43 | onSubmit(values);
44 | setSubmitting(false);
45 | },
46 | });
47 |
48 | const edit = values && values.id;
49 | const title = edit ? t("Edit Project") : t("New Project");
50 |
51 | return (
52 |
64 | );
65 | }
66 |
67 | ProjectFormContainer.propTypes = {
68 | initialValues: ProjectType,
69 | onCancel: PropTypes.func,
70 | onSubmit: PropTypes.func,
71 | };
72 |
73 | export default ProjectFormContainer;
74 |
--------------------------------------------------------------------------------
/src/TinyManager/containers/Projects/ProjectList.js:
--------------------------------------------------------------------------------
1 | import React, { useCallback, useEffect, useMemo, useState } from "react";
2 | import PropTypes from "prop-types";
3 | import { makeStyles } from "@material-ui/core/styles";
4 | import Grid from "@material-ui/core/Grid";
5 | import Button from "@material-ui/core/Button";
6 | import Typography from "@material-ui/core/Typography";
7 | import Fade from "@material-ui/core/Fade";
8 |
9 | import Loader from "TinyManager/components/Loader";
10 | import ProjectCard from "TinyManager/components/ProjectCard";
11 | import TinyManagerAPI from "TinyManager/services/TinyManagerAPI";
12 | import { useTranslation } from "TinyManager/providers/TranslationProvider";
13 | import useDialog from "TinyManager/hooks/useDialog";
14 | import ConfirmationDialog from "TinyManager/components/ConfirmationDialog/ConfirmationDialog";
15 | import OutlinedSelect from "TinyManager/components/OutlinedSelect";
16 |
17 | function sortProjectsBy(projects, filter) {
18 | return projects.sort((a, b) => {
19 | if (b[filter] < a[filter]) {
20 | return -1;
21 | }
22 | if (b[filter] > a[filter]) {
23 | return 1;
24 | }
25 | return 0;
26 | });
27 | }
28 |
29 | function filterProjectsBy(projects, filter) {
30 | switch (filter) {
31 | case "pending":
32 | return projects.filter((t) => t.progress !== 100);
33 | case "completed":
34 | return projects.filter((t) => t.progress === 100);
35 | case "all":
36 | default:
37 | return projects;
38 | }
39 | }
40 | const useStyles = makeStyles((theme) => ({
41 | container: {
42 | display: "flex",
43 | flexDirection: "column",
44 | flex: 1,
45 | overflow: "hidden",
46 | },
47 | list: {
48 | flex: 1,
49 | marginTop: theme.spacing(),
50 | overflow: "auto",
51 | padding: theme.spacing(3.5),
52 | },
53 | card: {
54 | marginBottom: theme.spacing(3.5),
55 | },
56 | action: {
57 | marginTop: theme.spacing(1),
58 | },
59 | }));
60 |
61 | function ProjectList(props) {
62 | const { onClick, onNew } = props;
63 |
64 | const classes = useStyles();
65 | const { t } = useTranslation();
66 | const [loading, setLoading] = useState(true);
67 | const [projects, setProjects] = useState([]);
68 | const [project, setProject] = useState(null);
69 | const [deleteDialog, setDeleteDialog] = useState(false);
70 | const [filterBy, setFilterBy] = useState("all");
71 | const [sortBy, setSortBy] = useState("name");
72 |
73 | const [
74 | deleteAllProjectDialog,
75 | openDeleteAllProjectDialog,
76 | closeDeleteAllProjectDialog,
77 | ] = useDialog();
78 |
79 | const sortByMenu = useMemo(
80 | () => [
81 | {
82 | label: t("Progress"),
83 | value: "progress",
84 | },
85 | {
86 | label: t("Name"),
87 | value: "name",
88 | },
89 | ],
90 | [t]
91 | );
92 |
93 | const filterByMenu = useMemo(
94 | () => [
95 | { label: t("All"), value: "all" },
96 | { label: t("Pending"), value: "pending" },
97 | { label: t("Completed"), value: "completed" },
98 | ],
99 | [t]
100 | );
101 |
102 | useEffect(() => {
103 | TinyManagerAPI.fetchProjects()
104 | .then(async (projects) => {
105 | const tasks = await Promise.all(
106 | projects.map((project) =>
107 | TinyManagerAPI.fetchTasks(Number(project.id))
108 | )
109 | );
110 |
111 | projects = projects.map((project, i) => {
112 | const projectTasks = tasks[i];
113 | project.tasks = projectTasks;
114 | project.progress =
115 | projectTasks && projectTasks.length
116 | ? projectTasks.reduce(
117 | (progress, task) =>
118 | progress +
119 | ((100 / projectTasks.length) * Number(task.progress)) / 100,
120 | 0
121 | )
122 | : 0;
123 | return project;
124 | });
125 |
126 | setLoading(false);
127 | setProjects(projects);
128 | })
129 | .catch(() => {
130 | setLoading(false);
131 | });
132 | }, []);
133 |
134 | const handleProjectClick = (event, project) => {
135 | onClick(project.id);
136 | };
137 |
138 | const openDeleteDialog = useCallback((event, project) => {
139 | event.stopPropagation();
140 | setDeleteDialog(true);
141 | setProject(project);
142 | }, []);
143 |
144 | const closeDeleteDialog = useCallback(() => {
145 | setDeleteDialog(false);
146 | setProject(null);
147 | }, []);
148 |
149 | const handleDeleteProject = useCallback(() => {
150 | const { id } = project;
151 |
152 | TinyManagerAPI.removeProject(id).then(() => {
153 | setProjects((projects) => projects.filter((p) => p.id !== id));
154 | });
155 |
156 | closeDeleteDialog();
157 | }, [project, closeDeleteDialog]);
158 |
159 | const handleDeleteAllProject = useCallback(() => {
160 | if (projects.length) {
161 | const { projectIds, taskIds } = projects.reduce(
162 | ({ projectIds, taskIds }, project) => {
163 | return {
164 | projectIds: [...projectIds, project.id],
165 | taskIds: [...taskIds, ...project.tasks.map((t) => t.id)],
166 | };
167 | },
168 | { projectIds: [], taskIds: [] }
169 | );
170 |
171 | TinyManagerAPI.removeBulkProjects(projectIds).then(async () => {
172 | await TinyManagerAPI.removeBulkTasks(taskIds);
173 | setProjects([]);
174 | closeDeleteAllProjectDialog();
175 | });
176 | }
177 | }, [closeDeleteAllProjectDialog, projects]);
178 |
179 | const handleChangeFilterBy = useCallback((event) => {
180 | setFilterBy(event.target.value);
181 | }, []);
182 |
183 | const handleChangeSortBy = useCallback((event) => {
184 | setSortBy(event.target.value);
185 | }, []);
186 |
187 | const hasProjects = projects && projects.length;
188 |
189 | let $projects = sortProjectsBy(projects, sortBy);
190 | $projects = filterProjectsBy(projects, filterBy);
191 |
192 | if (loading) {
193 | return ;
194 | }
195 |
196 | return (
197 |
198 |
199 |
200 |
201 | {t("Projects")}
202 |
203 |
204 | {t("Add New Project")}
205 |
206 |
207 |
213 | {t("Delete All")}
214 |
215 |
216 |
217 |
226 |
227 |
235 |
236 |
237 | {hasProjects && $projects.length ? (
238 |
239 | {$projects.map((project) => (
240 |
241 |
249 |
250 | ))}
251 |
252 | ) : (
253 |
254 | {t("No available projects.")}
255 |
256 | )}
257 |
258 |
268 |
276 |
277 |
278 | );
279 | }
280 |
281 | ProjectList.propTypes = {
282 | onNew: PropTypes.func,
283 | onClick: PropTypes.func,
284 | };
285 |
286 | export default ProjectList;
287 |
--------------------------------------------------------------------------------
/src/TinyManager/containers/Projects/ProjectView.js:
--------------------------------------------------------------------------------
1 | import React, { useCallback, useEffect, useMemo, useState } from "react";
2 | import PropTypes from "prop-types";
3 | import { makeStyles } from "@material-ui/core/styles";
4 | import Typography from "@material-ui/core/Typography";
5 | import Grid from "@material-ui/core/Grid";
6 | import Button from "@material-ui/core/Button";
7 | import Fade from "@material-ui/core/Fade";
8 | import Dialog from "@material-ui/core/Dialog";
9 |
10 | import TinyManagerAPI from "TinyManager/services/TinyManagerAPI";
11 | import useDialog from "TinyManager/hooks/useDialog";
12 | import { useTranslation } from "TinyManager/providers/TranslationProvider";
13 | import TaskFormContainer from "TinyManager/containers/Projects/TaskForm";
14 | import ProjectFormContainer from "TinyManager/containers/Projects/ProjectForm";
15 | import ProjectCard from "TinyManager/components/ProjectCard";
16 | import Loader from "TinyManager/components/Loader";
17 | import TaskCard from "TinyManager/components/TaskCard";
18 | import ConfirmationDialog from "TinyManager/components/ConfirmationDialog";
19 | import OutlinedSelect from "TinyManager/components/OutlinedSelect";
20 |
21 | const useStyles = makeStyles((theme) => ({
22 | container: {
23 | display: "flex",
24 | flexDirection: "column",
25 | flex: 1,
26 | },
27 | noTasksMessageWrapper: {
28 | marginTop: theme.spacing(),
29 | marginRight: "auto",
30 | marginLeft: "auto",
31 | maxWidth: 500,
32 | },
33 | noTasksMessage: {
34 | minWidth: 500,
35 | },
36 | selection: {
37 | marginRight: theme.spacing(),
38 | },
39 | list: {
40 | padding: `${theme.spacing(3)}px ${theme.spacing(2)}px`,
41 | height: "85%",
42 | overflow: "auto",
43 | alignContent: "baseline",
44 | },
45 | action: {
46 | maxWidth: 500,
47 | marginRight: "auto",
48 | marginLeft: "auto",
49 | },
50 | }));
51 |
52 | function sortTasksBy(tasks, filter) {
53 | return tasks.sort((a, b) => {
54 | if (b[filter] < a[filter]) {
55 | return -1;
56 | }
57 | if (b[filter] > a[filter]) {
58 | return 1;
59 | }
60 | return 0;
61 | });
62 | }
63 |
64 | function filterTasksBy(tasks, filter) {
65 | switch (filter) {
66 | case "pending":
67 | return tasks.filter((t) => t.progress !== 100);
68 | case "completed":
69 | return tasks.filter((t) => t.progress === 100);
70 | case "all":
71 | default:
72 | return tasks;
73 | }
74 | }
75 |
76 | function ProjectView(props) {
77 | const { match, redirectToProjectList } = props;
78 | const { params } = match;
79 | const { projectId } = params;
80 |
81 | const classes = useStyles();
82 |
83 | const [{ loading, project, tasks, task }, setStore] = useState({
84 | loading: true,
85 | project: {},
86 | tasks: [],
87 | task: {},
88 | });
89 |
90 | const [sortBy, setSortBy] = useState("createdAt");
91 | const [filterBy, setFilterBy] = useState("all");
92 | const [taskDialog, openTaskDialog, closeTaskDialog] = useDialog();
93 | const [projectDialog, openProjectDialog, closeProjectDialog] = useDialog();
94 | const [
95 | deleteAllTaskDialog,
96 | openDeleteAllTaskDialog,
97 | closeDeleteAllTaskDialog,
98 | ] = useDialog();
99 | const { t } = useTranslation();
100 |
101 | const sortByMenu = useMemo(
102 | () => [
103 | {
104 | label: t("Priority"),
105 | value: "priority",
106 | },
107 | {
108 | label: t("Progress"),
109 | value: "progress",
110 | },
111 | {
112 | label: t("Title"),
113 | value: "title",
114 | },
115 | {
116 | label: t("Created"),
117 | value: "createdAt",
118 | },
119 | {
120 | label: t("Updated"),
121 | value: "updatedAt",
122 | },
123 | ],
124 | [t]
125 | );
126 |
127 | const filterByMenu = useMemo(
128 | () => [
129 | { label: t("All"), value: "all" },
130 | { label: t("Pending"), value: "pending" },
131 | { label: t("Completed"), value: "completed" },
132 | ],
133 | [t]
134 | );
135 |
136 | const handleCloseTaskDialog = useCallback(() => {
137 | closeTaskDialog(false);
138 | setStore((store) => ({ ...store, task: {} }));
139 | }, [closeTaskDialog]);
140 |
141 | const handleChangeSortBy = useCallback((event) => {
142 | setSortBy(event.target.value);
143 | }, []);
144 |
145 | const handleChangeFilterBy = useCallback((event) => {
146 | setFilterBy(event.target.value);
147 | }, []);
148 |
149 | const handleUpdateTask = useCallback(
150 | (task) => {
151 | TinyManagerAPI.updateTask(task.id, task).then((task) => {
152 | setStore((store) => ({
153 | ...store,
154 | tasks: store.tasks.map((t) => (t.id === task.id ? task : t)),
155 | }));
156 | handleCloseTaskDialog();
157 | });
158 | },
159 | [handleCloseTaskDialog]
160 | );
161 |
162 | const handleUpdateProject = useCallback(
163 | (project) => {
164 | TinyManagerAPI.updateProject(project.id, project).then((project) => {
165 | setStore((store) => ({
166 | ...store,
167 | project,
168 | }));
169 | closeProjectDialog();
170 | });
171 | },
172 | [closeProjectDialog]
173 | );
174 |
175 | const handleAddNewTask = useCallback(
176 | (task) => {
177 | TinyManagerAPI.addTask(
178 | Object.assign({ projectId: Number(projectId) }, task)
179 | ).then((task) => {
180 | setStore((store) => ({ ...store, tasks: [task, ...store.tasks] }));
181 | handleCloseTaskDialog();
182 | });
183 | },
184 | [handleCloseTaskDialog, projectId]
185 | );
186 |
187 | const handleTaskFormSubmit = useCallback(
188 | (task) => {
189 | const handler = task.id ? handleUpdateTask : handleAddNewTask;
190 | handler(task);
191 | },
192 | [handleUpdateTask, handleAddNewTask]
193 | );
194 |
195 | const handleTaskClick = useCallback(
196 | (e, task = {}) => {
197 | setStore((store) => ({ ...store, task }));
198 | openTaskDialog();
199 | },
200 | [openTaskDialog]
201 | );
202 |
203 | const handleTaskDelete = useCallback((e, task) => {
204 | TinyManagerAPI.removeTask(task.id);
205 | setStore((store) => ({
206 | ...store,
207 | tasks: store.tasks.filter((t) => t.id !== task.id),
208 | }));
209 | }, []);
210 |
211 | const handleProjectEditClick = useCallback(() => {
212 | openProjectDialog();
213 | }, [openProjectDialog]);
214 |
215 | const handleDeleteAllTask = useCallback(() => {
216 | if (tasks.length) {
217 | TinyManagerAPI.removeBulkTasks(tasks.map(({ id }) => id)).then(() => {
218 | setStore((store) => ({ ...store, tasks: [] }));
219 | closeDeleteAllTaskDialog();
220 | });
221 | }
222 | }, [closeDeleteAllTaskDialog, tasks]);
223 |
224 | useEffect(() => {
225 | if (projectId) {
226 | setStore((store) => ({ ...store, loading: true }));
227 | TinyManagerAPI.fetchProject(Number(projectId))
228 | .then((project) => {
229 | TinyManagerAPI.fetchTasks(Number(projectId)).then((tasks) => {
230 | setStore((store) => ({ ...store, loading: false, project, tasks }));
231 | });
232 | })
233 | .catch(redirectToProjectList);
234 | } else {
235 | setStore((store) => ({ ...store, loading: false }));
236 | redirectToProjectList();
237 | }
238 | }, [projectId, redirectToProjectList]);
239 |
240 | if (loading) {
241 | return ;
242 | }
243 |
244 | const hasTasks = tasks && tasks.length;
245 |
246 | let $tasks = sortTasksBy(tasks, sortBy);
247 | $tasks = filterTasksBy($tasks, filterBy);
248 |
249 | return (
250 |
251 |
252 |
253 |
254 |
262 | progress +
263 | ((100 / tasks.length) * Number(task.progress)) / 100,
264 | 0
265 | )
266 | : 0
267 | }
268 | />
269 |
270 |
271 |
276 | {t("Add New Task")}
277 |
278 |
279 |
285 | {t("Delete All")}
286 |
287 |
288 |
289 |
298 |
306 |
307 |
308 | {$tasks && $tasks.length ? (
309 |
310 | {$tasks.map((task) => (
311 |
312 |
317 |
318 | ))}
319 |
320 | ) : (
321 |
322 |
323 | {t("No available tasks.")}
324 |
325 |
326 | )}
327 |
328 |
333 |
334 |
335 |
336 |
341 |
342 |
349 |
350 |
351 | );
352 | }
353 |
354 | ProjectView.propTypes = {
355 | redirectToProjectList: PropTypes.func,
356 | match: PropTypes.shape({
357 | params: PropTypes.object,
358 | }),
359 | };
360 |
361 | export default ProjectView;
362 |
--------------------------------------------------------------------------------
/src/TinyManager/containers/Projects/TaskForm.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 | import { useFormik } from "formik";
4 |
5 | import TaskForm from "TinyManager/components/TaskForm";
6 | import { TaskType } from "TinyManager/types";
7 | import { useTranslation } from "TinyManager/providers/TranslationProvider";
8 |
9 | function TaskFormContainer(props) {
10 | const { onCancel, onSubmit, initialValues } = props;
11 | const { t } = useTranslation();
12 |
13 | const formik = useFormik({
14 | initialValues: {
15 | title: "",
16 | note: "",
17 | priority: 0,
18 | progress: 0,
19 | ...initialValues,
20 | },
21 | validate: (values) => {
22 | const errors = {};
23 |
24 | if (!values.title) {
25 | errors.title = t("Title is required.");
26 | }
27 |
28 | return errors;
29 | },
30 | onSubmit: (values) => {
31 | onSubmit(values);
32 | },
33 | enableReinitialize: true,
34 | });
35 |
36 | const isEdit = initialValues && initialValues.id;
37 | const formTitle = isEdit ? t("Edit Task") : t("New Task");
38 |
39 | return (
40 |
50 | );
51 | }
52 |
53 | TaskFormContainer.propTypes = {
54 | initialValues: TaskType,
55 | onCancel: PropTypes.func,
56 | onSubmit: PropTypes.func,
57 | };
58 |
59 | export default TaskFormContainer;
60 |
--------------------------------------------------------------------------------
/src/TinyManager/containers/Projects/index.js:
--------------------------------------------------------------------------------
1 | import React, { useCallback } from "react";
2 | import PropTypes from "prop-types";
3 | import { Route, Switch } from "react-router-dom";
4 | import { makeStyles } from "@material-ui/core/styles";
5 |
6 | import { join, withProps } from "TinyManager/services/Utils";
7 | import TinyManagerAPI from "TinyManager/services/TinyManagerAPI";
8 | import ProjectView from "TinyManager/containers/Projects/ProjectView";
9 | import ProjectList from "TinyManager/containers/Projects/ProjectList";
10 | import ProjectForm from "TinyManager/containers/Projects/ProjectForm";
11 |
12 | const useStyles = makeStyles((theme) => ({
13 | container: {
14 | flex: 1,
15 | display: "flex",
16 | justifyContent: "center",
17 | },
18 | content: {
19 | flex: 1,
20 | display: "flex",
21 | padding: theme.spacing(3.5),
22 | },
23 | }));
24 |
25 | function Projects(props) {
26 | const { match, history } = props;
27 | const { path, url } = match;
28 |
29 | const classes = useStyles();
30 |
31 | const redirect = useCallback(
32 | (url) => {
33 | history.push(url);
34 | },
35 | [history]
36 | );
37 |
38 | const redirectToNewProject = useCallback(() => {
39 | redirect(join(url, "/new"));
40 | }, [redirect, url]);
41 |
42 | const redirectToProjectView = useCallback(
43 | (projectId) => {
44 | redirect(join(url, "/", projectId));
45 | },
46 | [url, redirect]
47 | );
48 |
49 | const redirectToProjectList = useCallback(() => {
50 | redirect(url);
51 | }, [redirect, url]);
52 |
53 | const handleAddNewProject = useCallback(
54 | (values) => {
55 | TinyManagerAPI.addProject(values).then(redirectToProjectList);
56 | },
57 | [redirectToProjectList]
58 | );
59 |
60 | return (
61 |
62 |
63 |
64 |
71 |
77 |
84 |
85 |
86 |
87 | );
88 | }
89 |
90 | Projects.propTypes = {
91 | history: PropTypes.object,
92 | match: PropTypes.shape({
93 | path: PropTypes.string,
94 | url: PropTypes.string,
95 | }),
96 | };
97 |
98 | export default Projects;
99 |
--------------------------------------------------------------------------------
/src/TinyManager/containers/Todos/TodoFormDialog.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 | import { Dialog, DialogContent } from "@material-ui/core";
4 |
5 | import TodoForm from "TinyManager/components/TodoForm";
6 | import { TodoType } from "TinyManager/types";
7 | import { merge } from "TinyManager/services/Utils";
8 | import { useTranslation } from "TinyManager/providers/TranslationProvider";
9 | import { useFormik } from "formik";
10 |
11 | function TodoFormDialog(props) {
12 | const { initialValue = {}, open, disabled, onClose, onSubmit } = props;
13 | const { t } = useTranslation();
14 |
15 | const { values, errors, handleChange, handleSubmit } = useFormik({
16 | initialValues: merge({ title: "" }, initialValue),
17 | validate: (values) => {
18 | const errors = {};
19 |
20 | if (!values.title) {
21 | errors.title = t("Title is required.");
22 | }
23 |
24 | return errors;
25 | },
26 | onSubmit: (values) => {
27 | onSubmit(values);
28 | },
29 | enableReinitialize: true,
30 | });
31 |
32 | const isEdit = values && values.id;
33 | const formTitle = isEdit ? t("Edit Todo") : t("Add Todo");
34 |
35 | return (
36 |
37 |
38 |
48 |
49 |
50 | );
51 | }
52 |
53 | TodoFormDialog.propTypes = {
54 | initialValue: TodoType,
55 | open: PropTypes.bool,
56 | disabled: PropTypes.bool,
57 | onClose: PropTypes.func,
58 | onSubmit: PropTypes.func,
59 | };
60 |
61 | export default TodoFormDialog;
62 |
--------------------------------------------------------------------------------
/src/TinyManager/containers/Todos/index.js:
--------------------------------------------------------------------------------
1 | import React, { useCallback, useEffect, useMemo, useState } from "react";
2 | import PropTypes from "prop-types";
3 | import { makeStyles } from "@material-ui/core/styles";
4 | import Box from "@material-ui/core/Box";
5 | import Divider from "@material-ui/core/Divider";
6 | import Button from "@material-ui/core/Button";
7 |
8 | import TodoFormDialog from "./TodoFormDialog";
9 | import Loader from "TinyManager/components/Loader";
10 | import TodoList from "TinyManager/components/TodoList";
11 | import TinyManagerAPI from "TinyManager/services/TinyManagerAPI";
12 | import { useTranslation } from "TinyManager/providers/TranslationProvider";
13 | import useDialog from "TinyManager/hooks/useDialog";
14 | import ConfirmationDialog from "TinyManager/components/ConfirmationDialog/ConfirmationDialog";
15 | import OutlinedSelect from "TinyManager/components/OutlinedSelect";
16 |
17 | const useStyles = makeStyles((theme) => ({
18 | container: {
19 | flex: 1,
20 | overflow: "hidden",
21 | width: "100%",
22 | display: "flex",
23 | padding: theme.spacing(1),
24 | },
25 | todosContainer: {
26 | flex: 1,
27 | height: "100%",
28 | textAlign: "right",
29 | },
30 | todosFilter: {
31 | marginRight: theme.spacing(),
32 | },
33 | }));
34 |
35 | export const FILTERS = {
36 | Completed: "completed",
37 | Pending: "pending",
38 | All: "all",
39 | };
40 |
41 | function Todos() {
42 | const [{ todos, todo, loading, saving, edit }, setStore] = useState({
43 | todos: [],
44 | todo: { title: "" },
45 | loading: false,
46 | saving: false,
47 | edit: false,
48 | });
49 |
50 | const [
51 | deleteAllDialog,
52 | openDeleteAllDialog,
53 | closeDeleteAllDialog,
54 | ] = useDialog();
55 |
56 | const [filter, setFilter] = useState(FILTERS.All);
57 | const { t } = useTranslation();
58 | const classes = useStyles();
59 |
60 | const filterByMenu = useMemo(
61 | () =>
62 | Object.keys(FILTERS).map((key) => ({
63 | label: t(key),
64 | value: FILTERS[key],
65 | })),
66 | [t]
67 | );
68 |
69 | const filterTodos = useCallback(
70 | (todos = []) => {
71 | switch (filter) {
72 | case FILTERS.Completed:
73 | return todos.filter((t) => t.completed);
74 | case FILTERS.Pending:
75 | return todos.filter((t) => !t.completed);
76 | case FILTERS.All:
77 | default:
78 | return todos;
79 | }
80 | },
81 | [filter]
82 | );
83 |
84 | const fetchTodos = useCallback(() => {
85 | setStore((store) => ({ ...store, loading: true }));
86 |
87 | TinyManagerAPI.fetchTodos().then((todos) => {
88 | setStore((store) => ({
89 | ...store,
90 | todos: filterTodos(todos),
91 | loading: false,
92 | }));
93 | });
94 | }, [filterTodos]);
95 |
96 | const handleOpenDialog = useCallback((event, todo = { title: "" }) => {
97 | setStore((store) => ({ ...store, edit: true, todo: { ...todo } }));
98 | }, []);
99 |
100 | const handleCloseDialog = useCallback(() => {
101 | setStore((store) => ({
102 | ...store,
103 | edit: false,
104 | todo: { title: "" },
105 | errors: {},
106 | }));
107 | }, []);
108 |
109 | const handleAddNewTodo = useCallback(
110 | (todo) => {
111 | TinyManagerAPI.addTodo(todo)
112 | .then((todo) => {
113 | setStore((store) => ({
114 | ...store,
115 | saving: false,
116 | todos: [todo, ...store.todos],
117 | }));
118 | handleCloseDialog();
119 | })
120 | .catch(() => {
121 | setStore((store) => ({ ...store, saving: false }));
122 | alert("Error adding todo");
123 | });
124 | },
125 | [handleCloseDialog]
126 | );
127 |
128 | const handleEditTodo = useCallback(
129 | (todo) => {
130 | TinyManagerAPI.updateTodo(todo)
131 | .then((todo) => {
132 | setStore((store) => {
133 | return {
134 | ...store,
135 | saving: false,
136 | todos: store.todos.map((t) => (t.id === todo.id ? todo : t)),
137 | };
138 | });
139 | handleCloseDialog();
140 | })
141 | .catch(() => {
142 | setStore((store) => ({ ...store, saving: false }));
143 | alert("Error editing todo");
144 | });
145 | },
146 | [handleCloseDialog]
147 | );
148 |
149 | const handleSubmit = useCallback(
150 | (todo) => {
151 | setStore((store) => ({ ...store, saving: true }));
152 | const handle = todo.id ? handleEditTodo : handleAddNewTodo;
153 | handle(todo);
154 | },
155 | [handleAddNewTodo, handleEditTodo]
156 | );
157 |
158 | const handleTodoCheck = useCallback((e, todo) => {
159 | if (todo && todo.id) {
160 | TinyManagerAPI.updateTodo(
161 | Object.assign({}, todo, { completed: !todo.completed })
162 | ).then(() => {
163 | setStore((store) => ({
164 | ...store,
165 | todos: store.todos.map((t) =>
166 | t.id === todo.id ? { ...t, completed: !t.completed } : t
167 | ),
168 | }));
169 | });
170 | }
171 | }, []);
172 |
173 | const handleTodoDelete = useCallback((e, todo) => {
174 | if (todo && todo.id) {
175 | TinyManagerAPI.removeTodo(todo).then(() => {
176 | setStore((store) => ({
177 | ...store,
178 | todos: store.todos.filter((t) => t.id !== todo.id),
179 | }));
180 | });
181 | }
182 | }, []);
183 |
184 | const handleDeleteAllTodos = useCallback(() => {
185 | if (todos.length) {
186 | TinyManagerAPI.removeBulkTodos(todos.map(({ id }) => id)).then(() => {
187 | setStore((store) => ({ ...store, todos: [] }));
188 | });
189 | }
190 | closeDeleteAllDialog();
191 | }, [todos, closeDeleteAllDialog]);
192 |
193 | const handleFilterChange = useCallback((event) => {
194 | setFilter(event.target.value);
195 | }, []);
196 |
197 | useEffect(() => {
198 | fetchTodos();
199 | }, [fetchTodos]);
200 |
201 | return (
202 |
203 |
204 | {loading ? (
205 |
206 | ) : (
207 |
208 |
215 |
224 |
225 |
231 | {t("Delete All")}
232 |
233 |
234 |
239 | {t("Add")}
240 |
241 |
242 |
243 |
244 |
251 |
252 | )}
253 | {edit && (
254 |
261 | )}
262 |
270 |
271 | );
272 | }
273 |
274 | Todos.propTypes = {
275 | className: PropTypes.string,
276 | };
277 |
278 | export default Todos;
279 |
--------------------------------------------------------------------------------
/src/TinyManager/containers/Topbar/index.js:
--------------------------------------------------------------------------------
1 | import React, { useCallback, useMemo } from "react";
2 |
3 | import Topbar from "TinyManager/components/Topbar";
4 | import { useTheme } from "TinyManager/providers/ThemeProvider";
5 | import { useTranslation } from "TinyManager/providers/TranslationProvider";
6 |
7 | function TopbarContainer() {
8 | const { toggleDarkMode } = useTheme();
9 | const { t, i18n } = useTranslation();
10 |
11 | const handleLanguageChange = useCallback(
12 | (event, lang) => {
13 | i18n.changeLanguage(lang);
14 | },
15 | [i18n]
16 | );
17 |
18 | const languages = useMemo(
19 | () => [
20 | { label: "English", value: "en" },
21 | { label: "Français", value: "fr" },
22 | { label: "Español", value: "es" },
23 | { label: "Pусский", value: "ru" },
24 | { label: "Deutsch", value: "de" },
25 | { label: "हिंदी", value: "in" },
26 | { label: "日本語", value: "jp" },
27 | { label: "中文", value: "cn" },
28 | ],
29 | []
30 | );
31 |
32 | return (
33 |
39 | );
40 | }
41 |
42 | export default TopbarContainer;
43 |
--------------------------------------------------------------------------------
/src/TinyManager/decorators/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | export function wrapper(Story, width = 350) {
4 | return (
5 |
6 |
7 |
8 | );
9 | }
10 |
11 | export function withWrapper(Story) {
12 | return wrapper(Story);
13 | }
14 |
15 | export function withLargeWrapper(Story) {
16 | return wrapper(Story, 500);
17 | }
18 |
--------------------------------------------------------------------------------
/src/TinyManager/hooks/useDialog.js:
--------------------------------------------------------------------------------
1 | import { useCallback, useMemo, useState } from "react";
2 |
3 | function useDialog() {
4 | const [open, setOpen] = useState(false);
5 |
6 | const openDialog = useCallback(() => {
7 | setOpen(true);
8 | }, []);
9 |
10 | const closeDialog = useCallback(() => {
11 | setOpen(false);
12 | }, []);
13 |
14 | const toggleDialog = useCallback(() => {
15 | setOpen((open) => !open);
16 | }, []);
17 |
18 | const value = useMemo(() => [open, openDialog, closeDialog, toggleDialog], [
19 | open,
20 | openDialog,
21 | closeDialog,
22 | toggleDialog,
23 | ]);
24 |
25 | return value;
26 | }
27 |
28 | export default useDialog;
29 |
--------------------------------------------------------------------------------
/src/TinyManager/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { HashRouter } from "react-router-dom";
3 |
4 | import Content from "TinyManager/containers/Content";
5 | import ThemeProvider from "TinyManager/providers/ThemeProvider";
6 | import TranslationProvider from "TinyManager/providers/TranslationProvider";
7 | import SnackbarProvider from "TinyManager/providers/SnackbarProvider";
8 |
9 | function TinyManager() {
10 | return (
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | );
21 | }
22 |
23 | export default TinyManager;
24 |
--------------------------------------------------------------------------------
/src/TinyManager/providers/SnackbarProvider.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 | import { SnackbarProvider as NotistackSnackbarProvider } from "notistack";
4 |
5 | function SnackbarProvider(props) {
6 | const { children } = props;
7 | return (
8 |
9 | {children}
10 |
11 | );
12 | }
13 |
14 | SnackbarProvider.propTypes = {
15 | children: PropTypes.element,
16 | };
17 |
18 | export default SnackbarProvider;
19 |
20 | export { useSnackbar } from "notistack";
21 |
--------------------------------------------------------------------------------
/src/TinyManager/providers/ThemeProvider.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 | import { CssBaseline } from "@material-ui/core";
4 | import { MuiThemeProvider, createMuiTheme } from "@material-ui/core/styles";
5 | import {
6 | indigo,
7 | orange,
8 | grey,
9 | red,
10 | deepOrange,
11 | } from "@material-ui/core/colors";
12 |
13 | import TinyManagerAPI from "TinyManager/services/TinyManagerAPI";
14 |
15 | const ThemeContext = React.createContext();
16 |
17 | function ThemeProvider(props) {
18 | const { children, darkMode: darkModeProp = false } = props;
19 |
20 | const [darkMode, setDarkMode] = React.useState(
21 | darkModeProp || TinyManagerAPI.fetchDarkMode()
22 | );
23 |
24 | const toggleDarkMode = React.useCallback(() => {
25 | setDarkMode((darkMode) => {
26 | TinyManagerAPI.updateDarkMode(!darkMode);
27 | return !darkMode;
28 | });
29 | }, []);
30 |
31 | const theme = createMuiTheme({
32 | palette: {
33 | type: darkMode ? "dark" : "light",
34 | primary: darkMode ? orange : indigo,
35 | secondary: grey,
36 | error: darkMode ? deepOrange : red,
37 | },
38 | typography: {
39 | fontFamily: "'Fira Sans', sans-serif",
40 | },
41 | props: {
42 | MuiTextField: {
43 | variant: "outlined",
44 | },
45 | MuiButton: {
46 | size: "small",
47 | },
48 | },
49 | overrides: {
50 | MuiButton: { root: { textTransform: "none" } },
51 | },
52 | custom: {
53 | shadow: ["0px 0px 30px rgba(0,0,0,0.2)"],
54 | },
55 | });
56 |
57 | return (
58 |
59 |
60 |
61 | {children}
62 |
63 |
64 | );
65 | }
66 |
67 | export const useTheme = () => React.useContext(ThemeContext);
68 |
69 | ThemeProvider.propTypes = {
70 | /**
71 | * Children of theme provider ( element )
72 | */
73 | children: PropTypes.element,
74 | /**
75 | * Darkmode theme type boolean
76 | */
77 | darkMode: PropTypes.bool,
78 | };
79 |
80 | export default ThemeProvider;
81 |
--------------------------------------------------------------------------------
/src/TinyManager/providers/TranslationProvider.js:
--------------------------------------------------------------------------------
1 | import React, { createContext, useMemo } from "react";
2 | import i18n from "i18next";
3 | import { initReactI18next } from "react-i18next";
4 | import PropTypes from "prop-types";
5 |
6 | import cn from "TinyManager/assets/locales/cn/translations.json";
7 | import de from "TinyManager/assets/locales/de/translations.json";
8 | import en from "TinyManager/assets/locales/en/translations.json";
9 | import es from "TinyManager/assets/locales/es/translations.json";
10 | import fr from "TinyManager/assets/locales/fr/translations.json";
11 | import ind from "TinyManager/assets/locales/in/translations.json";
12 | import ru from "TinyManager/assets/locales/ru/translations.json";
13 | import jp from "TinyManager/assets/locales/jp/translations.json";
14 |
15 | const TranslationContext = createContext({});
16 |
17 | const resources = {
18 | cn: { translation: cn },
19 | de: { translation: de },
20 | en: { translation: en },
21 | es: { translation: es },
22 | fr: { translation: fr },
23 | in: { translation: ind },
24 | jp: { translation: jp },
25 | ru: { translation: ru },
26 | };
27 |
28 | i18n.use(initReactI18next).init({
29 | resources,
30 | lng: "en",
31 | fallbackLng: "en",
32 | keySeparator: false,
33 | interpolation: {
34 | escapeValue: false,
35 | },
36 | });
37 |
38 | function TranslationProvider(props) {
39 | const { children } = props;
40 |
41 | const value = useMemo(() => ({}), []);
42 | return (
43 |
44 | {children}
45 |
46 | );
47 | }
48 |
49 | TranslationProvider.propTypes = {
50 | children: PropTypes.element,
51 | };
52 |
53 | export default TranslationProvider;
54 |
55 | export { useTranslation, Trans as Translate } from "react-i18next";
56 |
--------------------------------------------------------------------------------
/src/TinyManager/services/DbService.js:
--------------------------------------------------------------------------------
1 | import Dexie from "dexie";
2 |
3 | const db = new Dexie("TinyManager");
4 |
5 | function initateDB() {
6 | db.version(1).stores({
7 | projects: "++id,name",
8 | });
9 |
10 | db.version(1).stores({
11 | todos: "++id,title,completed",
12 | });
13 |
14 | db.version(1).stores({
15 | tasks: "++id,title,projectId",
16 | });
17 |
18 | db.projects.defineClass({
19 | name: String,
20 | description: String,
21 | createdAt: Date,
22 | updatedAt: Date,
23 | });
24 |
25 | db.todos.defineClass({
26 | title: String,
27 | completed: Boolean,
28 | });
29 |
30 | db.tasks.defineClass({
31 | title: String,
32 | note: String,
33 | priority: Number,
34 | progress: Number,
35 | projectId: Number,
36 | createdAt: Date,
37 | updatedAt: Date,
38 | });
39 | }
40 |
41 | function getTable(tableName) {
42 | const table = db.table(tableName);
43 | return {
44 | name: table.name,
45 | };
46 | }
47 |
48 | async function find(tableName, query = {}) {
49 | const { where } = query;
50 |
51 | let table = db[tableName];
52 |
53 | if (where) {
54 | table = table.where(where);
55 | }
56 |
57 | return table.reverse().toArray();
58 | }
59 |
60 | function findOne(tableName, query) {
61 | return db[tableName].get(query);
62 | }
63 |
64 | function insert(tableName, record) {
65 | record = { ...record, createdAt: new Date(), updatedAt: new Date() };
66 | return db[tableName]
67 | .add(record)
68 | .then((recordId) => findOne(tableName, recordId));
69 | }
70 |
71 | function update(tableName, record) {
72 | record = { ...record, updatedAt: new Date() };
73 | return db[tableName].update(record.id, record).then((updateCount) => {
74 | if (updateCount) {
75 | return findOne(tableName, record.id);
76 | } else {
77 | return new Error("Error updating record");
78 | }
79 | });
80 | }
81 |
82 | function remove(tableName, record) {
83 | return db[tableName].delete(record.id);
84 | }
85 |
86 | function removeBulk(tableName, ids) {
87 | return db[tableName].bulkDelete(ids);
88 | }
89 |
90 | initateDB();
91 |
92 | export default {
93 | find,
94 | findOne,
95 | insert,
96 | update,
97 | remove,
98 | removeBulk,
99 | getTable,
100 | };
101 |
--------------------------------------------------------------------------------
/src/TinyManager/services/DbService.spec.js:
--------------------------------------------------------------------------------
1 | import DbService from "./DbService";
2 |
3 | describe("DbService", () => {
4 | it("must be defined", () => {
5 | expect(DbService).toBeDefined();
6 | expect(DbService).toBeTruthy();
7 | });
8 |
9 | describe("getTable", () => {
10 | const { getTable } = DbService;
11 |
12 | it("must be defined", () => {
13 | expect(getTable).toBeDefined();
14 | });
15 |
16 | it("must return table", () => {
17 | const table = getTable("projects");
18 | expect(table).toBeDefined();
19 | expect(table.name).toBe("projects");
20 | });
21 | });
22 |
23 | describe("find", () => {
24 | const { find } = DbService;
25 |
26 | it("must be defined", () => {
27 | expect(find).toBeDefined();
28 | });
29 | });
30 |
31 | describe("findOne", () => {
32 | const { findOne } = DbService;
33 |
34 | it("must be defined", () => {
35 | expect(findOne).toBeDefined();
36 | });
37 | });
38 |
39 | describe("insert", () => {
40 | const { insert } = DbService;
41 |
42 | it("must be defined", () => {
43 | expect(insert).toBeDefined();
44 | });
45 | });
46 |
47 | describe("update", () => {
48 | const { update } = DbService;
49 |
50 | it("must be defined", () => {
51 | expect(update).toBeDefined();
52 | });
53 | });
54 |
55 | describe("remove", () => {
56 | const { remove } = DbService;
57 |
58 | it("must be defined", () => {
59 | expect(remove).toBeDefined();
60 | });
61 | });
62 |
63 | describe("DB", () => {
64 | const { getTable } = DbService;
65 |
66 | it("must be initialized", () => {
67 | const projectsTable = getTable("projects");
68 |
69 | expect(projectsTable).toBeDefined();
70 | expect(projectsTable.name).toEqual("projects");
71 |
72 | const todosTable = getTable("todos");
73 |
74 | expect(todosTable).toBeDefined();
75 | expect(todosTable.name).toEqual("todos");
76 |
77 | const tasksTable = getTable("tasks");
78 |
79 | expect(tasksTable).toBeDefined();
80 | expect(tasksTable.name).toEqual("tasks");
81 |
82 | const updatesTable = getTable("updates");
83 |
84 | expect(updatesTable).toBeDefined();
85 | expect(updatesTable.name).toEqual("updates");
86 | });
87 | });
88 | });
89 |
--------------------------------------------------------------------------------
/src/TinyManager/services/LocalStorageService.js:
--------------------------------------------------------------------------------
1 | const DEFAULT_NOTES = "dn";
2 | const DARK_MODE = "dm";
3 | const NOTES = "ns";
4 |
5 | function setItem(key, value, service) {
6 | service.setItem(key, JSON.stringify(value));
7 | }
8 |
9 | function getItem(key, service) {
10 | try {
11 | return JSON.parse(service.getItem(key));
12 | } catch (err) {
13 | return;
14 | }
15 | }
16 |
17 | function setDefaultNotes(value, service = localStorage) {
18 | setItem(DEFAULT_NOTES, value, service);
19 | }
20 |
21 | function getDefaultNotes(service = localStorage) {
22 | return getItem(DEFAULT_NOTES, service);
23 | }
24 |
25 | function setDarkMode(value, service = localStorage) {
26 | setItem(DARK_MODE, value, service);
27 | }
28 |
29 | function getDarkMode(service = localStorage) {
30 | return getItem(DARK_MODE, service);
31 | }
32 |
33 | function setNotes(value, service = localStorage) {
34 | setItem(NOTES, value, service);
35 | }
36 |
37 | function getNotes(service = localStorage) {
38 | return getItem(NOTES, service);
39 | }
40 |
41 | export default {
42 | setDefaultNotes,
43 | getDefaultNotes,
44 | setDarkMode,
45 | getDarkMode,
46 | setNotes,
47 | getNotes,
48 | DEFAULT_NOTES,
49 | DARK_MODE,
50 | NOTES,
51 | };
52 |
--------------------------------------------------------------------------------
/src/TinyManager/services/LocalStorageService.spec.js:
--------------------------------------------------------------------------------
1 | import LocalStorageService from "./LocalStorageService";
2 |
3 | describe("LocalStorageService", () => {
4 | const getItem = jest.fn();
5 | const setItem = jest.fn();
6 |
7 | const service = {
8 | getItem,
9 | setItem,
10 | };
11 |
12 | beforeEach(() => {
13 | getItem.mockClear();
14 | setItem.mockClear();
15 | });
16 |
17 | it("must be defined", () => {
18 | expect(LocalStorageService).toBeDefined();
19 | });
20 |
21 | describe("DEFAULT_NOTES", () => {
22 | const { DEFAULT_NOTES } = LocalStorageService;
23 |
24 | it("must be defined", () => {
25 | expect(DEFAULT_NOTES).toBeDefined();
26 | });
27 | });
28 |
29 | describe("setDefaultNotes", () => {
30 | const { setDefaultNotes, DEFAULT_NOTES } = LocalStorageService;
31 |
32 | it("must be defined", () => {
33 | expect(setDefaultNotes).toBeDefined();
34 | });
35 |
36 | it("must set local storage DEFAULT_NOTES value", () => {
37 | setDefaultNotes(true, service);
38 | expect(service.setItem).toHaveBeenCalledTimes(1);
39 | expect(service.setItem).toHaveBeenCalledWith(DEFAULT_NOTES, "true");
40 | });
41 | });
42 |
43 | describe("getDefaultNotes", () => {
44 | const { getDefaultNotes } = LocalStorageService;
45 |
46 | it("must be defined", () => {
47 | expect(getDefaultNotes).toBeDefined();
48 | });
49 |
50 | it("must get local storage DEFAULT_NOTES value", () => {
51 | service.getItem.mockImplementation(() => true);
52 |
53 | const defaultNotes = getDefaultNotes(service);
54 | expect(service.getItem).toHaveBeenCalledTimes(1);
55 | expect(defaultNotes).toBeTruthy();
56 | });
57 | });
58 |
59 | describe("setDarkMode", () => {
60 | const { setDarkMode, DARK_MODE } = LocalStorageService;
61 |
62 | it("must be defined", () => {
63 | expect(setDarkMode).toBeDefined();
64 | });
65 |
66 | it("must set local storage DARK_MODE value", () => {
67 | setDarkMode(true, service);
68 | expect(service.setItem).toHaveBeenCalledTimes(1);
69 | expect(service.setItem).toHaveBeenCalledWith(DARK_MODE, "true");
70 | });
71 | });
72 |
73 | describe("getDarkMode", () => {
74 | const { getDarkMode } = LocalStorageService;
75 |
76 | it("must be defined", () => {
77 | expect(getDarkMode).toBeDefined();
78 | });
79 |
80 | it("must get local storage DARK_MODE value", () => {
81 | service.getItem.mockImplementation(() => true);
82 |
83 | const darkMode = getDarkMode(service);
84 | expect(service.getItem).toHaveBeenCalledTimes(1);
85 | expect(darkMode).toBeTruthy();
86 | });
87 | });
88 |
89 | describe("setNotes", () => {
90 | const { setNotes, NOTES } = LocalStorageService;
91 |
92 | it("must be defined", () => {
93 | expect(setNotes).toBeDefined();
94 | });
95 |
96 | it("must set local storage NOTES value", () => {
97 | setNotes("test-note", service);
98 | expect(service.setItem).toHaveBeenCalledTimes(1);
99 | expect(service.setItem).toHaveBeenCalledWith(NOTES, '"test-note"');
100 | });
101 | });
102 |
103 | describe("getNotes", () => {
104 | const { getNotes } = LocalStorageService;
105 |
106 | it("must be defined", () => {
107 | expect(getNotes).toBeDefined();
108 | });
109 |
110 | it("must get local storage NOTES value", () => {
111 | service.getItem.mockImplementation(() => JSON.stringify("test-note"));
112 |
113 | const notes = getNotes(service);
114 | expect(service.getItem).toHaveBeenCalledTimes(1);
115 | expect(notes).toBe("test-note");
116 | });
117 | });
118 | });
119 |
--------------------------------------------------------------------------------
/src/TinyManager/services/QuoteService.js:
--------------------------------------------------------------------------------
1 | import quotes from "TinyManager/assets/quotes";
2 |
3 | const count = quotes.length;
4 |
5 | function getRandomInt(max) {
6 | return Math.floor(Math.random() * Math.floor(max));
7 | }
8 |
9 | function getQuote() {
10 | const randomNumber = getRandomInt(count - 1);
11 | return quotes[randomNumber];
12 | }
13 |
14 | export default {
15 | getQuote,
16 | };
17 |
--------------------------------------------------------------------------------
/src/TinyManager/services/QuoteService.spec.js:
--------------------------------------------------------------------------------
1 | import QuoteService from "./QuoteService";
2 |
3 | describe("QuoteService", () => {
4 | describe("getQuote", () => {
5 | const { getQuote } = QuoteService;
6 |
7 | it("must be defined", () => {
8 | expect(getQuote).toBeDefined();
9 | });
10 |
11 | it("must return a quote", () => {
12 | const quote = getQuote();
13 | expect(quote).toBeDefined();
14 | });
15 | });
16 | });
17 |
--------------------------------------------------------------------------------
/src/TinyManager/services/TinyManagerAPI.js:
--------------------------------------------------------------------------------
1 | import DbService from "TinyManager/services/DbService";
2 | import LocalStorageService from "TinyManager/services/LocalStorageService";
3 |
4 | function fetchProjects() {
5 | return DbService.find("projects");
6 | }
7 |
8 | function fetchProject(projectId) {
9 | return DbService.findOne("projects", projectId);
10 | }
11 |
12 | function fetchTasks(projectId) {
13 | return DbService.find("tasks", { where: { projectId } });
14 | }
15 |
16 | function fetchTask(taskId) {
17 | return DbService.findOne("tasks", taskId);
18 | }
19 |
20 | function addProject(project) {
21 | return DbService.insert("projects", project);
22 | }
23 |
24 | function addTask(task) {
25 | return DbService.insert("tasks", task);
26 | }
27 |
28 | function updateProject(projectId, project) {
29 | return DbService.update("projects", project);
30 | }
31 |
32 | function updateTask(taskId, task) {
33 | return DbService.update("tasks", task);
34 | }
35 |
36 | function removeProject(projectId) {
37 | return DbService.remove("projects", { id: projectId });
38 | }
39 |
40 | function removeBulkProjects(projectIds) {
41 | return DbService.removeBulk("projects", projectIds);
42 | }
43 |
44 | function removeTask(taskId) {
45 | return DbService.remove("tasks", { id: taskId });
46 | }
47 |
48 | function removeBulkTasks(taskIds) {
49 | return DbService.removeBulk("tasks", taskIds);
50 | }
51 |
52 | function fetchTodos() {
53 | return DbService.find("todos");
54 | }
55 |
56 | function addTodo(todo) {
57 | return DbService.insert("todos", todo);
58 | }
59 |
60 | function updateTodo(todo) {
61 | return DbService.update("todos", todo);
62 | }
63 |
64 | function removeTodo(todo) {
65 | return DbService.remove("todos", todo);
66 | }
67 |
68 | function removeBulkTodos(todoIds) {
69 | return DbService.removeBulk("todos", todoIds);
70 | }
71 |
72 | function fetchNotes() {
73 | return LocalStorageService.getNotes() || "";
74 | }
75 |
76 | function updateNotes(notes) {
77 | return LocalStorageService.setNotes(notes);
78 | }
79 |
80 | function updateDefaultNotes(value) {
81 | LocalStorageService.setDefaultNotes(value);
82 | }
83 |
84 | function fetchDefaultNotes() {
85 | return LocalStorageService.getDefaultNotes();
86 | }
87 |
88 | function updateDarkMode(value) {
89 | LocalStorageService.setDarkMode(value);
90 | }
91 |
92 | function fetchDarkMode() {
93 | return LocalStorageService.getDarkMode();
94 | }
95 |
96 | export default {
97 | fetchProjects,
98 | fetchProject,
99 | fetchTasks,
100 | fetchTask,
101 | addProject,
102 | addTask,
103 | updateProject,
104 | updateTask,
105 | removeTodo,
106 | removeBulkTodos,
107 | removeProject,
108 | removeBulkProjects,
109 | removeTask,
110 | removeBulkTasks,
111 | fetchTodos,
112 | addTodo,
113 | updateTodo,
114 | fetchNotes,
115 | updateNotes,
116 | updateDefaultNotes,
117 | fetchDefaultNotes,
118 | updateDarkMode,
119 | fetchDarkMode,
120 | };
121 |
--------------------------------------------------------------------------------
/src/TinyManager/services/Utils.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import moment from "moment";
3 |
4 | function join(...strings) {
5 | return strings.join("");
6 | }
7 |
8 | function merge(...objects) {
9 | return objects.reduce(
10 | (accumulator, object) => ({ ...accumulator, ...object }),
11 | {}
12 | );
13 | }
14 |
15 | function noop() {
16 | /* No Action */
17 | }
18 |
19 | // eslint-disable-next-line no-unused-vars
20 | function withProps(Component, props) {
21 | // eslint-disable-next-line react/display-name
22 | return function (innerProps) {
23 | return ;
24 | };
25 | }
26 |
27 | function formatDate(date = new Date(), format = "DD-MM-YYYY") {
28 | return date && moment(date).format(format);
29 | }
30 |
31 | export { join, merge, noop, withProps, formatDate };
32 |
--------------------------------------------------------------------------------
/src/TinyManager/types/index.js:
--------------------------------------------------------------------------------
1 | import PropTypes from "prop-types";
2 |
3 | export const ProjectType = PropTypes.shape({
4 | id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
5 | name: PropTypes.string,
6 | description: PropTypes.string,
7 | });
8 |
9 | export const TaskType = PropTypes.shape({
10 | id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
11 | title: PropTypes.string,
12 | note: PropTypes.string,
13 | priority: PropTypes.number,
14 | progress: PropTypes.number,
15 | });
16 |
17 | export const TodoType = PropTypes.shape({
18 | id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
19 | title: PropTypes.string,
20 | completed: PropTypes.bool,
21 | });
22 |
23 | export default {
24 | ProjectType,
25 | TaskType,
26 | TodoType,
27 | };
28 |
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
4 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
5 | sans-serif;
6 | -webkit-font-smoothing: antialiased;
7 | -moz-osx-font-smoothing: grayscale;
8 | }
9 |
10 | #root {
11 | height: 100vh;
12 | width: 100vw;
13 | overflow: hidden;
14 | }
15 |
16 | a {
17 | color: inherit;
18 | text-decoration: none;
19 | }
20 |
21 | a:visited {
22 | color: inherit;
23 | }
24 |
25 | a[disabled],
26 | .disabled-link {
27 | pointer-events: none;
28 | }
29 |
30 | /* width */
31 | ::-webkit-scrollbar {
32 | width: 4px;
33 | }
34 |
35 | /* Track */
36 | ::-webkit-scrollbar-track {
37 | background: #f1f1f1;
38 | }
39 |
40 | /* Handle */
41 | ::-webkit-scrollbar-thumb {
42 | background: #888;
43 | border-radius: 20px;
44 | }
45 |
46 | /* Handle on hover */
47 | ::-webkit-scrollbar-thumb:hover {
48 | background: #555;
49 | }
50 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom";
3 |
4 | import TinyManager from "TinyManager";
5 | import * as serviceWorker from "serviceWorker";
6 |
7 | import "./index.css";
8 |
9 | ReactDOM.render(
10 |
11 |
12 | ,
13 | document.getElementById("root")
14 | );
15 |
16 | serviceWorker.register();
17 |
--------------------------------------------------------------------------------
/src/serviceWorker.js:
--------------------------------------------------------------------------------
1 | // This optional code is used to register a service worker.
2 | // register() is not called by default.
3 |
4 | // This lets the app load faster on subsequent visits in production, and gives
5 | // it offline capabilities. However, it also means that developers (and users)
6 | // will only see deployed updates on subsequent visits to a page, after all the
7 | // existing tabs open on the page have been closed, since previously cached
8 | // resources are updated in the background.
9 |
10 | // To learn more about the benefits of this model and instructions on how to
11 | // opt-in, read https://bit.ly/CRA-PWA
12 |
13 | const isLocalhost = Boolean(
14 | window.location.hostname === 'localhost' ||
15 | // [::1] is the IPv6 localhost address.
16 | window.location.hostname === '[::1]' ||
17 | // 127.0.0.0/8 are considered localhost for IPv4.
18 | window.location.hostname.match(
19 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
20 | )
21 | );
22 |
23 | export function register(config) {
24 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
25 | // The URL constructor is available in all browsers that support SW.
26 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);
27 | if (publicUrl.origin !== window.location.origin) {
28 | // Our service worker won't work if PUBLIC_URL is on a different origin
29 | // from what our page is served on. This might happen if a CDN is used to
30 | // serve assets; see https://github.com/facebook/create-react-app/issues/2374
31 | return;
32 | }
33 |
34 | window.addEventListener('load', () => {
35 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
36 |
37 | if (isLocalhost) {
38 | // This is running on localhost. Let's check if a service worker still exists or not.
39 | checkValidServiceWorker(swUrl, config);
40 |
41 | // Add some additional logging to localhost, pointing developers to the
42 | // service worker/PWA documentation.
43 | navigator.serviceWorker.ready.then(() => {
44 | console.log(
45 | 'This web app is being served cache-first by a service ' +
46 | 'worker. To learn more, visit https://bit.ly/CRA-PWA'
47 | );
48 | });
49 | } else {
50 | // Is not localhost. Just register service worker
51 | registerValidSW(swUrl, config);
52 | }
53 | });
54 | }
55 | }
56 |
57 | function registerValidSW(swUrl, config) {
58 | navigator.serviceWorker
59 | .register(swUrl)
60 | .then(registration => {
61 | registration.onupdatefound = () => {
62 | const installingWorker = registration.installing;
63 | if (installingWorker == null) {
64 | return;
65 | }
66 | installingWorker.onstatechange = () => {
67 | if (installingWorker.state === 'installed') {
68 | if (navigator.serviceWorker.controller) {
69 | // At this point, the updated precached content has been fetched,
70 | // but the previous service worker will still serve the older
71 | // content until all client tabs are closed.
72 | console.log(
73 | 'New content is available and will be used when all ' +
74 | 'tabs for this page are closed. See https://bit.ly/CRA-PWA.'
75 | );
76 |
77 | // Execute callback
78 | if (config && config.onUpdate) {
79 | config.onUpdate(registration);
80 | }
81 | } else {
82 | // At this point, everything has been precached.
83 | // It's the perfect time to display a
84 | // "Content is cached for offline use." message.
85 | console.log('Content is cached for offline use.');
86 |
87 | // Execute callback
88 | if (config && config.onSuccess) {
89 | config.onSuccess(registration);
90 | }
91 | }
92 | }
93 | };
94 | };
95 | })
96 | .catch(error => {
97 | console.error('Error during service worker registration:', error);
98 | });
99 | }
100 |
101 | function checkValidServiceWorker(swUrl, config) {
102 | // Check if the service worker can be found. If it can't reload the page.
103 | fetch(swUrl, {
104 | headers: { 'Service-Worker': 'script' },
105 | })
106 | .then(response => {
107 | // Ensure service worker exists, and that we really are getting a JS file.
108 | const contentType = response.headers.get('content-type');
109 | if (
110 | response.status === 404 ||
111 | (contentType != null && contentType.indexOf('javascript') === -1)
112 | ) {
113 | // No service worker found. Probably a different app. Reload the page.
114 | navigator.serviceWorker.ready.then(registration => {
115 | registration.unregister().then(() => {
116 | window.location.reload();
117 | });
118 | });
119 | } else {
120 | // Service worker found. Proceed as normal.
121 | registerValidSW(swUrl, config);
122 | }
123 | })
124 | .catch(() => {
125 | console.log(
126 | 'No internet connection found. App is running in offline mode.'
127 | );
128 | });
129 | }
130 |
131 | export function unregister() {
132 | if ('serviceWorker' in navigator) {
133 | navigator.serviceWorker.ready
134 | .then(registration => {
135 | registration.unregister();
136 | })
137 | .catch(error => {
138 | console.error(error.message);
139 | });
140 | }
141 | }
142 |
--------------------------------------------------------------------------------
/src/setupTests.js:
--------------------------------------------------------------------------------
1 | // jest-dom adds custom jest matchers for asserting on DOM nodes.
2 | // allows you to do things like:
3 | // expect(element).toHaveTextContent(/react/i)
4 | // learn more: https://github.com/testing-library/jest-dom
5 | import '@testing-library/jest-dom/extend-expect';
6 |
--------------------------------------------------------------------------------
/src/storybook.spec.js:
--------------------------------------------------------------------------------
1 | import path from "path";
2 | import initStoryshots from "@storybook/addon-storyshots";
3 |
4 | initStoryshots({
5 | configPath: path.resolve(__dirname, "../.storybook"),
6 | });
7 |
--------------------------------------------------------------------------------