Skip to content

Commit 2d57716

Browse files
committed
Updated
1 parent 1b10f20 commit 2d57716

File tree

12 files changed

+91
-75
lines changed

12 files changed

+91
-75
lines changed

.github/workflows/npm-publish-api.yml

Lines changed: 0 additions & 32 deletions
This file was deleted.

.github/workflows/npm-publish-ui.yml

Lines changed: 0 additions & 32 deletions
This file was deleted.

app/Http/Controllers/Api/V1/TimeEntryController.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,7 @@ private function getTimeEntriesQuery(Organization $organization, TimeEntryIndexR
212212
$filter->addTaskIdsFilter($request->input('task_ids'));
213213
$filter->addClientIdsFilter($request->input('client_ids'));
214214
$filter->addBillableFilter($request->input('billable'));
215+
$filter->addInvoicedFilter($request->input('invoiced'));
215216

216217
return $filter->get();
217218
}

app/Http/Requests/V1/TimeEntry/TimeEntryIndexExportRequest.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,11 @@ public function rules(): array
119119
'string',
120120
'in:true,false',
121121
],
122+
// Filter by invoiced status (invoiced_at not null vs null)
123+
'invoiced' => [
124+
'string',
125+
'in:true,false',
126+
],
122127
// Limit the number of returned time entries (default: 150)
123128
'limit' => [
124129
'integer',

app/Http/Requests/V1/TimeEntry/TimeEntryIndexRequest.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,11 @@ public function rules(): array
122122
'string',
123123
'in:true,false',
124124
],
125+
// Filter by invoiced status (invoiced_at not null vs null)
126+
'invoiced' => [
127+
'string',
128+
'in:true,false',
129+
],
125130
// Limit the number of returned time entries (default: 150)
126131
'limit' => [
127132
'integer',

app/Service/TimeEntryFilter.php

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,36 @@ public function addBillable(?bool $billable): self
141141
return $this;
142142
}
143143

144+
public function addInvoicedFilter(?string $invoiced): self
145+
{
146+
if ($invoiced === null) {
147+
return $this;
148+
}
149+
if ($invoiced === 'true') {
150+
$this->addInvoiced(true);
151+
} elseif ($invoiced === 'false') {
152+
$this->addInvoiced(false);
153+
} else {
154+
Log::warning('Invalid invoiced filter value', ['value' => $invoiced]);
155+
}
156+
157+
return $this;
158+
}
159+
160+
public function addInvoiced(?bool $invoiced): self
161+
{
162+
if ($invoiced === null) {
163+
return $this;
164+
}
165+
if ($invoiced) {
166+
$this->builder->whereNotNull('invoiced_at');
167+
} else {
168+
$this->builder->whereNull('invoiced_at');
169+
}
170+
171+
return $this;
172+
}
173+
144174
/**
145175
* @param array<string>|null $clientIds
146176
*/

package.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@
1313
"format:check": "prettier --check './**/*.{js,jsx,cjs,mjs,ts,tsx,cts,mts,vue}'"
1414
},
1515
"devDependencies": {
16+
"eslint": "^9.19.0",
17+
"eslint-config-prettier": "^9.1.0",
18+
"eslint-plugin-vue": "^9.30.0",
19+
"globals": "^15.12.0",
20+
"typescript-eslint": "^8.18.2",
1621
"@eslint/eslintrc": "^3.2.0",
1722
"@eslint/js": "^9.19.0",
1823
"@inertiajs/vue3": "^1.0.0",
@@ -30,6 +35,7 @@
3035
"openapi-zod-client": "^1.16.2",
3136
"postcss": "^8.4.47",
3237
"postcss-nesting": "^12.1.5",
38+
"prettier": "^3.3.3",
3339
"tailwindcss": "^3.4.13",
3440
"typescript": "^5.7.3",
3541
"vite": "^6.0.11",

resources/js/Pages/ReportingDetailed.vue

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
ChevronRightIcon,
1414
ChevronDoubleRightIcon,
1515
ClockIcon,
16+
DocumentTextIcon,
1617
} from '@heroicons/vue/20/solid';
1718
import DateRangePicker from '@/packages/ui/src/Input/DateRangePicker.vue';
1819
import BillableIcon from '@/packages/ui/src/Icons/BillableIcon.vue';
@@ -84,6 +85,7 @@ const selectedMembers = ref<string[]>([]);
8485
const selectedTasks = ref<string[]>([]);
8586
const selectedClients = ref<string[]>([]);
8687
const billable = ref<'true' | 'false' | null>(null);
88+
const invoiced = ref<'true' | 'false' | null>(null);
8789
const roundingEnabled = ref<boolean>(false);
8890
const roundingType = ref<TimeEntryRoundingType>('nearest');
8991
const roundingMinutes = ref<number>(15);
@@ -114,6 +116,7 @@ function getFilterAttributes() {
114116
client_ids: selectedClients.value.length > 0 ? selectedClients.value : undefined,
115117
tag_ids: selectedTags.value.length > 0 ? selectedTags.value : undefined,
116118
billable: billable.value !== null ? billable.value : undefined,
119+
invoiced: invoiced.value !== null ? invoiced.value : undefined,
117120
rounding_type: roundingEnabled.value ? roundingType.value : undefined,
118121
rounding_minutes: roundingEnabled.value ? roundingMinutes.value : undefined,
119122
};
@@ -344,6 +347,32 @@ async function downloadExport(format: ExportFormat) {
344347
:icon="BillableIcon"></ReportingFilterBadge>
345348
</template>
346349
</SelectDropdown>
350+
<SelectDropdown
351+
v-model="invoiced"
352+
:get-key-from-item="(item) => item.value"
353+
:get-name-for-item="(item) => item.label"
354+
:items="[
355+
{
356+
label: 'Both',
357+
value: null,
358+
},
359+
{
360+
label: 'Invoiced',
361+
value: 'true',
362+
},
363+
{
364+
label: 'Not invoiced',
365+
value: 'false',
366+
},
367+
]"
368+
@changed="updateFilteredTimeEntries">
369+
<template #trigger>
370+
<ReportingFilterBadge
371+
:active="invoiced !== null"
372+
:title="invoiced === 'false' ? 'Not invoiced' : 'Invoiced'"
373+
:icon="DocumentTextIcon"></ReportingFilterBadge>
374+
</template>
375+
</SelectDropdown>
347376
<ReportingRoundingControls
348377
v-model:enabled="roundingEnabled"
349378
v-model:type="roundingType"

resources/js/packages/ui/src/Icons/InvoiceStatusIcon.vue

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,7 @@ const iconSizeClasses = computed(() => {
3232
<component
3333
:is="IconComponent"
3434
:class="[iconColorClasses, iconSizeClasses]"
35-
:title="invoiced ? 'Invoiced' : 'Not invoiced'"
36-
/>
35+
:title="invoiced ? 'Invoiced' : 'Not invoiced'" />
3736
</template>
3837

3938
<style scoped></style>

resources/js/packages/ui/src/TimeEntry/TimeEntryEditModal.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ async function deleteEntry() {
123123
await props.deleteTimeEntry(editableTimeEntry.value.id);
124124
// Success - close modal and let the store handle success message
125125
show.value = false;
126-
} catch (error) {
126+
} catch {
127127
// Error is already handled by handleApiRequestNotifications
128128
// Don't close the modal on error
129129
} finally {

0 commit comments

Comments
 (0)