add linter
This commit is contained in:
48
.eslintrc.json
Normal file
48
.eslintrc.json
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
{
|
||||||
|
"env": {
|
||||||
|
"browser": true,
|
||||||
|
"es2021": true
|
||||||
|
},
|
||||||
|
"extends": [
|
||||||
|
"eslint:recommended",
|
||||||
|
"plugin:@typescript-eslint/recommended",
|
||||||
|
"plugin:@typescript-eslint/stylistic",
|
||||||
|
"plugin:react/recommended",
|
||||||
|
"plugin:react-hooks/recommended",
|
||||||
|
"plugin:tailwindcss/recommended"
|
||||||
|
],
|
||||||
|
"parserOptions": {
|
||||||
|
"ecmaFeatures": {
|
||||||
|
"jsx": true
|
||||||
|
},
|
||||||
|
"ecmaVersion": 12,
|
||||||
|
"sourceType": "module"
|
||||||
|
},
|
||||||
|
"plugins": [
|
||||||
|
"react",
|
||||||
|
"react-hooks",
|
||||||
|
"tailwindcss"
|
||||||
|
],
|
||||||
|
"rules": {
|
||||||
|
"no-undef": ["error", {"typeof": true}],
|
||||||
|
"react-hooks/exhaustive-deps": "off",
|
||||||
|
"react/react-in-jsx-scope": "off",
|
||||||
|
"react/require-default-props": "off",
|
||||||
|
"semi": [2, "never"],
|
||||||
|
"tailwindcss/no-custom-classname": ["warn", {
|
||||||
|
"cssFiles": ["resources/css/app.css"],
|
||||||
|
"whitelist": []
|
||||||
|
}],
|
||||||
|
"tailwindcss/enforces-negative-arbitrary-values": "warn",
|
||||||
|
"tailwindcss/enforces-shorthand": "warn",
|
||||||
|
"tailwindcss/migration-from-tailwind-2": "warn",
|
||||||
|
// "tailwindcss/no-arbitrary-value": "off",
|
||||||
|
// "tailwindcss/no-arbitrary-value": "error",
|
||||||
|
"tailwindcss/no-contradicting-classname": "error"
|
||||||
|
},
|
||||||
|
"settings": {
|
||||||
|
"react": {
|
||||||
|
"version": "18.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -48,7 +48,7 @@ class ToDoController extends Controller
|
|||||||
$data['checked'] = $request->input('checked') ? now() : null;
|
$data['checked'] = $request->input('checked') ? now() : null;
|
||||||
$todo->update($data);
|
$todo->update($data);
|
||||||
|
|
||||||
if ($request->user()->currentTimeTracker->to_do_id === $todo->id) {
|
if ($request->user()->currentTimeTracker?->to_do_id === $todo->id) {
|
||||||
$request->user()->stopCurrentTimeTracker();
|
$request->user()->stopCurrentTimeTracker();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,8 @@ class ToDoRequest extends FormRequest
|
|||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'name' => ['string', 'min:3', 'max:255'],
|
'name' => ['string', 'min:3', 'max:255'],
|
||||||
'checked' => ['boolean'],
|
'description' => ['string', 'max:2000'],
|
||||||
|
'checked' => ['boolean', 'nullable'],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ class ToDoResource extends JsonResource
|
|||||||
'id' => $this->id,
|
'id' => $this->id,
|
||||||
'user_id' => $this->user_id,
|
'user_id' => $this->user_id,
|
||||||
'name' => $this->name,
|
'name' => $this->name,
|
||||||
|
'description' => $this->description,
|
||||||
'checked' => $this->checked,
|
'checked' => $this->checked,
|
||||||
'duration' => $this->duration,
|
'duration' => $this->duration,
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -3,11 +3,15 @@
|
|||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"build": "vite build"
|
"build": "vite build",
|
||||||
|
"lint": "./node_modules/.bin/eslint resources/js/ --ext .js,.jsx,ts,tsx",
|
||||||
|
"lint-fix": "eslint ./ --fix"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/react": "^18.2.48",
|
"@types/react": "^18.2.48",
|
||||||
"@types/react-dom": "^18.2.18",
|
"@types/react-dom": "^18.2.18",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^7.0.1",
|
||||||
|
"@typescript-eslint/parser": "^7.0.1",
|
||||||
"@vitejs/plugin-react": "^4.2.1",
|
"@vitejs/plugin-react": "^4.2.1",
|
||||||
"autoprefixer": "^10.4.17",
|
"autoprefixer": "^10.4.17",
|
||||||
"axios": "^1.6.4",
|
"axios": "^1.6.4",
|
||||||
|
|||||||
208
pnpm-lock.yaml
generated
208
pnpm-lock.yaml
generated
@@ -22,6 +22,12 @@ devDependencies:
|
|||||||
'@types/react-dom':
|
'@types/react-dom':
|
||||||
specifier: ^18.2.18
|
specifier: ^18.2.18
|
||||||
version: 18.2.18
|
version: 18.2.18
|
||||||
|
'@typescript-eslint/eslint-plugin':
|
||||||
|
specifier: ^7.0.1
|
||||||
|
version: 7.0.1(@typescript-eslint/parser@7.0.1)(eslint@8.56.0)(typescript@5.3.3)
|
||||||
|
'@typescript-eslint/parser':
|
||||||
|
specifier: ^7.0.1
|
||||||
|
version: 7.0.1(eslint@8.56.0)(typescript@5.3.3)
|
||||||
'@vitejs/plugin-react':
|
'@vitejs/plugin-react':
|
||||||
specifier: ^4.2.1
|
specifier: ^4.2.1
|
||||||
version: 4.2.1(vite@5.0.12)
|
version: 4.2.1(vite@5.0.12)
|
||||||
@@ -771,6 +777,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==}
|
resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/@types/json-schema@7.0.15:
|
||||||
|
resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/@types/prop-types@15.7.11:
|
/@types/prop-types@15.7.11:
|
||||||
resolution: {integrity: sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==}
|
resolution: {integrity: sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==}
|
||||||
dev: true
|
dev: true
|
||||||
@@ -793,6 +803,142 @@ packages:
|
|||||||
resolution: {integrity: sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==}
|
resolution: {integrity: sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/@types/semver@7.5.7:
|
||||||
|
resolution: {integrity: sha512-/wdoPq1QqkSj9/QOeKkFquEuPzQbHTWAMPH/PaUMB+JuR31lXhlWXRZ52IpfDYVlDOUBvX09uBrPwxGT1hjNBg==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/@typescript-eslint/eslint-plugin@7.0.1(@typescript-eslint/parser@7.0.1)(eslint@8.56.0)(typescript@5.3.3):
|
||||||
|
resolution: {integrity: sha512-OLvgeBv3vXlnnJGIAgCLYKjgMEU+wBGj07MQ/nxAaON+3mLzX7mJbhRYrVGiVvFiXtwFlkcBa/TtmglHy0UbzQ==}
|
||||||
|
engines: {node: ^16.0.0 || >=18.0.0}
|
||||||
|
peerDependencies:
|
||||||
|
'@typescript-eslint/parser': ^7.0.0
|
||||||
|
eslint: ^8.56.0
|
||||||
|
typescript: '*'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
typescript:
|
||||||
|
optional: true
|
||||||
|
dependencies:
|
||||||
|
'@eslint-community/regexpp': 4.10.0
|
||||||
|
'@typescript-eslint/parser': 7.0.1(eslint@8.56.0)(typescript@5.3.3)
|
||||||
|
'@typescript-eslint/scope-manager': 7.0.1
|
||||||
|
'@typescript-eslint/type-utils': 7.0.1(eslint@8.56.0)(typescript@5.3.3)
|
||||||
|
'@typescript-eslint/utils': 7.0.1(eslint@8.56.0)(typescript@5.3.3)
|
||||||
|
'@typescript-eslint/visitor-keys': 7.0.1
|
||||||
|
debug: 4.3.4
|
||||||
|
eslint: 8.56.0
|
||||||
|
graphemer: 1.4.0
|
||||||
|
ignore: 5.3.0
|
||||||
|
natural-compare: 1.4.0
|
||||||
|
semver: 7.6.0
|
||||||
|
ts-api-utils: 1.2.1(typescript@5.3.3)
|
||||||
|
typescript: 5.3.3
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/@typescript-eslint/parser@7.0.1(eslint@8.56.0)(typescript@5.3.3):
|
||||||
|
resolution: {integrity: sha512-8GcRRZNzaHxKzBPU3tKtFNing571/GwPBeCvmAUw0yBtfE2XVd0zFKJIMSWkHJcPQi0ekxjIts6L/rrZq5cxGQ==}
|
||||||
|
engines: {node: ^16.0.0 || >=18.0.0}
|
||||||
|
peerDependencies:
|
||||||
|
eslint: ^8.56.0
|
||||||
|
typescript: '*'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
typescript:
|
||||||
|
optional: true
|
||||||
|
dependencies:
|
||||||
|
'@typescript-eslint/scope-manager': 7.0.1
|
||||||
|
'@typescript-eslint/types': 7.0.1
|
||||||
|
'@typescript-eslint/typescript-estree': 7.0.1(typescript@5.3.3)
|
||||||
|
'@typescript-eslint/visitor-keys': 7.0.1
|
||||||
|
debug: 4.3.4
|
||||||
|
eslint: 8.56.0
|
||||||
|
typescript: 5.3.3
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/@typescript-eslint/scope-manager@7.0.1:
|
||||||
|
resolution: {integrity: sha512-v7/T7As10g3bcWOOPAcbnMDuvctHzCFYCG/8R4bK4iYzdFqsZTbXGln0cZNVcwQcwewsYU2BJLay8j0/4zOk4w==}
|
||||||
|
engines: {node: ^16.0.0 || >=18.0.0}
|
||||||
|
dependencies:
|
||||||
|
'@typescript-eslint/types': 7.0.1
|
||||||
|
'@typescript-eslint/visitor-keys': 7.0.1
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/@typescript-eslint/type-utils@7.0.1(eslint@8.56.0)(typescript@5.3.3):
|
||||||
|
resolution: {integrity: sha512-YtT9UcstTG5Yqy4xtLiClm1ZpM/pWVGFnkAa90UfdkkZsR1eP2mR/1jbHeYp8Ay1l1JHPyGvoUYR6o3On5Nhmw==}
|
||||||
|
engines: {node: ^16.0.0 || >=18.0.0}
|
||||||
|
peerDependencies:
|
||||||
|
eslint: ^8.56.0
|
||||||
|
typescript: '*'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
typescript:
|
||||||
|
optional: true
|
||||||
|
dependencies:
|
||||||
|
'@typescript-eslint/typescript-estree': 7.0.1(typescript@5.3.3)
|
||||||
|
'@typescript-eslint/utils': 7.0.1(eslint@8.56.0)(typescript@5.3.3)
|
||||||
|
debug: 4.3.4
|
||||||
|
eslint: 8.56.0
|
||||||
|
ts-api-utils: 1.2.1(typescript@5.3.3)
|
||||||
|
typescript: 5.3.3
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/@typescript-eslint/types@7.0.1:
|
||||||
|
resolution: {integrity: sha512-uJDfmirz4FHib6ENju/7cz9SdMSkeVvJDK3VcMFvf/hAShg8C74FW+06MaQPODHfDJp/z/zHfgawIJRjlu0RLg==}
|
||||||
|
engines: {node: ^16.0.0 || >=18.0.0}
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/@typescript-eslint/typescript-estree@7.0.1(typescript@5.3.3):
|
||||||
|
resolution: {integrity: sha512-SO9wHb6ph0/FN5OJxH4MiPscGah5wjOd0RRpaLvuBv9g8565Fgu0uMySFEPqwPHiQU90yzJ2FjRYKGrAhS1xig==}
|
||||||
|
engines: {node: ^16.0.0 || >=18.0.0}
|
||||||
|
peerDependencies:
|
||||||
|
typescript: '*'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
typescript:
|
||||||
|
optional: true
|
||||||
|
dependencies:
|
||||||
|
'@typescript-eslint/types': 7.0.1
|
||||||
|
'@typescript-eslint/visitor-keys': 7.0.1
|
||||||
|
debug: 4.3.4
|
||||||
|
globby: 11.1.0
|
||||||
|
is-glob: 4.0.3
|
||||||
|
minimatch: 9.0.3
|
||||||
|
semver: 7.6.0
|
||||||
|
ts-api-utils: 1.2.1(typescript@5.3.3)
|
||||||
|
typescript: 5.3.3
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/@typescript-eslint/utils@7.0.1(eslint@8.56.0)(typescript@5.3.3):
|
||||||
|
resolution: {integrity: sha512-oe4his30JgPbnv+9Vef1h48jm0S6ft4mNwi9wj7bX10joGn07QRfqIqFHoMiajrtoU88cIhXf8ahwgrcbNLgPA==}
|
||||||
|
engines: {node: ^16.0.0 || >=18.0.0}
|
||||||
|
peerDependencies:
|
||||||
|
eslint: ^8.56.0
|
||||||
|
dependencies:
|
||||||
|
'@eslint-community/eslint-utils': 4.4.0(eslint@8.56.0)
|
||||||
|
'@types/json-schema': 7.0.15
|
||||||
|
'@types/semver': 7.5.7
|
||||||
|
'@typescript-eslint/scope-manager': 7.0.1
|
||||||
|
'@typescript-eslint/types': 7.0.1
|
||||||
|
'@typescript-eslint/typescript-estree': 7.0.1(typescript@5.3.3)
|
||||||
|
eslint: 8.56.0
|
||||||
|
semver: 7.6.0
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
- typescript
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/@typescript-eslint/visitor-keys@7.0.1:
|
||||||
|
resolution: {integrity: sha512-hwAgrOyk++RTXrP4KzCg7zB2U0xt7RUU0ZdMSCsqF3eKUwkdXUMyTb0qdCuji7VIbcpG62kKTU9M1J1c9UpFBw==}
|
||||||
|
engines: {node: ^16.0.0 || >=18.0.0}
|
||||||
|
dependencies:
|
||||||
|
'@typescript-eslint/types': 7.0.1
|
||||||
|
eslint-visitor-keys: 3.4.3
|
||||||
|
dev: true
|
||||||
|
|
||||||
/@ungap/structured-clone@1.2.0:
|
/@ungap/structured-clone@1.2.0:
|
||||||
resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==}
|
resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==}
|
||||||
dev: true
|
dev: true
|
||||||
@@ -903,6 +1049,11 @@ packages:
|
|||||||
is-string: 1.0.7
|
is-string: 1.0.7
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/array-union@2.1.0:
|
||||||
|
resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/array.prototype.flat@1.3.2:
|
/array.prototype.flat@1.3.2:
|
||||||
resolution: {integrity: sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==}
|
resolution: {integrity: sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
@@ -1184,6 +1335,13 @@ packages:
|
|||||||
resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==}
|
resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/dir-glob@3.0.1:
|
||||||
|
resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
dependencies:
|
||||||
|
path-type: 4.0.0
|
||||||
|
dev: true
|
||||||
|
|
||||||
/dlv@1.1.3:
|
/dlv@1.1.3:
|
||||||
resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==}
|
resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==}
|
||||||
dev: true
|
dev: true
|
||||||
@@ -1699,6 +1857,18 @@ packages:
|
|||||||
define-properties: 1.2.1
|
define-properties: 1.2.1
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/globby@11.1.0:
|
||||||
|
resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
dependencies:
|
||||||
|
array-union: 2.1.0
|
||||||
|
dir-glob: 3.0.1
|
||||||
|
fast-glob: 3.3.2
|
||||||
|
ignore: 5.3.0
|
||||||
|
merge2: 1.4.1
|
||||||
|
slash: 3.0.0
|
||||||
|
dev: true
|
||||||
|
|
||||||
/gopd@1.0.1:
|
/gopd@1.0.1:
|
||||||
resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==}
|
resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==}
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -2101,6 +2271,13 @@ packages:
|
|||||||
yallist: 3.1.1
|
yallist: 3.1.1
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/lru-cache@6.0.0:
|
||||||
|
resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
dependencies:
|
||||||
|
yallist: 4.0.0
|
||||||
|
dev: true
|
||||||
|
|
||||||
/merge2@1.4.1:
|
/merge2@1.4.1:
|
||||||
resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
|
resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
|
||||||
engines: {node: '>= 8'}
|
engines: {node: '>= 8'}
|
||||||
@@ -2309,6 +2486,11 @@ packages:
|
|||||||
minipass: 7.0.4
|
minipass: 7.0.4
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/path-type@4.0.0:
|
||||||
|
resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/picocolors@1.0.0:
|
/picocolors@1.0.0:
|
||||||
resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==}
|
resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==}
|
||||||
dev: true
|
dev: true
|
||||||
@@ -2601,6 +2783,14 @@ packages:
|
|||||||
hasBin: true
|
hasBin: true
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/semver@7.6.0:
|
||||||
|
resolution: {integrity: sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
hasBin: true
|
||||||
|
dependencies:
|
||||||
|
lru-cache: 6.0.0
|
||||||
|
dev: true
|
||||||
|
|
||||||
/set-function-length@1.2.0:
|
/set-function-length@1.2.0:
|
||||||
resolution: {integrity: sha512-4DBHDoyHlM1IRPGYcoxexgh67y4ueR53FKV1yyxwFMY7aCqcN/38M1+SwZ/qJQ8iLv7+ck385ot4CcisOAPT9w==}
|
resolution: {integrity: sha512-4DBHDoyHlM1IRPGYcoxexgh67y4ueR53FKV1yyxwFMY7aCqcN/38M1+SwZ/qJQ8iLv7+ck385ot4CcisOAPT9w==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
@@ -2646,6 +2836,11 @@ packages:
|
|||||||
engines: {node: '>=14'}
|
engines: {node: '>=14'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/slash@3.0.0:
|
||||||
|
resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/source-map-js@1.0.2:
|
/source-map-js@1.0.2:
|
||||||
resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==}
|
resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
@@ -2820,6 +3015,15 @@ packages:
|
|||||||
is-number: 7.0.0
|
is-number: 7.0.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/ts-api-utils@1.2.1(typescript@5.3.3):
|
||||||
|
resolution: {integrity: sha512-RIYA36cJn2WiH9Hy77hdF9r7oEwxAtB/TS9/S4Qd90Ap4z5FSiin5zEiTL44OII1Y3IIlEvxwxFUVgrHSZ/UpA==}
|
||||||
|
engines: {node: '>=16'}
|
||||||
|
peerDependencies:
|
||||||
|
typescript: '>=4.2.0'
|
||||||
|
dependencies:
|
||||||
|
typescript: 5.3.3
|
||||||
|
dev: true
|
||||||
|
|
||||||
/ts-interface-checker@0.1.13:
|
/ts-interface-checker@0.1.13:
|
||||||
resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==}
|
resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==}
|
||||||
dev: true
|
dev: true
|
||||||
@@ -3034,6 +3238,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
|
resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/yallist@4.0.0:
|
||||||
|
resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/yaml@2.3.4:
|
/yaml@2.3.4:
|
||||||
resolution: {integrity: sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==}
|
resolution: {integrity: sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==}
|
||||||
engines: {node: '>= 14'}
|
engines: {node: '>= 14'}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react'
|
||||||
import './bootstrap';
|
import './bootstrap'
|
||||||
import {createRoot} from "react-dom/client";
|
import {createRoot} from "react-dom/client"
|
||||||
import App from "./pages/App";
|
import App from "./pages/App"
|
||||||
import '../css/app.css'
|
import '../css/app.css'
|
||||||
import './utilities/customProperties.ts'
|
import './utilities/customProperties.ts'
|
||||||
|
|
||||||
|
|||||||
6
resources/js/bootstrap.js
vendored
6
resources/js/bootstrap.js
vendored
@@ -4,10 +4,10 @@
|
|||||||
* CSRF token as a header based on the value of the "XSRF" token cookie.
|
* CSRF token as a header based on the value of the "XSRF" token cookie.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import axios from 'axios';
|
import axios from 'axios'
|
||||||
window.axios = axios;
|
window.axios = axios
|
||||||
|
|
||||||
window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
|
window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Echo exposes an expressive API for subscribing to channels and listening
|
* Echo exposes an expressive API for subscribing to channels and listening
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import React, {FC} from "react";
|
import React, {FC} from "react"
|
||||||
import {PropsWithChildren} from "react";
|
import {PropsWithChildren} from "react"
|
||||||
|
|
||||||
const Card: FC<PropsWithChildren<CardProps>> = ({children, className = ''}) => {
|
const Card: FC<PropsWithChildren<CardProps>> = ({children, className = ''}) => {
|
||||||
|
|
||||||
return <div className={`${className} border m-1 rounded py-1 px-2`}>
|
return <div className={`${className} m-1 rounded border px-2 py-1`}>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ const Field: FC<FieldProps> = ({children, type = 'text', className = '', classNa
|
|||||||
htmlFor={props.id ?? undefined}>
|
htmlFor={props.id ?? undefined}>
|
||||||
{children}
|
{children}
|
||||||
</label>}
|
</label>}
|
||||||
<input className={`${className} w-full mt-2 rounded dark:bg-gray-700`}
|
<input className={`${className} mt-2 w-full rounded dark:bg-gray-700`}
|
||||||
type={type}
|
type={type}
|
||||||
{...props}/>
|
{...props}/>
|
||||||
<div className={`error-message`} />
|
<div className={`error-message`} />
|
||||||
@@ -24,7 +24,7 @@ interface FieldProps {
|
|||||||
type?: HTMLInputTypeAttribute,
|
type?: HTMLInputTypeAttribute,
|
||||||
name: string,
|
name: string,
|
||||||
id?: string,
|
id?: string,
|
||||||
value: any,
|
value: string|number|undefined,
|
||||||
placeholder?: string,
|
placeholder?: string,
|
||||||
autoFocus?: boolean,
|
autoFocus?: boolean,
|
||||||
className?: string,
|
className?: string,
|
||||||
@@ -32,3 +32,27 @@ interface FieldProps {
|
|||||||
step?: string,
|
step?: string,
|
||||||
onChange: (event: React.ChangeEvent<HTMLInputElement>) => void,
|
onChange: (event: React.ChangeEvent<HTMLInputElement>) => void,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const TextArea: FC<TextAreaProps> = ({children, name, value, className = '', classNameForm = '', ...props}) => {
|
||||||
|
|
||||||
|
return <div className={`form-control ${classNameForm}`}>
|
||||||
|
{children && <label className="block text-gray-900 dark:text-gray-200"
|
||||||
|
htmlFor={props.id ?? undefined}>
|
||||||
|
{children}
|
||||||
|
</label>}
|
||||||
|
<textarea name={name} className={`${className} mt-2 w-full rounded dark:bg-gray-700`} {...props}>
|
||||||
|
{value}
|
||||||
|
</textarea>
|
||||||
|
<div className={`error-message`}/>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TextAreaProps {
|
||||||
|
children?: ReactElement|string,
|
||||||
|
id?: string,
|
||||||
|
name: string,
|
||||||
|
value: string|undefined,
|
||||||
|
className?: string,
|
||||||
|
classNameForm?: string,
|
||||||
|
onChange: (event: React.ChangeEvent<HTMLTextAreaElement>) => void,
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React from "react"
|
import React from "react"
|
||||||
import {Link, NavLink, useLocation} from "react-router-dom"
|
import {Link, NavLink, useLocation} from "react-router-dom"
|
||||||
import useAuthUser from "../hooks/AuthUser";
|
import useAuthUser from "../hooks/AuthUser"
|
||||||
import Tracker from "./TimeTrackers/Tracker";
|
import Tracker from "./TimeTrackers/Tracker"
|
||||||
|
|
||||||
const Header = () => {
|
const Header = () => {
|
||||||
|
|
||||||
@@ -10,8 +10,8 @@ const Header = () => {
|
|||||||
|
|
||||||
console.log(authUser)
|
console.log(authUser)
|
||||||
|
|
||||||
return <header className="bg-blue-700 text-white text-xl">
|
return <header className="bg-blue-700 text-xl text-white">
|
||||||
<div className="flex py-3 px-5 justify-between">
|
<div className="flex justify-between px-5 py-3">
|
||||||
<div>
|
<div>
|
||||||
<Link to="/">Ticcat</Link>
|
<Link to="/">Ticcat</Link>
|
||||||
</div>
|
</div>
|
||||||
@@ -26,7 +26,7 @@ const Header = () => {
|
|||||||
{/*<Link to="/sinscrire">S'inscrire</Link>*/}
|
{/*<Link to="/sinscrire">S'inscrire</Link>*/}
|
||||||
</span>}
|
</span>}
|
||||||
</div>
|
</div>
|
||||||
<nav className="bg-gray-600 px-5 flex gap-5">
|
<nav className="flex gap-5 bg-gray-600 px-5">
|
||||||
<NavLink to="/">ToDos</NavLink>
|
<NavLink to="/">ToDos</NavLink>
|
||||||
<NavLink to="/times">Times</NavLink>
|
<NavLink to="/times">Times</NavLink>
|
||||||
</nav>
|
</nav>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, {FC, FormEvent, PropsWithChildren, ReactNode} from "react"
|
import React, {FC, FormEvent, ReactNode} from "react"
|
||||||
import ReactDOM from "react-dom"
|
import ReactDOM from "react-dom"
|
||||||
|
|
||||||
export const Modal: FC<ModalProps> = ({children, show, closeModal, ...props}) => {
|
export const Modal: FC<ModalProps> = ({children, show, closeModal, ...props}) => {
|
||||||
@@ -9,7 +9,7 @@ export const Modal: FC<ModalProps> = ({children, show, closeModal, ...props}) =>
|
|||||||
}
|
}
|
||||||
|
|
||||||
return show && <Overlay onClick={closeModal}>
|
return show && <Overlay onClick={closeModal}>
|
||||||
<div className="dark:bg-gray-900 cursor-auto dark:text-white bg-white"
|
<div className="cursor-auto bg-white dark:bg-gray-900 dark:text-white"
|
||||||
onClick={stopEvent}
|
onClick={stopEvent}
|
||||||
{...props}>
|
{...props}>
|
||||||
{children}
|
{children}
|
||||||
@@ -29,7 +29,7 @@ const Overlay: FC<OverlayProps> = ({children, ...props}) => {
|
|||||||
|
|
||||||
if (app) {
|
if (app) {
|
||||||
return ReactDOM.createPortal(
|
return ReactDOM.createPortal(
|
||||||
<button className={"flex justify-center items-center w-full fixed inset-0 z-10 bg-gray-900/50"}
|
<button className={"fixed inset-0 z-10 flex w-full items-center justify-center bg-gray-900/50"}
|
||||||
{...props}>
|
{...props}>
|
||||||
{children}
|
{children}
|
||||||
</button>,
|
</button>,
|
||||||
|
|||||||
@@ -23,6 +23,12 @@ export const DraggableSVG: FC<ComponentProps<any>> = (props) => SVGSkeleton({
|
|||||||
...props
|
...props
|
||||||
})
|
})
|
||||||
|
|
||||||
|
export const EditSVG: FC<ComponentProps<any>> = (props) => SVGSkeleton({
|
||||||
|
viewBox: "0 0 576 512",
|
||||||
|
paths: <path d="M402.3 344.9l32-32c5-5 13.7-1.5 13.7 5.7V464c0 26.5-21.5 48-48 48H48c-26.5 0-48-21.5-48-48V112c0-26.5 21.5-48 48-48h273.5c7.1 0 10.7 8.6 5.7 13.7l-32 32c-1.5 1.5-3.5 2.3-5.7 2.3H48v352h352V350.5c0-2.1.8-4.1 2.3-5.6zm156.6-201.8L296.3 405.7l-90.4 10c-26.2 2.9-48.5-19.2-45.6-45.6l10-90.4L432.9 17.1c22.9-22.9 59.9-22.9 82.7 0l43.2 43.2c22.9 22.9 22.9 60 .1 82.8zM460.1 174L402 115.9 216.2 301.8l-7.3 65.3 65.3-7.3L460.1 174zm64.8-79.7l-43.2-43.2c-4.1-4.1-10.8-4.1-14.8 0L436 82l58.1 58.1 30.9-30.9c4-4.2 4-10.8-.1-14.9z" />,
|
||||||
|
...props
|
||||||
|
})
|
||||||
|
|
||||||
export const PauseSVG: FC<ComponentProps<any>> = (props) => SVGSkeleton({
|
export const PauseSVG: FC<ComponentProps<any>> = (props) => SVGSkeleton({
|
||||||
paths: <path d="M16 19q-.825 0-1.412-.587T14 17V7q0-.825.588-1.412T16 5q.825 0 1.413.588T18 7v10q0 .825-.587 1.413T16 19m-8 0q-.825 0-1.412-.587T6 17V7q0-.825.588-1.412T8 5q.825 0 1.413.588T10 7v10q0 .825-.587 1.413T8 19"/>,
|
paths: <path d="M16 19q-.825 0-1.412-.587T14 17V7q0-.825.588-1.412T16 5q.825 0 1.413.588T18 7v10q0 .825-.587 1.413T16 19m-8 0q-.825 0-1.412-.587T6 17V7q0-.825.588-1.412T8 5q.825 0 1.413.588T10 7v10q0 .825-.587 1.413T8 19"/>,
|
||||||
...props
|
...props
|
||||||
|
|||||||
@@ -1,20 +1,17 @@
|
|||||||
import React, {FC, FormEvent, ReactEventHandler, useState} from "react"
|
import React, {FC, FormEvent, useState} from "react"
|
||||||
import Field from "../Field";
|
import Field from "../Field"
|
||||||
import {timeTracker} from "../../utilities/types";
|
import {timeTracker} from "../../utilities/types"
|
||||||
|
|
||||||
const TimeTrackerEdit: FC<TimeTrackerEditProps> = ({timeTracker}) => {
|
const TimeTrackerEdit: FC<TimeTrackerEditProps> = ({timeTracker}) => {
|
||||||
|
|
||||||
const [trackerForm, setTrackerForm] = useState<timeTracker>(timeTracker)
|
const [trackerForm, setTrackerForm] = useState<timeTracker>(timeTracker)
|
||||||
|
|
||||||
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
console.log(trackerForm, event.target.value)
|
|
||||||
setTrackerForm({...trackerForm, [event.target.name]: event.target.value.replace('T', ' ')})
|
setTrackerForm({...trackerForm, [event.target.name]: event.target.value.replace('T', ' ')})
|
||||||
}
|
}
|
||||||
|
|
||||||
const onSubmit = (event: FormEvent) => {
|
const onSubmit = (event: FormEvent) => {
|
||||||
event.preventDefault()
|
console.log('submit', trackerForm, event)
|
||||||
|
|
||||||
console.log(trackerForm)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return <form onSubmit={onSubmit}>
|
return <form onSubmit={onSubmit}>
|
||||||
@@ -27,7 +24,7 @@ const TimeTrackerEdit: FC<TimeTrackerEditProps> = ({timeTracker}) => {
|
|||||||
value={trackerForm.end_at}
|
value={trackerForm.end_at}
|
||||||
onChange={handleChange}/>
|
onChange={handleChange}/>
|
||||||
|
|
||||||
<button type="submit">Valider</button>
|
<button type="submit" onClick={onSubmit}>Valider</button>
|
||||||
</form>
|
</form>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React, {useEffect, useState} from "react"
|
import React, {useEffect, useState} from "react"
|
||||||
import useTracker from "../../hooks/TraskerHook"
|
import useTracker from "../../hooks/TraskerHook"
|
||||||
import {Link} from "react-router-dom";
|
import {Link} from "react-router-dom"
|
||||||
import {StopSVG} from "../SVG";
|
import {StopSVG} from "../SVG"
|
||||||
|
|
||||||
const Tracker = () => {
|
const Tracker = () => {
|
||||||
|
|
||||||
@@ -14,14 +14,14 @@ const Tracker = () => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setTimeout(() => setTimer(formatTimer(currentTimeTracker?.start_at)), 1000)
|
setTimeout(() => setTimer(formatTimer(currentTimeTracker?.start_at)), 1000)
|
||||||
}, [timer]);
|
}, [timer])
|
||||||
|
|
||||||
const formatTimer = (startAt: string|null|undefined) => {
|
const formatTimer = (startAt: string|null|undefined) => {
|
||||||
if (!startAt) {
|
if (!startAt) {
|
||||||
return '--:--'
|
return '--:--'
|
||||||
}
|
}
|
||||||
let timer = Math.floor(((new Date()).getTime() - (new Date(startAt)).getTime()) / 1000)
|
const timer = Math.floor(((new Date()).getTime() - (new Date(startAt)).getTime()) / 1000)
|
||||||
return timer.durationify()
|
return Number(timer).durationify()
|
||||||
// let hours = Math.floor(timer / 3600)
|
// let hours = Math.floor(timer / 3600)
|
||||||
// let minutes = Math.floor((timer - hours * 3600) / 60)
|
// let minutes = Math.floor((timer - hours * 3600) / 60)
|
||||||
// let secondes = timer - hours * 3600 - minutes * 60
|
// let secondes = timer - hours * 3600 - minutes * 60
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React, {FC, useEffect, useState} from "react"
|
import React, {FC, useEffect, useState} from "react"
|
||||||
import useAxiosTools from "../../hooks/AxiosTools";
|
import useAxiosTools from "../../hooks/AxiosTools"
|
||||||
import {toDo} from "../../utilities/types";
|
import {toDo} from "../../utilities/types"
|
||||||
import {PlaySVG} from "../SVG";
|
import {PlaySVG} from "../SVG"
|
||||||
|
|
||||||
const ToDoFinish: FC<ToDoFinishProps> = ({reload}) => {
|
const ToDoFinish: FC<ToDoFinishProps> = ({reload}) => {
|
||||||
|
|
||||||
@@ -36,14 +36,14 @@ const ToDoFinish: FC<ToDoFinishProps> = ({reload}) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return <div>
|
return <div>
|
||||||
<button className="flex justify-between items-center w-full bg-blue-700 px-2 py-1 rounded cursor-pointer"
|
<button className="flex w-full cursor-pointer items-center justify-between rounded bg-blue-700 px-2 py-1"
|
||||||
onClick={handleShow}>
|
onClick={handleShow}>
|
||||||
<h2 className="inline">Tâches terminées</h2>
|
<h2 className="inline">Tâches terminées</h2>
|
||||||
<span><PlaySVG className={`w-4 transition ${showTodos ? 'rotate-90' : 'rotate-180'}`} /></span>
|
<span><PlaySVG className={`w-4 transition ${showTodos ? 'rotate-90' : 'rotate-180'}`} /></span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
{errorLabel()}
|
{errorLabel()}
|
||||||
{showTodos && <ul className="list-disc m-2">
|
{showTodos && <ul className="m-2 list-disc">
|
||||||
{toDos.map(toDo => <li key={toDo.id} className="flex gap-2">
|
{toDos.map(toDo => <li key={toDo.id} className="flex gap-2">
|
||||||
<span>{toDo.checked ? (new Date(toDo.checked)).toSmallFrDate() : ''}</span>
|
<span>{toDo.checked ? (new Date(toDo.checked)).toSmallFrDate() : ''}</span>
|
||||||
<span className="flex-1">{toDo.name}</span>
|
<span className="flex-1">{toDo.name}</span>
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
import React, {FC, useEffect, useState} from "react";
|
import React, {FC, useEffect, useState} from "react"
|
||||||
import useAxiosTools from "../../hooks/AxiosTools";
|
import useAxiosTools from "../../hooks/AxiosTools"
|
||||||
import {toDo} from "../../utilities/types";
|
import {toDo} from "../../utilities/types"
|
||||||
import {Link} from "react-router-dom";
|
import {Link} from "react-router-dom"
|
||||||
import useTracker from "../../hooks/TraskerHook";
|
import useTracker from "../../hooks/TraskerHook"
|
||||||
import {Simulate} from "react-dom/test-utils";
|
import {DraggableSVG, PauseSVG, PlaySVG} from "../SVG"
|
||||||
import load = Simulate.load;
|
|
||||||
import {DraggableSVG, PauseSVG, PlaySVG} from "../SVG";
|
|
||||||
|
|
||||||
const ToDoIndex: FC<ToDoIndexProps> = ({reload, setReload}) => {
|
const ToDoIndex: FC<ToDoIndexProps> = ({reload, setReload}) => {
|
||||||
|
|
||||||
@@ -21,7 +19,7 @@ const ToDoIndex: FC<ToDoIndexProps> = ({reload, setReload}) => {
|
|||||||
if (reload && !loading) {
|
if (reload && !loading) {
|
||||||
fetchToDos()
|
fetchToDos()
|
||||||
}
|
}
|
||||||
}, [reload]);
|
}, [reload])
|
||||||
|
|
||||||
const fetchToDos = async () => {
|
const fetchToDos = async () => {
|
||||||
try {
|
try {
|
||||||
@@ -57,18 +55,18 @@ const ToDoIndex: FC<ToDoIndexProps> = ({reload, setReload}) => {
|
|||||||
onChange={() =>toggleCheck(toDo)}
|
onChange={() =>toggleCheck(toDo)}
|
||||||
className=""/>
|
className=""/>
|
||||||
<Link to={"/todos/" + toDo.id}
|
<Link to={"/todos/" + toDo.id}
|
||||||
className={`${toDo.checked ? 'line-through' : ''} flex-1 flex justify-between`}>
|
className={`${toDo.checked ? 'line-through' : ''} flex flex-1 justify-between`}>
|
||||||
<span>{toDo.name}</span>
|
<span>{toDo.name}</span>
|
||||||
<span className="text-gray-400 text-md mr-2">{toDo.duration.durationify()}</span>
|
<span className="mr-2 text-sm text-gray-400">{toDo.duration.durationify()}</span>
|
||||||
</Link>
|
</Link>
|
||||||
{toDo.id === currentTimeTracker?.to_do?.id
|
{toDo.id === currentTimeTracker?.to_do?.id
|
||||||
? <button className="cursor-pointer w-7 justify-center flex items-center"
|
? <button className="flex w-7 cursor-pointer items-center justify-center"
|
||||||
type="button"
|
type="button"
|
||||||
title="Commencer"
|
title="Commencer"
|
||||||
onClick={stopCurrentTimeTrack}>
|
onClick={stopCurrentTimeTrack}>
|
||||||
<PauseSVG className="w-7"/>
|
<PauseSVG className="w-7"/>
|
||||||
</button>
|
</button>
|
||||||
: <button className="cursor-pointer w-7 justify-center flex items-center"
|
: <button className="flex w-7 cursor-pointer items-center justify-center"
|
||||||
type="button"
|
type="button"
|
||||||
title="Commencer"
|
title="Commencer"
|
||||||
onClick={() => startTrackToDo(toDo)}>
|
onClick={() => startTrackToDo(toDo)}>
|
||||||
|
|||||||
@@ -1,101 +0,0 @@
|
|||||||
import React, {FC, HTMLInputTypeAttribute, ReactElement, useEffect, useState} from "react"
|
|
||||||
import {useParams} from "react-router-dom";
|
|
||||||
import useAxiosTools from "../../hooks/AxiosTools";
|
|
||||||
import {timeTracker, toDo} from "../../utilities/types";
|
|
||||||
|
|
||||||
const ToDoShow = () => {
|
|
||||||
|
|
||||||
const {id} = useParams()
|
|
||||||
console.log(id)
|
|
||||||
const {setLoading, errorCatch, errorLabel, axiosGet, axiosPut} = useAxiosTools(true)
|
|
||||||
const [toDo, setToDo] = useState<toDo|null>(null)
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
fetchToDo()
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
const fetchToDo = async () => {
|
|
||||||
try {
|
|
||||||
const res = await axiosGet('/api/todos/' + id)
|
|
||||||
setToDo(res.data)
|
|
||||||
} catch (error) {
|
|
||||||
errorCatch(error)
|
|
||||||
} finally {
|
|
||||||
setLoading(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return <div>
|
|
||||||
<h1>{toDo?.name}</h1>
|
|
||||||
<h1>{toDo?.description}</h1>
|
|
||||||
|
|
||||||
{toDo && <ToDoTimeTrackers toDo={toDo} />}
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ToDoShow
|
|
||||||
|
|
||||||
const ToDoTimeTrackers: FC<ToDoTimeTrackers> = ({toDo: toDo}) => {
|
|
||||||
|
|
||||||
const {setLoading, errorCatch, errorLabel, axiosGet, axiosPut} = useAxiosTools(true)
|
|
||||||
const [timeTrackers, setTimeTrackers] = useState<timeTracker[]>([])
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
fetchTimeTrackers()
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
const fetchTimeTrackers = async () => {
|
|
||||||
try {
|
|
||||||
const res = await axiosGet(`/api/todos/${toDo.id}/time-trackers`)
|
|
||||||
setTimeTrackers(res.data)
|
|
||||||
} catch (error) {
|
|
||||||
errorCatch(error)
|
|
||||||
} finally {
|
|
||||||
setLoading(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const timeSpend = () => {
|
|
||||||
let timer = 0
|
|
||||||
let more = false
|
|
||||||
timeTrackers.forEach(timeTracker => {
|
|
||||||
if (! timeTracker.end_at) {
|
|
||||||
more = true
|
|
||||||
} else {
|
|
||||||
timer += ((new Date(timeTracker.end_at)).getTime()) - (new Date(timeTracker.start_at)).getTime()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
timer = Math.floor(timer / 1000)
|
|
||||||
return (more ? '+' : '') + timer.durationify()
|
|
||||||
// let hours = Math.floor(timer / 3600)
|
|
||||||
// let minutes = Math.floor((timer - hours * 3600) / 60)
|
|
||||||
// let secondes = timer - hours * 3600 - minutes * 60
|
|
||||||
// return `${more ? '+' : ''} ${hours}:${String(minutes).padStart(2, '0')}:${String(secondes).padStart(2, '0')}`
|
|
||||||
}
|
|
||||||
|
|
||||||
return <div>
|
|
||||||
<div>Temps passé : {timeSpend()}</div>
|
|
||||||
<table className="mx-auto">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Début</th>
|
|
||||||
<th>Fin</th>
|
|
||||||
<th>Différence</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{timeTrackers.map(timeTracker => <tr key={timeTracker.id} className="text-center">
|
|
||||||
<td>{timeTracker.start_at ? (new Date(timeTracker.start_at)).toSmallFrDate() : ''}</td>
|
|
||||||
<td>{timeTracker.end_at ? (new Date(timeTracker.end_at)).toSmallFrDate() : ''}</td>
|
|
||||||
<td className="text-right">{timeTracker.start_at && timeTracker.end_at ? (new Date(timeTracker.end_at)).difference(new Date(timeTracker.start_at)) : ''}</td>
|
|
||||||
</tr>)}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ToDoTimeTrackers {
|
|
||||||
toDo: toDo,
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import React, {FC, FormEvent, useState} from "react"
|
import React, {FC, FormEvent, useState} from "react"
|
||||||
import Field from "../Field";
|
import Field from "../Field"
|
||||||
import useAxiosTools from "../../hooks/AxiosTools";
|
import useAxiosTools from "../../hooks/AxiosTools"
|
||||||
|
|
||||||
const ToDoStore: FC<ToDoStoreProps> = ({setReload}) => {
|
const ToDoStore: FC<ToDoStoreProps> = ({setReload}) => {
|
||||||
|
|
||||||
@@ -24,10 +24,10 @@ const ToDoStore: FC<ToDoStoreProps> = ({setReload}) => {
|
|||||||
<Field name="todo"
|
<Field name="todo"
|
||||||
value={toDo}
|
value={toDo}
|
||||||
classNameForm="flex-1"
|
classNameForm="flex-1"
|
||||||
className="h-10 !mt-0 rounded-r-none px-2"
|
className="!mt-0 h-10 rounded-r-none px-2"
|
||||||
onChange={event => setToDo(event.target.value)} />
|
onChange={event => setToDo(event.target.value)} />
|
||||||
<button type="submit"
|
<button type="submit"
|
||||||
className="bg-blue-900 h-10 rounded-r px-5">
|
className="h-10 rounded-r bg-blue-900 px-5">
|
||||||
Ajouter
|
Ajouter
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ import React, {
|
|||||||
useContext,
|
useContext,
|
||||||
useEffect,
|
useEffect,
|
||||||
useState
|
useState
|
||||||
} from "react";
|
} from "react"
|
||||||
import axios from "axios";
|
import axios from "axios"
|
||||||
|
|
||||||
const AuthUserContext = createContext<AuthUserProps|undefined>(undefined)
|
const AuthUserContext = createContext<AuthUserProps|undefined>(undefined)
|
||||||
|
|
||||||
@@ -44,7 +44,7 @@ export const AuthUserProvider = ({children}: PropsWithChildren) => {
|
|||||||
const logout = async () => {
|
const logout = async () => {
|
||||||
try {
|
try {
|
||||||
setLoadingAuthUser(false)
|
setLoadingAuthUser(false)
|
||||||
const res = await axios.delete('/api/logout')
|
await axios.delete('/api/logout')
|
||||||
setAuthUser(null)
|
setAuthUser(null)
|
||||||
window.location.replace('/')
|
window.location.replace('/')
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import {useState} from "react";
|
import React, {useState} from "react"
|
||||||
import axios from "axios";
|
import axios from "axios"
|
||||||
import React from "react";
|
import {cleanErrorsForm, displayFormErrors} from "../utilities/form"
|
||||||
import {cleanErrorsForm, displayFormErrors} from "../utilities/form";
|
|
||||||
|
|
||||||
const useAxiosTools = (isLoading = false) => {
|
const useAxiosTools = (isLoading = false) => {
|
||||||
|
|
||||||
@@ -23,7 +22,7 @@ const useAxiosTools = (isLoading = false) => {
|
|||||||
|
|
||||||
const errorLabel = () => {
|
const errorLabel = () => {
|
||||||
|
|
||||||
return error ? <div className="bg-red-600 rounded m-2 text-center text-white px-2 py-1 mx-auto">{error}</div>: null
|
return error ? <div className="m-2 mx-auto rounded bg-red-600 px-2 py-1 text-center text-white">{error}</div>: null
|
||||||
}
|
}
|
||||||
|
|
||||||
const cleanErrors = () => {
|
const cleanErrors = () => {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import React, {createContext, PropsWithChildren, useContext, useEffect, useState} from "react";
|
import React, {createContext, PropsWithChildren, useContext, useEffect, useState} from "react"
|
||||||
import {timeTracker, toDo} from "../utilities/types";
|
import {timeTracker, toDo} from "../utilities/types"
|
||||||
import useAxiosTools from "./AxiosTools";
|
import useAxiosTools from "./AxiosTools"
|
||||||
|
|
||||||
|
|
||||||
const TrackerContext = createContext<TrackerProps|undefined>(undefined)
|
const TrackerContext = createContext<TrackerProps|undefined>(undefined)
|
||||||
@@ -9,12 +9,10 @@ interface TrackerProps {
|
|||||||
currentTimeTracker: timeTracker|null,
|
currentTimeTracker: timeTracker|null,
|
||||||
startTrackToDo: (toDo: toDo) => void,
|
startTrackToDo: (toDo: toDo) => void,
|
||||||
stopCurrentTimeTrack: () => void,
|
stopCurrentTimeTrack: () => void,
|
||||||
isToDoTracked: (toDo: toDo) => boolean,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const TrackerProvider = ({children}: PropsWithChildren) => {
|
export const TrackerProvider = ({children}: PropsWithChildren) => {
|
||||||
const [currentTimeTracker, setCurrentTimeTracker] = useState<timeTracker|null>(null)
|
const [currentTimeTracker, setCurrentTimeTracker] = useState<timeTracker|null>(null)
|
||||||
const [toDoTracked, setToDoTracked] = useState<toDo|null>(null)
|
|
||||||
const {axiosGet, axiosPost, axiosDelete} = useAxiosTools()
|
const {axiosGet, axiosPost, axiosDelete} = useAxiosTools()
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -41,16 +39,14 @@ export const TrackerProvider = ({children}: PropsWithChildren) => {
|
|||||||
|
|
||||||
const stopCurrentTimeTrack = async () => {
|
const stopCurrentTimeTrack = async () => {
|
||||||
try {
|
try {
|
||||||
const res = await axiosDelete(`/api/time-trackers/user`)
|
await axiosDelete(`/api/time-trackers/user`)
|
||||||
setCurrentTimeTracker(null)
|
setCurrentTimeTracker(null)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error)
|
console.error(error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const isToDoTracked = (toDo: toDo) => toDo.id === toDoTracked?.id
|
return <TrackerContext.Provider value={{currentTimeTracker, startTrackToDo, stopCurrentTimeTrack}}>
|
||||||
|
|
||||||
return <TrackerContext.Provider value={{currentTimeTracker, startTrackToDo, stopCurrentTimeTrack, isToDoTracked}}>
|
|
||||||
{children}
|
{children}
|
||||||
</TrackerContext.Provider>
|
</TrackerContext.Provider>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React from "react"
|
import React from "react"
|
||||||
import Router from "./Router"
|
import Router from "./Router"
|
||||||
import {AuthUserProvider} from "../hooks/AuthUser";
|
import {AuthUserProvider} from "../hooks/AuthUser"
|
||||||
import {TrackerProvider} from "../hooks/TraskerHook";
|
import {TrackerProvider} from "../hooks/TraskerHook"
|
||||||
|
|
||||||
const App = () => {
|
const App = () => {
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
import React, {FormEvent, SyntheticEvent, useState} from "react"
|
import React, {FormEvent, useState} from "react"
|
||||||
import Field from "../../components/Field";
|
import Field from "../../components/Field"
|
||||||
import axios from "axios";
|
import useAxiosTools from "../../hooks/AxiosTools"
|
||||||
import {useNavigate} from "react-router-dom";
|
|
||||||
import useAuthUser from "../../hooks/AuthUser";
|
|
||||||
import useAxiosTools from "../../hooks/AxiosTools";
|
|
||||||
|
|
||||||
const ForgotPassword = () => {
|
const ForgotPassword = () => {
|
||||||
|
|
||||||
@@ -16,15 +13,15 @@ const ForgotPassword = () => {
|
|||||||
try {
|
try {
|
||||||
cleanErrors()
|
cleanErrors()
|
||||||
await axiosGet('/sanctum/csrf-cookie')
|
await axiosGet('/sanctum/csrf-cookie')
|
||||||
const res = await axiosPost('/api/forgot', {email})
|
await axiosPost('/api/forgot', {email})
|
||||||
setMessage(true)
|
setMessage(true)
|
||||||
} catch (e) {
|
} catch (error) {
|
||||||
errorCatch(e)
|
errorCatch(error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return <div>
|
return <div>
|
||||||
<form onSubmit={handleSubmit} className="w-96 mx-auto mt-10 border shadow p-3">
|
<form onSubmit={handleSubmit} className="mx-auto mt-10 w-96 border p-3 shadow">
|
||||||
<h1 className="text-center">Mot de passe oublié</h1>
|
<h1 className="text-center">Mot de passe oublié</h1>
|
||||||
|
|
||||||
{message && <p className="bg-green-600">Un email vous a été envoyé pour modifier le mot de passe.</p>}
|
{message && <p className="bg-green-600">Un email vous a été envoyé pour modifier le mot de passe.</p>}
|
||||||
@@ -37,7 +34,7 @@ const ForgotPassword = () => {
|
|||||||
value={email}
|
value={email}
|
||||||
onChange={event => setEmail(event.target.value)}
|
onChange={event => setEmail(event.target.value)}
|
||||||
autoFocus>Email</Field>
|
autoFocus>Email</Field>
|
||||||
<button type="submit" className="mt-5 btn-primary w-full block text-lg">Valider</button>
|
<button type="submit" className="btn-primary mt-5 block w-full text-lg">Valider</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
import React, {FormEvent, SyntheticEvent, useState} from "react"
|
import React, {FormEvent, useState} from "react"
|
||||||
import axios from "axios";
|
import {Link, useNavigate} from "react-router-dom"
|
||||||
import {Link, useNavigate} from "react-router-dom";
|
import useAxiosTools from "../../hooks/AxiosTools"
|
||||||
import useAxiosTools from "../../hooks/AxiosTools";
|
import Field from "../../components/Field"
|
||||||
import Field from "../../components/Field";
|
import useAuthUser from "../../hooks/AuthUser"
|
||||||
import useAuthUser from "../../hooks/AuthUser";
|
import Card from "../../components/Card"
|
||||||
import Card from "../../components/Card";
|
|
||||||
|
|
||||||
const Login = () => {
|
const Login = () => {
|
||||||
|
|
||||||
@@ -28,9 +27,9 @@ const Login = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return <div>
|
return <div>
|
||||||
<Card className="w-96 mx-auto mt-10 p-2 overflow-hidden">
|
<Card className="mx-auto mt-10 w-96 overflow-hidden p-2">
|
||||||
<form onSubmit={handleSubmit}>
|
<form onSubmit={handleSubmit}>
|
||||||
<h1 className="text-center bg-blue-500 -mx-2 -mt-1 text-lg font-bold px-2 py-1 mb-2">
|
<h1 className="-mx-2 -mt-1 mb-2 bg-blue-500 px-2 py-1 text-center text-lg font-bold">
|
||||||
Connexion
|
Connexion
|
||||||
</h1>
|
</h1>
|
||||||
{errorLabel()}
|
{errorLabel()}
|
||||||
@@ -46,7 +45,7 @@ const Login = () => {
|
|||||||
placeholder="******"
|
placeholder="******"
|
||||||
value={password}
|
value={password}
|
||||||
onChange={event => setPassword(event.target.value)}>Mot de passe</Field>
|
onChange={event => setPassword(event.target.value)}>Mot de passe</Field>
|
||||||
<button type="submit" className="mt-5 btn-primary w-full block text-lg">Valider</button>
|
<button type="submit" className="btn-primary mt-5 block w-full text-lg">Valider</button>
|
||||||
<Link to="/mot-de-passe-oubliee" className="mt-2 inline-block">Mot de passe oublié ?</Link>
|
<Link to="/mot-de-passe-oubliee" className="mt-2 inline-block">Mot de passe oublié ?</Link>
|
||||||
</form>
|
</form>
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import React, {FormEvent, useState} from "react"
|
import React, {FormEvent, useState} from "react"
|
||||||
import useAuthUser from "../../hooks/AuthUser"
|
import useAuthUser from "../../hooks/AuthUser"
|
||||||
import Field from "../../components/Field";
|
import Field from "../../components/Field"
|
||||||
import useAxiosTools from "../../hooks/AxiosTools";
|
import useAxiosTools from "../../hooks/AxiosTools"
|
||||||
import PageLayout from "../../components/PageLayout";
|
import PageLayout from "../../components/PageLayout"
|
||||||
import Card from "../../components/Card";
|
import Card from "../../components/Card"
|
||||||
|
|
||||||
const Profile = () => {
|
const Profile = () => {
|
||||||
|
|
||||||
@@ -65,7 +65,7 @@ const Profile = () => {
|
|||||||
Longitude
|
Longitude
|
||||||
</Field>
|
</Field>
|
||||||
<div className="self-end">
|
<div className="self-end">
|
||||||
<button type="submit" className="btn-primary w-24 h-10">Valider</button>
|
<button type="submit" className="btn-primary h-10 w-24">Valider</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>}
|
</form>}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import React, {ChangeEvent, FormEvent, SyntheticEvent, useState} from "react"
|
import React, {FormEvent, useState} from "react"
|
||||||
import Field from "../../components/Field";
|
import Field from "../../components/Field"
|
||||||
import axios from "axios";
|
import axios from "axios"
|
||||||
import {useNavigate} from "react-router-dom";
|
import {useNavigate} from "react-router-dom"
|
||||||
import Card from "../../components/Card";
|
import Card from "../../components/Card"
|
||||||
import useAuthUser from "../../hooks/AuthUser";
|
import useAuthUser from "../../hooks/AuthUser"
|
||||||
|
|
||||||
const Register = () => {
|
const Register = () => {
|
||||||
|
|
||||||
@@ -26,10 +26,10 @@ const Register = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return <div>
|
return <div>
|
||||||
<Card className="w-96 mx-auto mt-10 p-2 overflow-hidden">
|
<Card className="mx-auto mt-10 w-96 overflow-hidden p-2">
|
||||||
<form onSubmit={handleSubmit}>
|
<form onSubmit={handleSubmit}>
|
||||||
<h1 className="text-center bg-blue-500 -mx-2 -mt-1 text-lg font-bold px-2 py-1 mb-2">
|
<h1 className="-mx-2 -mt-1 mb-2 bg-blue-500 px-2 py-1 text-center text-lg font-bold">
|
||||||
S'inscrire
|
S'inscrire
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<Field placeholder="Nom"
|
<Field placeholder="Nom"
|
||||||
@@ -49,7 +49,7 @@ const Register = () => {
|
|||||||
value={password}
|
value={password}
|
||||||
onChange={event => setPassword(event.target.value)}
|
onChange={event => setPassword(event.target.value)}
|
||||||
autoFocus>Mot de passe</Field>
|
autoFocus>Mot de passe</Field>
|
||||||
<button type="submit" className="mt-5 btn-primary w-full block text-lg">Valider</button>
|
<button type="submit" className="btn-primary mt-5 block w-full text-lg">Valider</button>
|
||||||
</form>
|
</form>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
import Field from "../../components/Field";
|
import Field from "../../components/Field"
|
||||||
import React, {FormEvent, useState} from "react";
|
import React, {FormEvent, useState} from "react"
|
||||||
import {useNavigate, useParams} from "react-router-dom";
|
import {useNavigate, useParams} from "react-router-dom"
|
||||||
import useAuthUser from "../../hooks/AuthUser";
|
import useAuthUser from "../../hooks/AuthUser"
|
||||||
import axios from "axios";
|
import useAxiosTools from "../../hooks/AxiosTools"
|
||||||
import useAxiosTools from "../../hooks/AxiosTools";
|
|
||||||
|
|
||||||
const Reset = () => {
|
const Reset = () => {
|
||||||
|
|
||||||
let {token} = useParams()
|
const {token} = useParams()
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const {setAuthUser} = useAuthUser()
|
const {setAuthUser} = useAuthUser()
|
||||||
const [email, setEmail] = useState('')
|
const [email, setEmail] = useState('')
|
||||||
@@ -29,7 +28,7 @@ const Reset = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return <div>
|
return <div>
|
||||||
<form onSubmit={handleSubmit} className="w-96 mx-auto mt-10 border shadow p-3">
|
<form onSubmit={handleSubmit} className="mx-auto mt-10 w-96 border p-3 shadow">
|
||||||
<h1 className="text-center">Modifier voter mot de passe</h1>
|
<h1 className="text-center">Modifier voter mot de passe</h1>
|
||||||
|
|
||||||
{errorLabel()}
|
{errorLabel()}
|
||||||
@@ -50,7 +49,7 @@ const Reset = () => {
|
|||||||
placeholder="******"
|
placeholder="******"
|
||||||
value={samePassword}
|
value={samePassword}
|
||||||
onChange={event => setSamePassword(event.target.value)}>Confirmation du mot de passe</Field>
|
onChange={event => setSamePassword(event.target.value)}>Confirmation du mot de passe</Field>
|
||||||
<button type="submit" className="mt-5 btn-primary w-full block text-lg">Valider</button>
|
<button type="submit" className="btn-primary mt-5 block w-full text-lg">Valider</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import React, {useState} from "react"
|
import React, {useState} from "react"
|
||||||
import useAuthUser from "../hooks/AuthUser";
|
import useAuthUser from "../hooks/AuthUser"
|
||||||
import ToDoStore from "../components/toDos/ToDoStore";
|
import ToDoStore from "../components/toDos/ToDoStore"
|
||||||
import ToDoIndex from "../components/toDos/ToDoIndex";
|
import ToDoIndex from "../components/toDos/ToDoIndex"
|
||||||
import ToDoFinish from "../components/toDos/ToDoFinish";
|
import ToDoFinish from "../components/toDos/ToDoFinish"
|
||||||
|
|
||||||
const Home = () => {
|
const Home = () => {
|
||||||
|
|
||||||
|
|||||||
@@ -1,19 +1,19 @@
|
|||||||
import React from "react";
|
import React from "react"
|
||||||
import {BrowserRouter, Route, Routes} from "react-router-dom";
|
import {BrowserRouter, Route, Routes} from "react-router-dom"
|
||||||
import {Suspense} from "react";
|
import {Suspense} from "react"
|
||||||
import Profile from "./Auth/Profile";
|
import Profile from "./Auth/Profile"
|
||||||
import Login from "./Auth/Login";
|
import Login from "./Auth/Login"
|
||||||
import Header from "../components/Header";
|
import Header from "../components/Header"
|
||||||
import Home from "./Home";
|
import Home from "./Home"
|
||||||
import useAuthUser from "../hooks/AuthUser";
|
import useAuthUser from "../hooks/AuthUser"
|
||||||
import Register from "./Auth/Register";
|
import Register from "./Auth/Register"
|
||||||
import ToDoShow from "../components/toDos/ToDoShow";
|
import ToDoShow from "./ToDos/ToDoShow"
|
||||||
import TimeTrackersIndex from "./TimeTrackersIndex";
|
import TimeTrackersIndex from "./TimeTrackersIndex"
|
||||||
|
|
||||||
const Router = () => {
|
const Router = () => {
|
||||||
|
|
||||||
console.log('router')
|
console.log('router')
|
||||||
const {authUser, loadingAuthUser, logout} = useAuthUser()
|
const {loadingAuthUser} = useAuthUser()
|
||||||
|
|
||||||
return <>
|
return <>
|
||||||
{loadingAuthUser ? '...loading'
|
{loadingAuthUser ? '...loading'
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
import React, {useEffect, useState} from "react"
|
import React, {useEffect, useState} from "react"
|
||||||
import useAxiosTools from "../hooks/AxiosTools";
|
import useAxiosTools from "../hooks/AxiosTools"
|
||||||
import {timeTracker, toDo} from "../utilities/types";
|
import {timeTracker} from "../utilities/types"
|
||||||
import {PlaySVG} from "../components/SVG";
|
import {EditSVG, PlaySVG} from "../components/SVG"
|
||||||
import useTracker from "../hooks/TraskerHook";
|
import useTracker from "../hooks/TraskerHook"
|
||||||
import {Modal} from "../components/Modals";
|
import {Modal} from "../components/Modals"
|
||||||
import TimeTrackerEdit from "../components/TimeTrackers/TimeTrackerEdit";
|
import TimeTrackerEdit from "../components/TimeTrackers/TimeTrackerEdit"
|
||||||
|
import {Link} from "react-router-dom"
|
||||||
|
|
||||||
const TimeTrackersIndex = () => {
|
const TimeTrackersIndex = () => {
|
||||||
|
|
||||||
const {loading, setLoading, errorCatch, errorLabel, axiosGet, axiosPut} = useAxiosTools(true)
|
const {loading, setLoading, errorCatch, errorLabel, axiosGet} = useAxiosTools(true)
|
||||||
const [timeTrackers, setTimeTrackers] = useState<timeTracker[]>([])
|
const [timeTrackers, setTimeTrackers] = useState<timeTracker[]>([])
|
||||||
const [showTrackers, setShowTrackers] = useState<timeTracker|null>(null)
|
const [showTrackers, setShowTrackers] = useState<timeTracker|null>(null)
|
||||||
const {startTrackToDo} = useTracker()
|
const {startTrackToDo} = useTracker()
|
||||||
@@ -31,19 +32,19 @@ const TimeTrackersIndex = () => {
|
|||||||
return <div className="p-5">
|
return <div className="p-5">
|
||||||
{errorLabel()}
|
{errorLabel()}
|
||||||
<ul>
|
<ul>
|
||||||
{timeTrackers.map(tracker => <li key={tracker.id} className="flex justify-between gap-5">
|
{timeTrackers.map(tracker => <li key={tracker.id} className="flex justify-between gap-5 odd:bg-blue-950">
|
||||||
<span className="text-center w-36">{tracker.start_at ? (new Date(tracker.start_at)).toSmallFrDate() : ''}</span>
|
<span className="w-24 text-center">{tracker.start_at ? (new Date(tracker.start_at)).toSmallFrDate() : ''}</span>
|
||||||
<span className="text-center w-36">{(new Date(tracker.end_at)).toSmallFrDate()}</span>
|
<span className="w-24 text-center">{(new Date(tracker.end_at)).toSmallFrDate()}</span>
|
||||||
<span className={`flex-1 ${tracker.to_do?.checked ? 'line-through' : ''}`}>{tracker.to_do.name}</span>
|
<Link to={"/todos/" + tracker.to_do?.id} className={`flex-1 ${tracker.to_do?.checked ? 'line-through' : ''}`}>{tracker.to_do.name}</Link>
|
||||||
<span className="flex gap-2">
|
<span className="flex gap-2">
|
||||||
{!tracker?.to_do?.checked && <button className="cursor-pointer w-7 justify-center flex items-center"
|
{!tracker?.to_do?.checked && <button className="flex w-7 cursor-pointer items-center justify-center"
|
||||||
type="button"
|
type="button"
|
||||||
title="Commencer"
|
title="Commencer"
|
||||||
onClick={() => startTrackToDo(tracker.to_do)}>
|
onClick={() => startTrackToDo(tracker.to_do)}>
|
||||||
<PlaySVG className="w-4"/>
|
<PlaySVG className="w-4"/>
|
||||||
</button>}
|
</button>}
|
||||||
<button onClick={() => setShowTrackers(tracker)}>
|
<button onClick={() => setShowTrackers(tracker)}>
|
||||||
Edit
|
<EditSVG className="w-5"/>
|
||||||
</button>
|
</button>
|
||||||
</span>
|
</span>
|
||||||
</li>)}
|
</li>)}
|
||||||
|
|||||||
131
resources/js/pages/ToDos/ToDoShow.tsx
Normal file
131
resources/js/pages/ToDos/ToDoShow.tsx
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
import React, {FC, useEffect, useState} from "react"
|
||||||
|
import {useParams} from "react-router-dom"
|
||||||
|
import useAxiosTools from "../../hooks/AxiosTools"
|
||||||
|
import {timeTracker, toDo} from "../../utilities/types"
|
||||||
|
import {EditSVG} from "../../components/SVG"
|
||||||
|
import Field, {TextArea} from "../../components/Field"
|
||||||
|
|
||||||
|
const ToDoShow = () => {
|
||||||
|
|
||||||
|
const {id} = useParams()
|
||||||
|
const {setLoading, errorCatch, errorLabel, axiosGet, axiosPut} = useAxiosTools(true)
|
||||||
|
const [toDo, setToDo] = useState<toDo|null>(null)
|
||||||
|
const [editMode, setEditMode] = useState(false)
|
||||||
|
const [editForm, setEditForm] = useState<toDo|null>(null)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetchToDo()
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const fetchToDo = async () => {
|
||||||
|
try {
|
||||||
|
const res = await axiosGet('/api/todos/' + id)
|
||||||
|
setToDo(res.data)
|
||||||
|
} catch (error) {
|
||||||
|
errorCatch(error)
|
||||||
|
} finally {
|
||||||
|
setLoading(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleEditTodoMode = async () => {
|
||||||
|
if (editMode) {
|
||||||
|
try {
|
||||||
|
const res = await axiosPut('/api/todos/' + id, editForm)
|
||||||
|
setToDo(res.data)
|
||||||
|
} catch (error) {
|
||||||
|
errorCatch(error)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setEditForm(toDo)
|
||||||
|
}
|
||||||
|
setEditMode(!editMode)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleEditTodo = (event: React.ChangeEvent<HTMLInputElement|HTMLTextAreaElement>) => {
|
||||||
|
setEditForm({...editForm, [event.target.name]: event.target.value})
|
||||||
|
}
|
||||||
|
|
||||||
|
return <div className="relative p-5">
|
||||||
|
{errorLabel()}
|
||||||
|
{editMode ? <>
|
||||||
|
<button className="absolute right-5 flex" onClick={handleEditTodoMode}>Valider <EditSVG className="w-5"/></button>
|
||||||
|
<Field name="name" value={editForm?.name} onChange={handleEditTodo} />
|
||||||
|
<TextArea name="description" value={editForm?.description} onChange={handleEditTodo} />
|
||||||
|
</> : <>
|
||||||
|
<button className="absolute right-5" onClick={handleEditTodoMode}><EditSVG className="w-5"/></button>
|
||||||
|
<h1 className="text-lg font-bold">{toDo?.name}</h1>
|
||||||
|
<p>{toDo?.description}</p>
|
||||||
|
</>}
|
||||||
|
|
||||||
|
|
||||||
|
{toDo && <ToDoTimeTrackers toDo={toDo}/>}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ToDoShow
|
||||||
|
|
||||||
|
const ToDoTimeTrackers: FC<ToDoTimeTrackers> = ({toDo: toDo}) => {
|
||||||
|
|
||||||
|
const {setLoading, errorCatch, errorLabel, axiosGet} = useAxiosTools(true)
|
||||||
|
const [timeTrackers, setTimeTrackers] = useState<timeTracker[]>([])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetchTimeTrackers()
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const fetchTimeTrackers = async () => {
|
||||||
|
try {
|
||||||
|
const res = await axiosGet(`/api/todos/${toDo.id}/time-trackers`)
|
||||||
|
setTimeTrackers(res.data)
|
||||||
|
} catch (error) {
|
||||||
|
errorCatch(error)
|
||||||
|
} finally {
|
||||||
|
setLoading(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeSpend = () => {
|
||||||
|
let timer = 0
|
||||||
|
let more = false
|
||||||
|
timeTrackers.forEach(timeTracker => {
|
||||||
|
if (! timeTracker.end_at) {
|
||||||
|
more = true
|
||||||
|
} else {
|
||||||
|
timer += ((new Date(timeTracker.end_at)).getTime()) - (new Date(timeTracker.start_at)).getTime()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
timer = Math.floor(timer / 1000)
|
||||||
|
return (more ? '+' : '') + timer.durationify()
|
||||||
|
// let hours = Math.floor(timer / 3600)
|
||||||
|
// let minutes = Math.floor((timer - hours * 3600) / 60)
|
||||||
|
// let secondes = timer - hours * 3600 - minutes * 60
|
||||||
|
// return `${more ? '+' : ''} ${hours}:${String(minutes).padStart(2, '0')}:${String(secondes).padStart(2, '0')}`
|
||||||
|
}
|
||||||
|
|
||||||
|
return <div className="p-5">
|
||||||
|
<div className="text-center">Temps passé : {timeSpend()}</div>
|
||||||
|
{errorLabel()}
|
||||||
|
<table className="mx-auto">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Début</th>
|
||||||
|
<th>Fin</th>
|
||||||
|
<th>Différence</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{timeTrackers.map(timeTracker => <tr key={timeTracker.id} className="text-center">
|
||||||
|
<td className="px-1">{timeTracker.start_at ? (new Date(timeTracker.start_at)).toSmallFrDate() : ''}</td>
|
||||||
|
<td className="px-1">{timeTracker.end_at ? (new Date(timeTracker.end_at)).toSmallFrDate() : ''}</td>
|
||||||
|
<td className="px-1 text-right">{timeTracker.start_at && timeTracker.end_at ? (new Date(timeTracker.end_at)).difference(new Date(timeTracker.start_at)) : ''}</td>
|
||||||
|
</tr>)}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ToDoTimeTrackers {
|
||||||
|
toDo: toDo,
|
||||||
|
}
|
||||||
@@ -57,15 +57,9 @@ Date.prototype.difference = function (start_at) {
|
|||||||
if (!this) {
|
if (!this) {
|
||||||
return '--:--'
|
return '--:--'
|
||||||
}
|
}
|
||||||
let end_at = this
|
const timer = Math.floor(((!start_at ? (new Date()) : this).getTime() - (!start_at ? this : start_at).getTime()) / 1000)
|
||||||
if (!start_at) {
|
const hours = Math.floor(timer / 3600)
|
||||||
end_at = new Date()
|
const minutes = Math.floor((timer - hours * 3600) / 60)
|
||||||
start_at = this
|
const secondes = timer - hours * 3600 - minutes * 60
|
||||||
}
|
|
||||||
let timer = Math.floor((end_at.getTime() - start_at.getTime()) / 1000)
|
|
||||||
let hours = Math.floor(timer / 3600)
|
|
||||||
let minutes = Math.floor((timer - hours * 3600) / 60)
|
|
||||||
let secondes = timer - hours * 3600 - minutes * 60
|
|
||||||
return `${hours}:${String(minutes).padStart(2, '0')}:${String(secondes).padStart(2, '0')}`
|
return `${hours}:${String(minutes).padStart(2, '0')}:${String(secondes).padStart(2, '0')}`
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,4 +10,8 @@ export default defineConfig({
|
|||||||
}),
|
}),
|
||||||
react(),
|
react(),
|
||||||
],
|
],
|
||||||
|
esbuild: {
|
||||||
|
jsxFactory: 'h',
|
||||||
|
jsxFragment: 'Fragment',
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user