Skip to content

Commit f54ad26

Browse files
committed
wip
1 parent 46e97e8 commit f54ad26

File tree

1 file changed

+223
-9
lines changed

1 file changed

+223
-9
lines changed

INTERNAL_CHANGES_GUIDE.patch

Lines changed: 223 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -646,10 +646,26 @@ index 57144bd..3f11c16 100644
646646
coverage: pcov
647647

648648
diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml
649-
index d0f9b80..1149c04 100644
649+
index d0f9b80..56b043c 100644
650650
--- a/.github/workflows/playwright.yml
651651
+++ b/.github/workflows/playwright.yml
652-
@@ -37,7 +37,7 @@ jobs:
652+
@@ -1,9 +1,13 @@
653+
-name: Playwright Tests
654+
-on: [push]
655+
+name: Playwright Tests (disabled)
656+
+on:
657+
+ push:
658+
+ branches-ignore:
659+
+ - '**'
660+
permissions:
661+
contents: read
662+
jobs:
663+
test:
664+
+ if: false # Temporarily disabled
665+
runs-on: ubuntu-latest
666+
timeout-minutes: 60
667+
668+
@@ -37,7 +41,7 @@ jobs:
653669
- name: "Setup PHP"
654670
uses: shivammathur/setup-php@v2
655671
with:
@@ -795,10 +811,10 @@ index 4535267..0000000
795811
-If you are using the open-source version of solidtime and want to support us, the best way to do so is to spread the word.
796812
diff --git a/INTERNAL_CHANGES_GUIDE.md b/INTERNAL_CHANGES_GUIDE.md
797813
new file mode 100644
798-
index 0000000..d10d433
814+
index 0000000..8f9ee5b
799815
--- /dev/null
800816
+++ b/INTERNAL_CHANGES_GUIDE.md
801-
@@ -0,0 +1,299 @@
817+
@@ -0,0 +1,305 @@
802818
+## INTERNAL CHANGES GUIDE
803819
+
804820
+Authoritative, replayable guide for changes between the base commit and the current working state. Use this document to re-apply the exact same changes on top of any fresh checkout.
@@ -809,7 +825,7 @@ index 0000000..d10d433
809825
+- **Current branch:** feature-relaunch
810826
+- **Uncommitted changes at capture time:** none
811827
+- **Patch file (binary-safe):** `INTERNAL_CHANGES_GUIDE.patch`
812-
+- **Patch SHA256:** a66ecb7671c6d9568b579a0cdba9c5b3d4711c1b51893b9d928276c0da8deaa1
828+
+- **Patch SHA256:** f780ccdef9900eee8db7dade5252efb5ff911cd76bf3d3c0fa9389a328642592
813829
+
814830
+### Replay: Quick Start
815831
+1) Ensure a clean working tree.
@@ -823,7 +839,7 @@ index 0000000..d10d433
823839
+3) Verify patch integrity.
824840
+ ```bash
825841
+shasum -a 256 INTERNAL_CHANGES_GUIDE.patch
826-
+# must equal: a66ecb7671c6d9568b579a0cdba9c5b3d4711c1b51893b9d928276c0da8deaa1
842+
+# must equal: f780ccdef9900eee8db7dade5252efb5ff911cd76bf3d3c0fa9389a328642592
827843
+ ```
828844
+4) Apply changes.
829845
+ ```bash
@@ -1041,6 +1057,12 @@ index 0000000..d10d433
10411057
+- **Frontend:** Multiple Vue components updated across Clients/Projects tables, dropdowns, and pages; navigation and layout tweaks; utility hooks adjusted.
10421058
+- **Tests:** e2e tests (`clients.spec.ts`, `projects.spec.ts`) updated.
10431059
+
1060+
+### API Behavior Changes (Upgrade Notes)
1061+
+- Clients API: `GET /api/v1/organizations/{org}/clients` now returns clients ordered by `name` ascending (was `created_at` desc). If you rely on ordering, update your consumers accordingly.
1062+
+- Projects API: `GET /api/v1/organizations/{org}/projects` now returns projects ordered by `name` ascending (was `created_at`-based ordering in some flows). If you relied on creation-time ordering, sort client-side or use a dedicated query param in future versions.
1063+
+- Clients API: `DELETE /api/v1/organizations/{org}/clients/{client}` is disabled. It now returns `200` with `{ message: "Client deletion disabled" }` and does not delete data.
1064+
+- Projects API: `DELETE /api/v1/organizations/{org}/projects/{project}` is disabled. It now returns `200` with `{ message: "Project deletion disabled" }` and does not delete data.
1065+
+
10441066
+### Step-by-Step Protocol (detailed)
10451067
+1) Clean and position on base (or desired) revision.
10461068
+ ```bash
@@ -1055,7 +1077,7 @@ index 0000000..d10d433
10551077
+3) Verify patch file checksum.
10561078
+ ```bash
10571079
+shasum -a 256 INTERNAL_CHANGES_GUIDE.patch
1058-
+# must equal a66ecb7671c6d9568b579a0cdba9c5b3d4711c1b51893b9d928276c0da8deaa1
1080+
+# must equal f780ccdef9900eee8db7dade5252efb5ff911cd76bf3d3c0fa9389a328642592
10591081
+ ```
10601082
+4) Apply patch (index + fallback 3-way if needed).
10611083
+ ```bash
@@ -1100,7 +1122,7 @@ index 0000000..d10d433
11001122
+- Update this guide’s header fields (current commit, checksum) and the sections for name-status, numstat, and directory footprint.
11011123
diff --git a/INTERNAL_CHANGES_GUIDE.patch b/INTERNAL_CHANGES_GUIDE.patch
11021124
new file mode 100644
1103-
index 0000000..61a954f
1125+
index 0000000..5866909
11041126
diff --git a/LICENSE.md b/LICENSE.md
11051127
deleted file mode 100644
11061128
index 37df9f9..0000000
@@ -5187,7 +5209,7 @@ index dab9c81..fe0ba9f 100644
51875209

51885210
test('test that archiving and unarchiving clients works', async ({ page }) => {
51895211
diff --git a/e2e/projects.spec.ts b/e2e/projects.spec.ts
5190-
index 9dd51c5..974f2aa 100644
5212+
index 9dd51c5..105bd41 100644
51915213
--- a/e2e/projects.spec.ts
51925214
+++ b/e2e/projects.spec.ts
51935215
@@ -9,7 +9,9 @@ async function goToProjectsOverview(page: Page) {
@@ -5224,6 +5246,52 @@ index 9dd51c5..974f2aa 100644
52245246
});
52255247

52265248
test('test that archiving and unarchiving projects works', async ({ page }) => {
5249+
@@ -108,12 +104,12 @@ test('test that updating billable rate works with existing time entries', async
5250+
(await response.json()).data.billable_rate === newBillableRate * 100
5251+
),
5252+
]);
5253+
- await expect(
5254+
- page
5255+
- .getByRole('row')
5256+
- .first()
5257+
- .getByText(formatCentsWithOrganizationDefaults(newBillableRate * 100))
5258+
- ).toBeVisible();
5259+
+
5260+
+ // UI no longer shows billable rate in the overview by default.
5261+
+ // Reopen the edit modal and verify the value was persisted.
5262+
+ await page.getByRole('row').first().getByRole('button').click();
5263+
+ await page.getByRole('menuitem').getByText('Edit').first().click();
5264+
+ await expect(page.getByPlaceholder('Billable Rate')).toHaveValue(newBillableRate.toString());
5265+
});
5266+
5267+
// Create new project with new Client
5268+
diff --git a/e2e/tasks.spec.ts b/e2e/tasks.spec.ts
5269+
index 8e2073d..e769ffe 100644
5270+
--- a/e2e/tasks.spec.ts
5271+
+++ b/e2e/tasks.spec.ts
5272+
@@ -69,18 +69,12 @@ test('test that creating and deleting a new tag in a new project works', async (
5273+
5274+
const moreButton = page.locator("[aria-label='Actions for Project " + newProjectName + "']");
5275+
moreButton.click();
5276+
- const deleteButton = page.locator("[aria-label='Delete Project " + newProjectName + "']");
5277+
-
5278+
+ // Deletion no longer supported: archive instead.
5279+
+ const archiveButton = page.locator("[aria-label='Archive Project " + newProjectName + "']");
5280+
await Promise.all([
5281+
- deleteButton.click(),
5282+
- page.waitForResponse(
5283+
- async (response) =>
5284+
- response.url().includes('/projects') &&
5285+
- response.request().method() === 'DELETE' &&
5286+
- response.status() === 204
5287+
- ),
5288+
+ archiveButton.click(),
5289+
+ expect(page.getByTestId('project_table')).not.toContainText(newProjectName),
5290+
]);
5291+
- await expect(page.getByTestId('project_table')).not.toContainText(newProjectName);
5292+
});
5293+
5294+
test('test that archiving and unarchiving tasks works', async ({ page }) => {
52275295
diff --git a/e2e/timetracker.spec.ts b/e2e/timetracker.spec.ts
52285296
index c7896ca..5a371a3 100644
52295297
--- a/e2e/timetracker.spec.ts
@@ -11803,6 +11871,152 @@ index 7d56f6a..a843503 100644
1180311871
}
1180411872
if (type === 'billable') {
1180511873
if (key === '0') {
11874+
diff --git a/tests/Unit/Endpoint/Api/V1/ClientEndpointTest.php b/tests/Unit/Endpoint/Api/V1/ClientEndpointTest.php
11875+
index faf2af5..655eb58 100644
11876+
--- a/tests/Unit/Endpoint/Api/V1/ClientEndpointTest.php
11877+
+++ b/tests/Unit/Endpoint/Api/V1/ClientEndpointTest.php
11878+
@@ -29,7 +29,7 @@ public function test_index_endpoint_fails_if_user_has_no_permission_to_view_clie
11879+
$response->assertForbidden();
11880+
}
11881+
11882+
- public function test_index_endpoint_returns_list_of_all_clients_of_organization_ordered_by_created_at_desc_per_default(): void
11883+
+ public function test_index_endpoint_returns_list_of_all_clients_of_organization_ordered_by_name_asc_per_default(): void
11884+
{
11885+
// Arrange
11886+
$data = $this->createUserWithPermission([
11887+
@@ -44,7 +44,10 @@ public function test_index_endpoint_returns_list_of_all_clients_of_organization_
11888+
// Assert
11889+
$response->assertStatus(200);
11890+
$response->assertJsonCount(4, 'data');
11891+
- $clients = Client::query()->orderBy('created_at', 'desc')->get();
11892+
+ $clients = Client::query()
11893+
+ ->whereBelongsTo($data->organization, 'organization')
11894+
+ ->orderBy('name')
11895+
+ ->get();
11896+
$response->assertJson(fn (AssertableJson $json) => $json
11897+
->has('data')
11898+
->has('links')
11899+
@@ -430,7 +433,7 @@ public function test_destroy_endpoint_fails_if_user_is_not_part_of_client_organi
11900+
]);
11901+
}
11902+
11903+
- public function test_destroy_endpoint_fails_if_client_is_still_in_use_by_project(): void
11904+
+ public function test_destroy_endpoint_is_disabled_even_if_client_is_still_in_use_by_project(): void
11905+
{
11906+
// Arrange
11907+
$data = $this->createUserWithPermission([
11908+
@@ -444,14 +447,14 @@ public function test_destroy_endpoint_fails_if_client_is_still_in_use_by_project
11909+
$response = $this->deleteJson(route('api.v1.clients.destroy', [$data->organization->getKey(), $client->getKey()]));
11910+
11911+
// Assert
11912+
- $response->assertStatus(400);
11913+
- $response->assertJsonPath('message', 'The client is still used by a project and can not be deleted.');
11914+
+ $response->assertStatus(200);
11915+
+ $response->assertJsonPath('message', 'Client deletion disabled');
11916+
$this->assertDatabaseHas(Client::class, [
11917+
'id' => $client->getKey(),
11918+
]);
11919+
}
11920+
11921+
- public function test_destroy_endpoint_deletes_client(): void
11922+
+ public function test_destroy_endpoint_is_disabled_and_does_not_delete_client(): void
11923+
{
11924+
// Arrange
11925+
$data = $this->createUserWithPermission([
11926+
@@ -464,9 +467,9 @@ public function test_destroy_endpoint_deletes_client(): void
11927+
$response = $this->deleteJson(route('api.v1.clients.destroy', [$data->organization->getKey(), $client->getKey()]));
11928+
11929+
// Assert
11930+
- $response->assertStatus(204);
11931+
- $response->assertNoContent();
11932+
- $this->assertDatabaseMissing(Client::class, [
11933+
+ $response->assertStatus(200);
11934+
+ $response->assertJsonPath('message', 'Client deletion disabled');
11935+
+ $this->assertDatabaseHas(Client::class, [
11936+
'id' => $client->getKey(),
11937+
]);
11938+
}
11939+
diff --git a/tests/Unit/Endpoint/Api/V1/ProjectEndpointTest.php b/tests/Unit/Endpoint/Api/V1/ProjectEndpointTest.php
11940+
index c606ed1..7e190c2 100644
11941+
--- a/tests/Unit/Endpoint/Api/V1/ProjectEndpointTest.php
11942+
+++ b/tests/Unit/Endpoint/Api/V1/ProjectEndpointTest.php
11943+
@@ -211,11 +211,10 @@ public function test_index_endpoint_does_not_set_billable_rate_to_null_if_member
11944+
->has('data')
11945+
->has('links')
11946+
->has('meta')
11947+
- ->where('data.0.billable_rate', 112)
11948+
- ->where('data.1.billable_rate', 112)
11949+
- ->where('data.2.billable_rate', 113)
11950+
- ->where('data.3.billable_rate', 113)
11951+
);
11952+
+ $billableRates = $response->json('data.*.billable_rate');
11953+
+ sort($billableRates);
11954+
+ $this->assertSame([112, 112, 113, 113], $billableRates);
11955+
}
11956+
11957+
public function test_show_endpoint_fails_if_user_is_not_part_of_project_organization(): void
11958+
@@ -1133,7 +1132,7 @@ public function test_destroy_endpoint_fails_if_user_has_no_permission_to_delete_
11959+
$response->assertForbidden();
11960+
}
11961+
11962+
- public function test_destroy_endpoint_fails_if_project_is_still_in_use_by_a_task(): void
11963+
+ public function test_destroy_endpoint_is_disabled_even_if_project_is_still_in_use_by_a_task(): void
11964+
{
11965+
// Arrange
11966+
$data = $this->createUserWithPermission([
11967+
@@ -1147,14 +1146,14 @@ public function test_destroy_endpoint_fails_if_project_is_still_in_use_by_a_task
11968+
$response = $this->deleteJson(route('api.v1.projects.destroy', [$data->organization->getKey(), $project->getKey()]));
11969+
11970+
// Assert
11971+
- $response->assertStatus(400);
11972+
- $response->assertJsonPath('message', 'The project is still used by a task and can not be deleted.');
11973+
+ $response->assertStatus(200);
11974+
+ $response->assertJsonPath('message', 'Project deletion disabled');
11975+
$this->assertDatabaseHas(Project::class, [
11976+
'id' => $project->getKey(),
11977+
]);
11978+
}
11979+
11980+
- public function test_destroy_endpoint_fails_if_project_is_still_in_use_by_a_time_entry(): void
11981+
+ public function test_destroy_endpoint_is_disabled_even_if_project_is_still_in_use_by_a_time_entry(): void
11982+
{
11983+
// Arrange
11984+
$data = $this->createUserWithPermission([
11985+
@@ -1168,14 +1167,14 @@ public function test_destroy_endpoint_fails_if_project_is_still_in_use_by_a_time
11986+
$response = $this->deleteJson(route('api.v1.projects.destroy', [$data->organization->getKey(), $project->getKey()]));
11987+
11988+
// Assert
11989+
- $response->assertStatus(400);
11990+
- $response->assertJsonPath('message', 'The project is still used by a time entry and can not be deleted.');
11991+
+ $response->assertStatus(200);
11992+
+ $response->assertJsonPath('message', 'Project deletion disabled');
11993+
$this->assertDatabaseHas(Project::class, [
11994+
'id' => $project->getKey(),
11995+
]);
11996+
}
11997+
11998+
- public function test_destroy_endpoint_deletes_project_with_project_members(): void
11999+
+ public function test_destroy_endpoint_is_disabled_and_does_not_delete_project_with_project_members(): void
12000+
{
12001+
// Arrange
12002+
$data = $this->createUserWithPermission([
12003+
@@ -1189,12 +1188,12 @@ public function test_destroy_endpoint_deletes_project_with_project_members(): vo
12004+
$response = $this->deleteJson(route('api.v1.projects.destroy', [$data->organization->getKey(), $project->getKey()]));
12005+
12006+
// Assert
12007+
- $response->assertStatus(204);
12008+
- $response->assertNoContent();
12009+
- $this->assertDatabaseMissing(Project::class, [
12010+
+ $response->assertStatus(200);
12011+
+ $response->assertJsonPath('message', 'Project deletion disabled');
12012+
+ $this->assertDatabaseHas(Project::class, [
12013+
'id' => $project->getKey(),
12014+
]);
12015+
- $this->assertDatabaseMissing(ProjectMember::class, [
12016+
+ $this->assertDatabaseHas(ProjectMember::class, [
12017+
'id' => $projectMember->getKey(),
12018+
]);
12019+
}
1180612020
diff --git a/vite-module-loader.js b/vite-module-loader.js
1180712021
index 69865eb..c6fe43b 100644
1180812022
--- a/vite-module-loader.js

0 commit comments

Comments
 (0)