Skip to content

Commit cb1d707

Browse files
authored
Merge pull request #24 from codebar-ag/main
m/p
2 parents 7df2502 + 5702f9b commit cb1d707

File tree

5 files changed

+168
-107
lines changed

5 files changed

+168
-107
lines changed

resources/js/Components/Common/Report/ReportTableHeading.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import TableHeading from '@/Components/Common/TableHeading.vue';
55
<template>
66
<TableHeading>
77
<div
8-
class="py-1.5 pr-3 text-left font-semibold text-text-primary pl-4 sm:pl-6 lg:pl-8">
8+
class="py-1.5 pr-3 text-left font-semibold text-text-primary pl-4 sm:pl-6 lg:pl-8 3xl:pl-12">
99
Name
1010
</div>
1111
<div class="px-3 py-1.5 text-left font-semibold text-text-primary">

resources/js/Components/Common/Report/ReportTableRow.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ async function deleteReport() {
6262
:original-report="report"></ReportEditModal>
6363
<TableRow>
6464
<div
65-
class="whitespace-nowrap min-w-0 flex items-center space-x-5 py-4 pr-3 text-sm font-medium text-text-primary pl-4 sm:pl-6 lg:pl-8">
65+
class="whitespace-nowrap min-w-0 flex items-center space-x-5 3xl:pl-12 py-4 pr-3 text-sm font-medium text-text-primary pl-4 sm:pl-6 lg:pl-8 3xl:pl-12">
6666
<span class="overflow-ellipsis overflow-hidden">
6767
{{ report.name }}
6868
</span>

resources/js/Components/Common/Reporting/ReportingOverview.vue

Lines changed: 84 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ import {
1616
import { formatCents } from '@/packages/ui/src/utils/money';
1717
import ReportingTabNavbar from '@/Components/Common/Reporting/ReportingTabNavbar.vue';
1818
import ReportingExportButton from '@/Components/Common/Reporting/ReportingExportButton.vue';
19-
import ReportingRoundingControls from '@/Components/Common/Reporting/ReportingRoundingControls.vue';
2019
import TaskMultiselectDropdown from '@/Components/Common/Task/TaskMultiselectDropdown.vue';
2120
import ClientMultiselectDropdown from '@/Components/Common/Client/ClientMultiselectDropdown.vue';
2221
import ReportingRow from '@/Components/Common/Reporting/ReportingRow.vue';
@@ -32,8 +31,9 @@ import DateRangePicker from '@/packages/ui/src/Input/DateRangePicker.vue';
3231
import ReportingExportModal from '@/Components/Common/Reporting/ReportingExportModal.vue';
3332
import ReportSaveButton from '@/Components/Common/Report/ReportSaveButton.vue';
3433
import TagDropdown from '@/packages/ui/src/Tag/TagDropdown.vue';
34+
import ReportingPieChart from '@/Components/Common/Reporting/ReportingPieChart.vue';
3535
36-
import { computed, type ComputedRef, inject, onMounted, ref, watch } from 'vue';
36+
import { computed, type ComputedRef, inject, onMounted, ref } from 'vue';
3737
import { type GroupingOption, useReportingStore } from '@/utils/useReporting';
3838
import { storeToRefs } from 'pinia';
3939
import {
@@ -54,9 +54,6 @@ import type { ExportFormat } from '@/types/reporting';
5454
import { getRandomColorWithSeed } from '@/packages/ui/src/utils/color';
5555
import { useProjectsStore } from '@/utils/useProjects';
5656
57-
// TimeEntryRoundingType is now defined in ReportingRoundingControls component
58-
type TimeEntryRoundingType = 'up' | 'down' | 'nearest';
59-
6057
const { handleApiRequestNotifications } = useNotificationsStore();
6158
6259
const startDate = useSessionStorage<string>(
@@ -74,9 +71,6 @@ const selectedTasks = ref<string[]>([]);
7471
const selectedClients = ref<string[]>([]);
7572
7673
const billable = ref<'true' | 'false' | null>(null);
77-
const roundingEnabled = ref<boolean>(false);
78-
const roundingType = ref<TimeEntryRoundingType>('nearest');
79-
const roundingMinutes = ref<number>(15);
8074
8175
const group = useStorage<GroupingOption>('reporting-group', 'project');
8276
const subGroup = useStorage<GroupingOption>('reporting-sub-group', 'task');
@@ -90,11 +84,6 @@ const { groupByOptions } = reportingStore;
9084
9185
const organization = inject<ComputedRef<Organization>>('organization');
9286
93-
// Watch rounding enabled state to trigger updates
94-
watch(roundingEnabled, () => {
95-
updateReporting();
96-
});
97-
9887
function getFilterAttributes(): AggregatedTimeEntriesQueryParams {
9988
let params: AggregatedTimeEntriesQueryParams = {
10089
start: getLocalizedDayJs(startDate.value).startOf('day').utc().format(),
@@ -122,8 +111,6 @@ function getFilterAttributes(): AggregatedTimeEntriesQueryParams {
122111
getCurrentRole() === 'employee'
123112
? getCurrentMembershipId()
124113
: undefined,
125-
rounding_type: roundingEnabled.value ? roundingType.value : undefined,
126-
rounding_minutes: roundingEnabled.value ? roundingMinutes.value : undefined,
127114
};
128115
return params;
129116
}
@@ -237,6 +224,42 @@ const { projects } = storeToRefs(projectsStore);
237224
const showExportModal = ref(false);
238225
const exportUrl = ref<string | null>(null);
239226
227+
const groupedPieChartData = computed(() => {
228+
return (
229+
aggregatedTableTimeEntries.value?.grouped_data?.map((entry) => {
230+
const name = getNameForReportingRowEntry(
231+
entry.key,
232+
aggregatedTableTimeEntries.value?.grouped_type
233+
);
234+
let color = getRandomColorWithSeed(entry.key ?? 'none');
235+
if (
236+
name &&
237+
aggregatedTableTimeEntries.value?.grouped_type &&
238+
emptyPlaceholder[
239+
aggregatedTableTimeEntries.value?.grouped_type
240+
] === name
241+
) {
242+
color = '#CCCCCC';
243+
} else if (
244+
aggregatedTableTimeEntries.value?.grouped_type === 'project'
245+
) {
246+
color =
247+
projects.value?.find((project) => project.id === entry.key)
248+
?.color ?? '#CCCCCC';
249+
}
250+
return {
251+
value: entry.seconds,
252+
name:
253+
getNameForReportingRowEntry(
254+
entry.key,
255+
aggregatedTableTimeEntries.value?.grouped_type
256+
) ?? '',
257+
color: color,
258+
};
259+
}) ?? []
260+
);
261+
});
262+
240263
const tableData = computed(() => {
241264
return aggregatedTableTimeEntries.value?.grouped_data?.map((entry) => {
242265
return {
@@ -260,13 +283,6 @@ const tableData = computed(() => {
260283
};
261284
});
262285
});
263-
264-
const showBillableRate = computed(() => {
265-
return !!(
266-
getCurrentRole() !== 'employee' ||
267-
organization?.value?.employees_can_see_billable_rates
268-
);
269-
});
270286
</script>
271287

272288
<template>
@@ -289,7 +305,7 @@ const showBillableRate = computed(() => {
289305
<div class="py-2.5 w-full border-b border-default-background-separator">
290306
<MainContainer class="sm:flex space-y-4 sm:space-y-0 justify-between">
291307
<div
292-
class="flex flex-wrap items-center space-y-2 sm:space-y-0 space-x-3">
308+
class="flex flex-wrap items-center space-y-2 sm:space-y-0 space-x-4">
293309
<div class="text-sm font-medium">Filters</div>
294310
<MemberMultiselectDropdown
295311
v-model="selectedMembers"
@@ -379,11 +395,6 @@ const showBillableRate = computed(() => {
379395
:icon="BillableIcon"></ReportingFilterBadge>
380396
</template>
381397
</SelectDropdown>
382-
<ReportingRoundingControls
383-
v-model:enabled="roundingEnabled"
384-
v-model:type="roundingType"
385-
v-model:minutes="roundingMinutes"
386-
@change="updateReporting"></ReportingRoundingControls>
387398
</div>
388399
<div>
389400
<DateRangePicker
@@ -403,9 +414,9 @@ const showBillableRate = computed(() => {
403414
</div>
404415
</MainContainer>
405416
<MainContainer>
406-
<div class="pt-6 items-start">
417+
<div class="sm:grid grid-cols-4 pt-6 items-start">
407418
<div
408-
class="bg-card-background rounded-lg border border-card-border pt-3">
419+
class="col-span-3 bg-card-background rounded-lg border border-card-border pt-3">
409420
<div
410421
class="text-sm flex text-text-primary items-center space-x-3 font-medium px-6 border-b border-card-background-separator pb-3">
411422
<span>Group by</span>
@@ -425,24 +436,33 @@ const showBillableRate = computed(() => {
425436
updateTableReporting
426437
"></ReportingGroupBySelect>
427438
</div>
428-
<div class="px-6 pt-6 pb-3">
429-
<template
430-
v-for="reportingRowEntry in tableData"
431-
:key="reportingRowEntry.description">
432-
<ReportingRow
433-
:entry="reportingRowEntry"
434-
:currency="getOrganizationCurrencyString()"></ReportingRow>
435-
</template>
439+
<div
440+
class="grid items-center"
441+
style="grid-template-columns: 1fr 100px 150px">
436442
<div
443+
class="contents [&>*]:border-card-background-separator [&>*]:border-b [&>*]:bg-tertiary [&>*]:pb-1.5 [&>*]:pt-1 text-text-secondary text-sm">
444+
<div class="pl-6">Name</div>
445+
<div class="text-right">Duration</div>
446+
<div class="text-right pr-6">Cost</div>
447+
</div>
448+
<template
437449
v-if="
438-
aggregatedTableTimeEntries &&
439-
aggregatedTableTimeEntries.grouped_data &&
440-
aggregatedTableTimeEntries.grouped_data.length > 0
441-
"
442-
class="border-t border-background-separator pt-3 mt-6 text-sm space-y-2">
443-
<div class="justify-between items-center flex">
444-
<div class="font-medium">Total</div>
445-
<div class="font-medium">
450+
aggregatedTableTimeEntries?.grouped_data &&
451+
aggregatedTableTimeEntries.grouped_data?.length > 0
452+
">
453+
<ReportingRow
454+
v-for="entry in tableData"
455+
:key="entry.description ?? 'none'"
456+
:currency="getOrganizationCurrencyString()"
457+
:type="aggregatedTableTimeEntries.grouped_type"
458+
:entry="entry"></ReportingRow>
459+
<div
460+
class="contents [&>*]:transition text-text-tertiary [&>*]:h-[50px]">
461+
<div class="flex items-center pl-6 font-medium">
462+
<span>Total</span>
463+
</div>
464+
<div
465+
class="justify-end flex items-center font-medium">
446466
{{
447467
formatHumanReadableDuration(
448468
aggregatedTableTimeEntries.seconds,
@@ -451,37 +471,36 @@ const showBillableRate = computed(() => {
451471
)
452472
}}
453473
</div>
454-
</div>
455-
<div
456-
v-if="
457-
aggregatedTableTimeEntries.cost !== null &&
458-
showBillableRate
459-
"
460-
class="justify-between items-center flex">
461-
<div class="font-medium">Total Billable</div>
462-
<div class="justify-end pr-6 flex items-center font-medium">
474+
<div
475+
class="justify-end pr-6 flex items-center font-medium">
463476
{{
464-
formatCents(
465-
aggregatedTableTimeEntries.cost,
466-
getOrganizationCurrencyString(),
467-
organization?.currency_format,
468-
organization?.currency_symbol,
469-
organization?.number_format
470-
)
477+
aggregatedTableTimeEntries.cost
478+
? formatCents(
479+
aggregatedTableTimeEntries.cost,
480+
getOrganizationCurrencyString(),
481+
organization?.currency_format,
482+
organization?.currency_symbol,
483+
organization?.number_format
484+
)
485+
: '--'
471486
}}
472487
</div>
473488
</div>
474-
</div>
489+
</template>
475490
<div
476491
v-else
477-
class="chart flex flex-col items-center justify-center py-12">
478-
<p class="text-lg text-text-primary font-medium">
492+
class="chart flex flex-col items-center justify-center py-12 col-span-3">
493+
<p class="text-lg text-text-primary font-semibold">
479494
No time entries found
480495
</p>
481496
<p>Try to change the filters and time range</p>
482497
</div>
483498
</div>
484499
</div>
500+
<div class="px-2 lg:px-4">
501+
<ReportingPieChart
502+
:data="groupedPieChartData"></ReportingPieChart>
503+
</div>
485504
</div>
486505
</MainContainer>
487506
</template>

resources/js/Components/Dashboard/ThisWeekOverview.vue

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,11 @@ import StatCard from '@/Components/Common/StatCard.vue';
1414
import { ClockIcon } from '@heroicons/vue/20/solid';
1515
import CardTitle from '@/packages/ui/src/CardTitle.vue';
1616
import LinearGradient from 'zrender/lib/graphic/LinearGradient';
17+
import ProjectsChartCard from '@/Components/Dashboard/ProjectsChartCard.vue';
1718
import { formatHumanReadableDuration } from '@/packages/ui/src/utils/time';
1819
import { formatCents } from '@/packages/ui/src/utils/money';
1920
import { getWeekStart } from '@/packages/ui/src/utils/settings';
20-
import { useCssVariable } from '@/utils/useCssVariable';
21+
import { useCssVar } from '@vueuse/core';
2122
import { getOrganizationCurrencyString } from '@/utils/money';
2223
import { useQuery } from '@tanstack/vue-query';
2324
import { getCurrentOrganizationId } from '@/utils/useUser';
@@ -59,7 +60,7 @@ const weekdays = computed(() => {
5960
}
6061
});
6162
62-
const accentColor = useCssVariable('--theme-color-chart');
63+
const accentColor = useCssVar('--theme-color-chart', null, { observe: true });
6364
6465
// Get the organization ID using the utility function
6566
const organizationId = computed(() => getCurrentOrganizationId());
@@ -175,8 +176,10 @@ const seriesData = computed(() => {
175176
});
176177
});
177178
178-
const markLineColor = useCssVariable('--color-border-secondary');
179-
const labelColor = useCssVariable('--color-text-secondary');
179+
const markLineColor = useCssVar('--color-border-secondary', null, {
180+
observe: true,
181+
});
182+
const labelColor = useCssVar('--color-text-secondary', null, { observe: true });
180183
const option = computed(() => {
181184
return {
182185
tooltip: {
@@ -201,7 +204,7 @@ const option = computed(() => {
201204
fontSize: 16,
202205
fontWeight: 600,
203206
margin: 24,
204-
fontFamily: 'Inter, sans-serif',
207+
fontFamily: 'Outfit, sans-serif',
205208
color: labelColor.value,
206209
},
207210
axisTick: {
@@ -212,10 +215,6 @@ const option = computed(() => {
212215
},
213216
yAxis: {
214217
type: 'value',
215-
axisLabel: {
216-
color: labelColor.value,
217-
fontFamily: 'Inter, sans-serif',
218-
},
219218
splitLine: {
220219
lineStyle: {
221220
color: markLineColor.value,
@@ -291,6 +290,11 @@ const option = computed(() => {
291290
)
292291
: '--'
293292
" />
293+
<ProjectsChartCard
294+
v-if="weeklyProjectOverview"
295+
:weekly-project-overview="
296+
weeklyProjectOverview
297+
"></ProjectsChartCard>
294298
</div>
295299
</div>
296300
</template>

0 commit comments

Comments
 (0)