mirror of
https://github.com/solidtime-io/solidtime.git
synced 2026-08-19 05:32:16 +01:00
Compare commits
6 Commits
6d7d6d46e0
...
b51c9c0f3b
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b51c9c0f3b | ||
|
|
f0de89b0e8 | ||
|
|
024283273a | ||
|
|
f01ab5fd9d | ||
|
|
4fb18f343f | ||
|
|
ba374c0371 |
@@ -66,7 +66,7 @@ class UserResource extends Resource
|
|||||||
->ignore($record?->getKey()),
|
->ignore($record?->getKey()),
|
||||||
])
|
])
|
||||||
->rule([
|
->rule([
|
||||||
'email',
|
'email:rfc,strict',
|
||||||
])
|
])
|
||||||
->maxLength(255),
|
->maxLength(255),
|
||||||
Forms\Components\Toggle::make('is_placeholder')
|
Forms\Components\Toggle::make('is_placeholder')
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ class InvitationStoreRequest extends BaseFormRequest
|
|||||||
return [
|
return [
|
||||||
'email' => [
|
'email' => [
|
||||||
'required',
|
'required',
|
||||||
'email',
|
'email:rfc,strict',
|
||||||
],
|
],
|
||||||
'role' => [
|
'role' => [
|
||||||
'required',
|
'required',
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ class UserUpdateRequest extends BaseFormRequest
|
|||||||
'max:255',
|
'max:255',
|
||||||
],
|
],
|
||||||
'email' => [
|
'email' => [
|
||||||
'email',
|
'email:rfc,strict',
|
||||||
'max:255',
|
'max:255',
|
||||||
UniqueEloquent::make(User::class, 'email')->ignore($this->user->id)->query(function (Builder $query) {
|
UniqueEloquent::make(User::class, 'email')->ignore($this->user->id)->query(function (Builder $query) {
|
||||||
/** @var Builder<User> $query */
|
/** @var Builder<User> $query */
|
||||||
|
|||||||
@@ -20,6 +20,11 @@ class RecalculateSpentTimeForProject implements ShouldDispatchAfterCommit, Shoul
|
|||||||
use Queueable;
|
use Queueable;
|
||||||
use SerializesModels;
|
use SerializesModels;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete the job if its models no longer exist.
|
||||||
|
*/
|
||||||
|
public bool $deleteWhenMissingModels = true;
|
||||||
|
|
||||||
public Project $project;
|
public Project $project;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -20,6 +20,11 @@ class RecalculateSpentTimeForTask implements ShouldDispatchAfterCommit, ShouldQu
|
|||||||
use Queueable;
|
use Queueable;
|
||||||
use SerializesModels;
|
use SerializesModels;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete the job if its models no longer exist.
|
||||||
|
*/
|
||||||
|
public bool $deleteWhenMissingModels = true;
|
||||||
|
|
||||||
public Task $task;
|
public Task $task;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -29,7 +29,8 @@ class ClockifyProjectsImporter extends DefaultImporter
|
|||||||
$records = $reader->getRecords();
|
$records = $reader->getRecords();
|
||||||
foreach ($records as $record) {
|
foreach ($records as $record) {
|
||||||
$clientId = null;
|
$clientId = null;
|
||||||
if ($record['Client'] !== '') {
|
// Newer Clockify exports no longer contain a "Client" column.
|
||||||
|
if (($record['Client'] ?? '') !== '') {
|
||||||
$clientId = $this->clientImportHelper->getKey([
|
$clientId = $this->clientImportHelper->getKey([
|
||||||
'name' => $record['Client'],
|
'name' => $record['Client'],
|
||||||
'organization_id' => $this->organization->id,
|
'organization_id' => $this->organization->id,
|
||||||
@@ -45,7 +46,7 @@ class ClockifyProjectsImporter extends DefaultImporter
|
|||||||
'color' => $this->colorService->getRandomColor(),
|
'color' => $this->colorService->getRandomColor(),
|
||||||
'is_billable' => $record['Billability'] === 'Yes',
|
'is_billable' => $record['Billability'] === 'Yes',
|
||||||
'billable_rate' => $billableRateKey !== null && $record[$billableRateKey] !== '' ? (int) (((float) $record[$billableRateKey]) * 100) : null,
|
'billable_rate' => $billableRateKey !== null && $record[$billableRateKey] !== '' ? (int) (((float) $record[$billableRateKey]) * 100) : null,
|
||||||
'estimated_time' => $record['Estimated (h)'] !== '' && is_numeric($record['Estimated (h)']) ? (int) ($record['Estimated (h)'] * 3600) : null,
|
'estimated_time' => isset($record['Estimated (h)']) && is_numeric($record['Estimated (h)']) ? (int) ($record['Estimated (h)'] * 3600) : null,
|
||||||
'archived_at' => $record['Status'] === 'Archived' ? Carbon::now() : null,
|
'archived_at' => $record['Status'] === 'Archived' ? Carbon::now() : null,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
@@ -80,7 +81,6 @@ class ClockifyProjectsImporter extends DefaultImporter
|
|||||||
{
|
{
|
||||||
$requiredFields = [
|
$requiredFields = [
|
||||||
'Project',
|
'Project',
|
||||||
'Client',
|
|
||||||
'Status',
|
'Status',
|
||||||
'Visibility',
|
'Visibility',
|
||||||
'Billability',
|
'Billability',
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ class ClockifyTimeEntriesImporter extends DefaultImporter
|
|||||||
]);
|
]);
|
||||||
$member = $this->memberImportHelper->getModelById($memberId);
|
$member = $this->memberImportHelper->getModelById($memberId);
|
||||||
$clientId = null;
|
$clientId = null;
|
||||||
if ($record['Client'] !== '') {
|
if (($record['Client'] ?? '') !== '') {
|
||||||
$clientId = $this->clientImportHelper->getKey([
|
$clientId = $this->clientImportHelper->getKey([
|
||||||
'name' => $record['Client'],
|
'name' => $record['Client'],
|
||||||
'organization_id' => $this->organization->id,
|
'organization_id' => $this->organization->id,
|
||||||
@@ -215,7 +215,6 @@ class ClockifyTimeEntriesImporter extends DefaultImporter
|
|||||||
{
|
{
|
||||||
$requiredFields = [
|
$requiredFields = [
|
||||||
'Project',
|
'Project',
|
||||||
'Client',
|
|
||||||
'Description',
|
'Description',
|
||||||
'User',
|
'User',
|
||||||
'Group',
|
'Group',
|
||||||
|
|||||||
@@ -171,7 +171,7 @@ abstract class DefaultImporter implements ImporterContract
|
|||||||
}, validate: [
|
}, validate: [
|
||||||
'email' => [
|
'email' => [
|
||||||
'required',
|
'required',
|
||||||
'email',
|
'email:rfc,strict',
|
||||||
'max:255',
|
'max:255',
|
||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
|
|||||||
114
package-lock.json
generated
114
package-lock.json
generated
@@ -67,7 +67,7 @@
|
|||||||
"typescript": "^5.9.3",
|
"typescript": "^5.9.3",
|
||||||
"vite": "^7.3.3",
|
"vite": "^7.3.3",
|
||||||
"vite-plugin-checker": "^0.14.4",
|
"vite-plugin-checker": "^0.14.4",
|
||||||
"vitest": "^4.1.9",
|
"vitest": "^4.1.10",
|
||||||
"vue": "^3.5.39",
|
"vue": "^3.5.39",
|
||||||
"vue-tsc": "^3.3.6"
|
"vue-tsc": "^3.3.6"
|
||||||
}
|
}
|
||||||
@@ -2796,16 +2796,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vitest/expect": {
|
"node_modules/@vitest/expect": {
|
||||||
"version": "4.1.9",
|
"version": "4.1.10",
|
||||||
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.9.tgz",
|
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.10.tgz",
|
||||||
"integrity": "sha512-vl/rYsUKcBr3SnQn166+XR5ZQcgMx3DQhFWdfli/cWpLnLUmbxZvyrJZotLFUryib+LtArYMSTJ5RbQ57ZqrlA==",
|
"integrity": "sha512-YsCn+qAk1GWjQOWFEsEcL2gNQ0zmVmQu3T03qP6UyjhtmdtwtbuI+DASn/7iQB3HGTXkdBwGddzxPlmiql5vlA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@standard-schema/spec": "^1.1.0",
|
"@standard-schema/spec": "^1.1.0",
|
||||||
"@types/chai": "^5.2.2",
|
"@types/chai": "^5.2.2",
|
||||||
"@vitest/spy": "4.1.9",
|
"@vitest/spy": "4.1.10",
|
||||||
"@vitest/utils": "4.1.9",
|
"@vitest/utils": "4.1.10",
|
||||||
"chai": "^6.2.2",
|
"chai": "^6.2.2",
|
||||||
"tinyrainbow": "^3.1.0"
|
"tinyrainbow": "^3.1.0"
|
||||||
},
|
},
|
||||||
@@ -2814,13 +2814,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vitest/mocker": {
|
"node_modules/@vitest/mocker": {
|
||||||
"version": "4.1.9",
|
"version": "4.1.10",
|
||||||
"resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.9.tgz",
|
"resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.10.tgz",
|
||||||
"integrity": "sha512-EVkXzBjrPGM+cK8/ANWgBrkUCfJfb38/EfTSO8h7pWvKkyPkpWxvR7BkD2MyItMF62C97zAEoqdpUixwR/e+Rw==",
|
"integrity": "sha512-v0xaezt+DKEmKfaxg133ldzADrwLGd7Ze1MfQQTYfvs8OqZIwbxyxaYURivwV7sWy5fqn3rH5uOrSp07bp44Ow==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vitest/spy": "4.1.9",
|
"@vitest/spy": "4.1.10",
|
||||||
"estree-walker": "^3.0.3",
|
"estree-walker": "^3.0.3",
|
||||||
"magic-string": "^0.30.21"
|
"magic-string": "^0.30.21"
|
||||||
},
|
},
|
||||||
@@ -2851,9 +2851,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vitest/pretty-format": {
|
"node_modules/@vitest/pretty-format": {
|
||||||
"version": "4.1.9",
|
"version": "4.1.10",
|
||||||
"resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.9.tgz",
|
"resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.10.tgz",
|
||||||
"integrity": "sha512-s0iufns3iIFitdgm+YR7g1whCAaGtXz459VS9/PqyKDEEFgYIhsHOQmXgIgDuYCt7DeQmiZT0Qe2OA2p4ZPu5A==",
|
"integrity": "sha512-W1HsjSH4MXQ9YfmmhLAoIYf1HRfekQCGngeIgcei6MP5QQGWUe0gkopdZQaVCFO+JDJMrAJGwa5pRpNpvy4P8Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -2864,13 +2864,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vitest/runner": {
|
"node_modules/@vitest/runner": {
|
||||||
"version": "4.1.9",
|
"version": "4.1.10",
|
||||||
"resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.9.tgz",
|
"resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.10.tgz",
|
||||||
"integrity": "sha512-KXLMDtc7oe70+3mJfGrPUWPesswH+3sTxAMAMl8DG7I8IUQT4XW718dY5ID3vPUcmlu27CcKfY4P3h3I29SLJg==",
|
"integrity": "sha512-IKI6kpIH+LmpROplyLwBBaCfMgOZOMsygVa6BARD6ahA04VRuJSa6OaVG7kRvSEMD870Vd91rSSw0eegtWyLGg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vitest/utils": "4.1.9",
|
"@vitest/utils": "4.1.10",
|
||||||
"pathe": "^2.0.3"
|
"pathe": "^2.0.3"
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
@@ -2878,14 +2878,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vitest/snapshot": {
|
"node_modules/@vitest/snapshot": {
|
||||||
"version": "4.1.9",
|
"version": "4.1.10",
|
||||||
"resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.9.tgz",
|
"resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.10.tgz",
|
||||||
"integrity": "sha512-Jc7RKGNBo8Z28WYIm0Niej4xdSPByRf6mU58VpHQkd6Zh05rlnA+twjbK5HyeIGHxrzsc3mJgS43uM0CZKzaIA==",
|
"integrity": "sha512-xRkfOT1qpTAi/Ti4Y1LtfRc3kEuqxGw59eN2jN9pRWMtS/XDevekhcFSqvQqjUNGksfjMJu3Y+oJ+4Ypn2OaJw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vitest/pretty-format": "4.1.9",
|
"@vitest/pretty-format": "4.1.10",
|
||||||
"@vitest/utils": "4.1.9",
|
"@vitest/utils": "4.1.10",
|
||||||
"magic-string": "^0.30.21",
|
"magic-string": "^0.30.21",
|
||||||
"pathe": "^2.0.3"
|
"pathe": "^2.0.3"
|
||||||
},
|
},
|
||||||
@@ -2894,9 +2894,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vitest/spy": {
|
"node_modules/@vitest/spy": {
|
||||||
"version": "4.1.9",
|
"version": "4.1.10",
|
||||||
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.9.tgz",
|
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.10.tgz",
|
||||||
"integrity": "sha512-fHpsS6mIi+PiEW+vcRVOMkX1oSaPKne3VOclSFICPcGOmfKgXPU5iAah+wcNcj2xPrCCmfq99IDGf+EojhhvhA==",
|
"integrity": "sha512-PLf/Ugvoq5wO/b4rwYCR1h2PSIdXz7wnkQFMiUpLdtM7l6pqVFcQIBEHyT1+l+cj7mNwAfZHzqXqDyjvOuwbDw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
@@ -2904,13 +2904,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vitest/utils": {
|
"node_modules/@vitest/utils": {
|
||||||
"version": "4.1.9",
|
"version": "4.1.10",
|
||||||
"resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.9.tgz",
|
"resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.10.tgz",
|
||||||
"integrity": "sha512-A51o8ymO5PpqlWNnBP9ZHPXDIpuMtTLlGSjN7la4US+LJzoUMyhwjA5QXlm39JexgwHKW4Xjs8Z2d3dLCXOeuA==",
|
"integrity": "sha512-fy9am/HWxbaGt/Sawrp90vt6Y6jQwf1RX77cz3uwoJwJVMli/e1IEwRPnMNJ7vKfPTwo0diXifkpPvwH9v7nGA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vitest/pretty-format": "4.1.9",
|
"@vitest/pretty-format": "4.1.10",
|
||||||
"convert-source-map": "^2.0.0",
|
"convert-source-map": "^2.0.0",
|
||||||
"tinyrainbow": "^3.1.0"
|
"tinyrainbow": "^3.1.0"
|
||||||
},
|
},
|
||||||
@@ -3207,9 +3207,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vue/language-core/node_modules/picomatch": {
|
"node_modules/@vue/language-core/node_modules/picomatch": {
|
||||||
"version": "4.0.4",
|
"version": "4.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz",
|
||||||
"integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
|
"integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
@@ -3696,9 +3696,9 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/baseline-browser-mapping": {
|
"node_modules/baseline-browser-mapping": {
|
||||||
"version": "2.10.40",
|
"version": "2.10.42",
|
||||||
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.40.tgz",
|
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.42.tgz",
|
||||||
"integrity": "sha512-BSSLZ9/Cjjv7Gtj5B68ZzXcXUg8iOf3fme+FCuh8rC/Go+Kmh8cox7M3A8dolou16s64QjLPOSdngh7GxXvkSw==",
|
"integrity": "sha512-c/jurFrDLyui7o1J86yLkRu4LMsTYcBohveus7/I2Hzdn9KIP2bdJPTue/lR1KH46enoPbD77GKeSYNdyPoD3Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"bin": {
|
"bin": {
|
||||||
@@ -3871,9 +3871,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/caniuse-lite": {
|
"node_modules/caniuse-lite": {
|
||||||
"version": "1.0.30001800",
|
"version": "1.0.30001802",
|
||||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001800.tgz",
|
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001802.tgz",
|
||||||
"integrity": "sha512-MMHtuAz9Ys840zAY5F4k6fV5GaivZ9sPk+nz0mY+GYVzRBnYkN0mpqkSR92oWRQ19yQWo4HvBV/FnC16AJX8MA==",
|
"integrity": "sha512-vmv8ub2xwTNmljSKf82mtCk5JH7hC+YgzLj3P5zotvA0tPQ9016tdNNOG8WRca1IxOnhSsivB+J0z5FeE5LOUw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -4266,9 +4266,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/electron-to-chromium": {
|
"node_modules/electron-to-chromium": {
|
||||||
"version": "1.5.384",
|
"version": "1.5.387",
|
||||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.384.tgz",
|
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.387.tgz",
|
||||||
"integrity": "sha512-g6KAKY1vkYsADvSPWvdJsuYT0ixdcu6lUtD9P/wJKGBEDlZVXh2AX42j1mPqqaQPDluWjara9ziQ7xqAeXCt5A==",
|
"integrity": "sha512-TaxwufTFDufvPEoXdhwVrA3UdFWBeWGkYoJ1K8ldF1xe6gKfth6iRNS5lTQ5JPNOHdGQm8PT1QYKUqFLCiUefQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
@@ -8130,19 +8130,19 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/vitest": {
|
"node_modules/vitest": {
|
||||||
"version": "4.1.9",
|
"version": "4.1.10",
|
||||||
"resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.9.tgz",
|
"resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.10.tgz",
|
||||||
"integrity": "sha512-nE3/LEyc0z87uHYLZebqCUOaJr2hdtuPp7BQ4BosVFnfltxgAvMG08NyrSGlPpOUWvR27c5flSmYFTNr78L9GQ==",
|
"integrity": "sha512-R9jUTe5S4Qb0HCd4TNqpC7oGcrMssMRGXLW80ubjWsW9VH5GF8y1Y0SFLY9AbqSk6nt0PnOx4H4WNJYZ13GUPw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vitest/expect": "4.1.9",
|
"@vitest/expect": "4.1.10",
|
||||||
"@vitest/mocker": "4.1.9",
|
"@vitest/mocker": "4.1.10",
|
||||||
"@vitest/pretty-format": "4.1.9",
|
"@vitest/pretty-format": "4.1.10",
|
||||||
"@vitest/runner": "4.1.9",
|
"@vitest/runner": "4.1.10",
|
||||||
"@vitest/snapshot": "4.1.9",
|
"@vitest/snapshot": "4.1.10",
|
||||||
"@vitest/spy": "4.1.9",
|
"@vitest/spy": "4.1.10",
|
||||||
"@vitest/utils": "4.1.9",
|
"@vitest/utils": "4.1.10",
|
||||||
"es-module-lexer": "^2.0.0",
|
"es-module-lexer": "^2.0.0",
|
||||||
"expect-type": "^1.3.0",
|
"expect-type": "^1.3.0",
|
||||||
"magic-string": "^0.30.21",
|
"magic-string": "^0.30.21",
|
||||||
@@ -8170,12 +8170,12 @@
|
|||||||
"@edge-runtime/vm": "*",
|
"@edge-runtime/vm": "*",
|
||||||
"@opentelemetry/api": "^1.9.0",
|
"@opentelemetry/api": "^1.9.0",
|
||||||
"@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0",
|
"@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0",
|
||||||
"@vitest/browser-playwright": "4.1.9",
|
"@vitest/browser-playwright": "4.1.10",
|
||||||
"@vitest/browser-preview": "4.1.9",
|
"@vitest/browser-preview": "4.1.10",
|
||||||
"@vitest/browser-webdriverio": "4.1.9",
|
"@vitest/browser-webdriverio": "4.1.10",
|
||||||
"@vitest/coverage-istanbul": "4.1.9",
|
"@vitest/coverage-istanbul": "4.1.10",
|
||||||
"@vitest/coverage-v8": "4.1.9",
|
"@vitest/coverage-v8": "4.1.10",
|
||||||
"@vitest/ui": "4.1.9",
|
"@vitest/ui": "4.1.10",
|
||||||
"happy-dom": "*",
|
"happy-dom": "*",
|
||||||
"jsdom": "*",
|
"jsdom": "*",
|
||||||
"vite": "^6.0.0 || ^7.0.0 || ^8.0.0"
|
"vite": "^6.0.0 || ^7.0.0 || ^8.0.0"
|
||||||
|
|||||||
@@ -49,7 +49,7 @@
|
|||||||
"typescript": "^5.9.3",
|
"typescript": "^5.9.3",
|
||||||
"vite": "^7.3.3",
|
"vite": "^7.3.3",
|
||||||
"vite-plugin-checker": "^0.14.4",
|
"vite-plugin-checker": "^0.14.4",
|
||||||
"vitest": "^4.1.9",
|
"vitest": "^4.1.10",
|
||||||
"vue": "^3.5.39",
|
"vue": "^3.5.39",
|
||||||
"vue-tsc": "^3.3.6"
|
"vue-tsc": "^3.3.6"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -527,6 +527,11 @@ const selectedProjectColor = computed(() => {
|
|||||||
return currentProject.value?.color || 'var(--theme-color-icon-default)';
|
return currentProject.value?.color || 'var(--theme-color-icon-default)';
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const selectedClientName = computed(() => {
|
||||||
|
const clientId = currentProject.value?.client_id;
|
||||||
|
return clientId ? clientsById.value.get(clientId)?.name : undefined;
|
||||||
|
});
|
||||||
|
|
||||||
// This state prevents the selection to jump to random items when the mouse cursor is
|
// This state prevents the selection to jump to random items when the mouse cursor is
|
||||||
// over an item and some Item in the Dropdown is selected by keyboard navigation to scroll into view
|
// over an item and some Item in the Dropdown is selected by keyboard navigation to scroll into view
|
||||||
const mouseEnterHighlightActivated = ref(true);
|
const mouseEnterHighlightActivated = ref(true);
|
||||||
@@ -572,15 +577,24 @@ const showCreateProject = ref(false);
|
|||||||
:size="props.size"
|
:size="props.size"
|
||||||
:class="twMerge('w-full justify-start overflow-hidden', props.class)">
|
:class="twMerge('w-full justify-start overflow-hidden', props.class)">
|
||||||
<div
|
<div
|
||||||
class="w-3 h-3 rounded-full shrink-0"
|
class="w-2.5 h-2.5 rounded-full shrink-0"
|
||||||
:style="{ backgroundColor: selectedProjectColor }"></div>
|
:style="{ backgroundColor: selectedProjectColor }"></div>
|
||||||
<span class="truncate shrink-[1] text-text-primary pr-1">{{
|
<span class="truncate shrink-[1] text-text-primary">{{
|
||||||
selectedProjectName
|
selectedProjectName
|
||||||
}}</span>
|
}}</span>
|
||||||
<template v-if="currentTask">
|
<template v-if="currentTask">
|
||||||
<ChevronRightIcon class="w-4 h-4 text-text-tertiary shrink-0" />
|
<ChevronRightIcon class="!size-3 text-text-primary shrink-0 -mx-1" />
|
||||||
<span class="truncate shrink-[100]">{{ currentTask.name }}</span>
|
<span class="truncate shrink-[100]">{{ currentTask.name }}</span>
|
||||||
</template>
|
</template>
|
||||||
|
<template v-if="selectedClientName">
|
||||||
|
<span class="dark:text-text-tertiary text-text-quaternary shrink-0"
|
||||||
|
>•</span
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="truncate shrink-[200] dark:text-text-tertiary text-text-quaternary"
|
||||||
|
>{{ selectedClientName }}</span
|
||||||
|
>
|
||||||
|
</template>
|
||||||
</Button>
|
</Button>
|
||||||
<button
|
<button
|
||||||
v-if="allowReset && project !== null"
|
v-if="allowReset && project !== null"
|
||||||
|
|||||||
2
resources/testfiles/clockify_projects_import_test_4.csv
Normal file
2
resources/testfiles/clockify_projects_import_test_4.csv
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
"Project","Status","Visibility","Billability","Tasks","Tracked (h)","Estimated (h)","Remaining (h)","Overage (h)","Progress(%)","Billable (h)","Non-billable (h)","Billable Rate (USD)","Amount (USD)","Project members","Project manager","Note"
|
||||||
|
"Project Without Client Column","Active","Public","Yes","Task 1, Task 2","0.00","100.00","","","","0.00","0.00","100.01","0.00","Constantin Graf","",""
|
||||||
|
@@ -0,0 +1,3 @@
|
|||||||
|
"Project","Description","Task","User","Group","Email","Tags","Billable","Start Date","Start Time","End Date","End Time","Duration (h)","Duration (decimal)","Billable Rate (USD)","Billable Amount (USD)"
|
||||||
|
"Project A","","","Peter Tester","","peter.test@email.test","Development, Backend","No","03/04/2024","10:23:52 AM","03/04/2024","10:23:52 AM","00:00:00","0.00","0.00","0.00"
|
||||||
|
"Project B","Working hard","Task 1","Peter Tester","","peter.test@email.test","","Yes","03/04/2024","10:23 AM","03/04/2024","11:23:01 AM","01:00:01","0.00","0.00","0.00"
|
||||||
|
@@ -0,0 +1,2 @@
|
|||||||
|
"Project","Description","Task","User","Group","Email","Tags","Billable","Start Date","Start Time","End Date","End Time","Client"
|
||||||
|
"Project A","Working hard","Task 1","Peter Tester","","peter.test@email.test","","Yes","03/04/2024","10:23 AM","03/04/2024","11:23:01 AM"
|
||||||
|
@@ -5,7 +5,7 @@
|
|||||||
{{ __('Your currently running time entry ":description" is now running for more than 8 hours!', ['description' => $timeEntry->description]) }}
|
{{ __('Your currently running time entry ":description" is now running for more than 8 hours!', ['description' => $timeEntry->description]) }}
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
{{ __('If you forgot to stop the Time Tracker you do that in solidtime:') }}
|
{{ __('If you forgot to stop the Time Tracker, you can do so in solidtime:') }}
|
||||||
|
|
||||||
@component('mail::button', ['url' => $dashboardUrl])
|
@component('mail::button', ['url' => $dashboardUrl])
|
||||||
{{ __('Go to solidtime') }}
|
{{ __('Go to solidtime') }}
|
||||||
|
|||||||
@@ -96,6 +96,29 @@ class ClockifyProjectsImporterTest extends ImporterTestAbstract
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function test_import_of_test_file_without_client_column_succeeds(): void
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
$organization = Organization::factory()->create();
|
||||||
|
$timezone = 'Europe/Vienna';
|
||||||
|
$importer = new ClockifyProjectsImporter;
|
||||||
|
$importer->init($organization);
|
||||||
|
// Newer Clockify exports no longer contain a "Client" column.
|
||||||
|
$data = Storage::disk('testfiles')->get('clockify_projects_import_test_4.csv');
|
||||||
|
|
||||||
|
// Act
|
||||||
|
$importer->importData($data, $timezone);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
$project = Project::query()->where('organization_id', $organization->id)->where('name', 'Project Without Client Column')->firstOrFail();
|
||||||
|
$this->assertNull($project->client_id);
|
||||||
|
$this->assertSame(100 * 3600, $project->estimated_time);
|
||||||
|
$this->assertEqualsCanonicalizing(
|
||||||
|
['Task 1', 'Task 2'],
|
||||||
|
Task::query()->where('project_id', $project->id)->pluck('name')->all(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
public function test_import_supports_activities_column_alias_for_tasks(): void
|
public function test_import_supports_activities_column_alias_for_tasks(): void
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
|
|||||||
@@ -136,6 +136,46 @@ class ClockifyTimeEntriesImporterTest extends ImporterTestAbstract
|
|||||||
$this->assertSame(1, $report->tasksCreated);
|
$this->assertSame(1, $report->tasksCreated);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function test_import_of_test_file_without_client_column_succeeds(): void
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
$organization = Organization::factory()->create();
|
||||||
|
$timezone = 'Europe/Vienna';
|
||||||
|
$importer = new ClockifyTimeEntriesImporter;
|
||||||
|
$importer->init($organization);
|
||||||
|
// Newer Clockify exports no longer contain a "Client" column.
|
||||||
|
$data = Storage::disk('testfiles')->get('clockify_time_entries_import_test_6.csv');
|
||||||
|
|
||||||
|
// Act
|
||||||
|
$importer->importData($data, $timezone);
|
||||||
|
$report = $importer->getReport();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
$this->assertSame(2, $report->timeEntriesCreated);
|
||||||
|
$this->assertSame(2, $report->projectsCreated);
|
||||||
|
$this->assertSame(0, $report->clientsCreated);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_import_of_test_file_with_client_column_but_missing_values_succeeds(): void
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
$organization = Organization::factory()->create();
|
||||||
|
$timezone = 'Europe/Vienna';
|
||||||
|
$importer = new ClockifyTimeEntriesImporter;
|
||||||
|
$importer->init($organization);
|
||||||
|
// Rows shorter than the header are padded with null by the CSV reader.
|
||||||
|
$data = Storage::disk('testfiles')->get('clockify_time_entries_import_test_7.csv');
|
||||||
|
|
||||||
|
// Act
|
||||||
|
$importer->importData($data, $timezone);
|
||||||
|
$report = $importer->getReport();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
$this->assertSame(1, $report->timeEntriesCreated);
|
||||||
|
$this->assertSame(1, $report->projectsCreated);
|
||||||
|
$this->assertSame(0, $report->clientsCreated);
|
||||||
|
}
|
||||||
|
|
||||||
public function test_import_fails_if_month_in_date_is_bigger_than_12(): void
|
public function test_import_fails_if_month_in_date_is_bigger_than_12(): void
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
|
|||||||
Reference in New Issue
Block a user