@hed-hog/operations 0.0.332 → 0.0.347
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/controllers/operations-collaborators.controller.d.ts +55 -36
- package/dist/controllers/operations-collaborators.controller.d.ts.map +1 -1
- package/dist/controllers/operations-projects.controller.d.ts +3 -0
- package/dist/controllers/operations-projects.controller.d.ts.map +1 -1
- package/dist/operations.service.d.ts +58 -36
- package/dist/operations.service.d.ts.map +1 -1
- package/dist/operations.service.js +34 -34
- package/dist/operations.service.js.map +1 -1
- package/dist/operations.service.spec.js +6 -0
- package/dist/operations.service.spec.js.map +1 -1
- package/hedhog/data/menu.yaml +5 -3
- package/hedhog/data/route.yaml +7 -7
- package/hedhog/frontend/app/_components/collaborator-details-screen.tsx.ejs +476 -0
- package/hedhog/frontend/app/_components/collaborator-form-screen.tsx.ejs +3 -1
- package/hedhog/frontend/app/_components/collaborator-select-with-create.tsx.ejs +261 -0
- package/hedhog/frontend/app/_components/collaborator-tasks-tab.tsx.ejs +358 -358
- package/hedhog/frontend/app/_components/collaborator-timesheets-tab.tsx.ejs +6 -6
- package/hedhog/frontend/app/_components/contract-content-editor.tsx.ejs +258 -0
- package/hedhog/frontend/app/_components/my-project-summary-screen.tsx.ejs +5 -4
- package/hedhog/frontend/app/_components/person-select-with-create.tsx.ejs +1 -0
- package/hedhog/frontend/app/_components/project-assignments-tab.tsx.ejs +0 -6
- package/hedhog/frontend/app/_components/project-cost-report-screen.tsx.ejs +23 -23
- package/hedhog/frontend/app/_components/project-details-screen.tsx.ejs +23 -50
- package/hedhog/frontend/app/_components/project-form-screen.tsx.ejs +62 -28
- package/hedhog/frontend/app/_components/task-detail-sheet.tsx.ejs +23 -6
- package/hedhog/frontend/app/_components/task-form-sheet.tsx.ejs +629 -629
- package/hedhog/frontend/app/_lib/api.ts.ejs +2 -2
- package/hedhog/frontend/app/_lib/utils/task-ui.ts.ejs +1 -1
- package/hedhog/frontend/app/my-projects/page.tsx.ejs +2 -16
- package/hedhog/frontend/app/my-tasks/page.tsx.ejs +86 -24
- package/hedhog/frontend/app/projects/page.tsx.ejs +6 -42
- package/hedhog/frontend/messages/operations/operations/en.json +2100 -0
- package/hedhog/frontend/messages/operations/operations/pt.json +2111 -0
- package/hedhog/frontend/widgets/capacity-distribution.tsx.ejs +16 -16
- package/hedhog/frontend/widgets/effort-by-project.tsx.ejs +16 -16
- package/hedhog/frontend/widgets/headcount-by-area.tsx.ejs +16 -16
- package/hedhog/frontend/widgets/index.ts.ejs +25 -25
- package/hedhog/frontend/widgets/managed-projects-status.tsx.ejs +16 -16
- package/hedhog/frontend/widgets/my-hours-period-kpi.tsx.ejs +16 -16
- package/hedhog/frontend/widgets/my-open-requests-kpi.tsx.ejs +16 -16
- package/hedhog/frontend/widgets/my-pending-requests-list.tsx.ejs +16 -16
- package/hedhog/frontend/widgets/my-project-allocations-kpi.tsx.ejs +16 -16
- package/hedhog/frontend/widgets/my-quick-actions.tsx.ejs +16 -16
- package/hedhog/frontend/widgets/my-relevant-deadlines.tsx.ejs +16 -16
- package/hedhog/frontend/widgets/my-timesheet-status-kpi.tsx.ejs +16 -16
- package/hedhog/frontend/widgets/my-weekly-journey.tsx.ejs +16 -16
- package/hedhog/frontend/widgets/portfolio-costs-kpi.tsx.ejs +16 -16
- package/hedhog/frontend/widgets/portfolio-effort-kpi.tsx.ejs +16 -16
- package/hedhog/frontend/widgets/portfolio-projects-kpi.tsx.ejs +16 -16
- package/hedhog/frontend/widgets/portfolio-risk-kpi.tsx.ejs +16 -16
- package/hedhog/frontend/widgets/project-status-overview.tsx.ejs +16 -16
- package/hedhog/frontend/widgets/shared-operations-widget.tsx.ejs +169 -169
- package/hedhog/frontend/widgets/strategic-deadlines.tsx.ejs +16 -16
- package/hedhog/frontend/widgets/team-approval-queue.tsx.ejs +16 -16
- package/hedhog/frontend/widgets/team-capacity-kpi.tsx.ejs +16 -16
- package/hedhog/frontend/widgets/team-headcount-kpi.tsx.ejs +16 -16
- package/hedhog/frontend/widgets/team-hours-kpi.tsx.ejs +16 -16
- package/hedhog/frontend/widgets/team-pending-approvals-kpi.tsx.ejs +16 -16
- package/hedhog/frontend/widgets/team-utilization-overview.tsx.ejs +16 -16
- package/hedhog/frontend/widgets/team-workload-alerts.tsx.ejs +16 -16
- package/hedhog/table/operations_collaborator.yaml +8 -8
- package/hedhog/table/operations_task.yaml +76 -76
- package/hedhog/table/operations_task_activity.yaml +51 -51
- package/package.json +6 -6
- package/src/controllers/operations-collaborators.controller.ts +9 -9
- package/src/controllers/operations-tasks.controller.ts +156 -156
- package/src/dashboard/widgets/MyQuickActions.tsx +22 -22
- package/src/dto/create-collaborator.dto.ts +4 -4
- package/src/operations.service.spec.ts +1006 -988
- package/src/operations.service.ts +40 -42
|
@@ -479,7 +479,7 @@ export function deleteTaskComment(
|
|
|
479
479
|
);
|
|
480
480
|
}
|
|
481
481
|
|
|
482
|
-
//
|
|
482
|
+
// Collaborator Payments
|
|
483
483
|
|
|
484
484
|
export type CollaboratorPayment = {
|
|
485
485
|
id: number;
|
|
@@ -553,7 +553,7 @@ export function deleteCollaboratorPayment(
|
|
|
553
553
|
);
|
|
554
554
|
}
|
|
555
555
|
|
|
556
|
-
//
|
|
556
|
+
// Collaborator Invoices
|
|
557
557
|
|
|
558
558
|
export type CollaboratorInvoice = {
|
|
559
559
|
id: number;
|
|
@@ -46,7 +46,7 @@ export function getTaskDescriptionPreview(value?: string | null) {
|
|
|
46
46
|
if (!value) return '';
|
|
47
47
|
|
|
48
48
|
return String(value)
|
|
49
|
-
.replace(/<br\s
|
|
49
|
+
.replace(/<br\s*\/?>(?=)/gi, ' ')
|
|
50
50
|
.replace(/<\/(p|div|li|ul|ol|h1|h2|h3|h4|h5|h6)>/gi, ' ')
|
|
51
51
|
.replace(/<li[^>]*>/gi, ' ')
|
|
52
52
|
.replace(/<[^>]*>/g, '')
|
|
@@ -49,12 +49,6 @@ function getPersonAvatarUrl(avatarId?: number | null): string {
|
|
|
49
49
|
: '/placeholder.png';
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
function getUserPhotoUrl(photoId?: number | null): string {
|
|
53
|
-
return typeof photoId === 'number' && photoId > 0
|
|
54
|
-
? `${process.env.NEXT_PUBLIC_API_BASE_URL}/user/avatar/${photoId}`
|
|
55
|
-
: '/placeholder.png';
|
|
56
|
-
}
|
|
57
|
-
|
|
58
52
|
function getInitials(value?: string | null): string {
|
|
59
53
|
if (!value) return '?';
|
|
60
54
|
return value
|
|
@@ -299,11 +293,7 @@ export default function OperationsMyProjectsPage() {
|
|
|
299
293
|
</span>
|
|
300
294
|
<Avatar className="h-5 w-5 shrink-0">
|
|
301
295
|
<AvatarImage
|
|
302
|
-
src={
|
|
303
|
-
project.clientUserPhotoId
|
|
304
|
-
? getUserPhotoUrl(project.clientUserPhotoId)
|
|
305
|
-
: getPersonAvatarUrl(project.clientAvatarId)
|
|
306
|
-
}
|
|
296
|
+
src={getPersonAvatarUrl(project.clientAvatarId)}
|
|
307
297
|
alt={project.clientName ?? ''}
|
|
308
298
|
/>
|
|
309
299
|
<AvatarFallback className="text-[9px] font-medium">
|
|
@@ -412,11 +402,7 @@ export default function OperationsMyProjectsPage() {
|
|
|
412
402
|
<div className="flex min-w-0 items-center gap-1.5">
|
|
413
403
|
<Avatar className="h-6 w-6 shrink-0">
|
|
414
404
|
<AvatarImage
|
|
415
|
-
src={
|
|
416
|
-
project.clientUserPhotoId
|
|
417
|
-
? getUserPhotoUrl(project.clientUserPhotoId)
|
|
418
|
-
: getPersonAvatarUrl(project.clientAvatarId)
|
|
419
|
-
}
|
|
405
|
+
src={getPersonAvatarUrl(project.clientAvatarId)}
|
|
420
406
|
alt={project.clientName ?? ''}
|
|
421
407
|
/>
|
|
422
408
|
<AvatarFallback className="text-[9px] font-medium">
|
|
@@ -6,7 +6,6 @@ import {
|
|
|
6
6
|
PaginationFooter,
|
|
7
7
|
SearchBar,
|
|
8
8
|
} from '@/components/entity-list';
|
|
9
|
-
import { RichTextEditor } from '@/components/rich-text-editor';
|
|
10
9
|
import { Button } from '@/components/ui/button';
|
|
11
10
|
import { Card, CardContent } from '@/components/ui/card';
|
|
12
11
|
import {
|
|
@@ -44,7 +43,7 @@ import {
|
|
|
44
43
|
} from '@/components/ui/table';
|
|
45
44
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
|
46
45
|
import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group';
|
|
47
|
-
import {
|
|
46
|
+
import { RichTextEditor } from '@/components/rich-text-editor';
|
|
48
47
|
import {
|
|
49
48
|
closestCenter,
|
|
50
49
|
DndContext,
|
|
@@ -87,14 +86,15 @@ import { useCallback, useMemo, useState } from 'react';
|
|
|
87
86
|
import { OperationsHeader } from '../_components/operations-header';
|
|
88
87
|
import { StatusBadge } from '../_components/status-badge';
|
|
89
88
|
import {
|
|
90
|
-
TaskCommentsSection,
|
|
91
89
|
TaskDetailSheet,
|
|
90
|
+
TaskCommentsSection,
|
|
92
91
|
type TaskDetailSheetData,
|
|
93
92
|
} from '../_components/task-detail-sheet';
|
|
94
93
|
import { TaskFileAttachments } from '../_components/task-file-attachments';
|
|
95
94
|
import { TimesheetEntryCreateSheet } from '../_components/timesheet-entry-create-sheet';
|
|
96
95
|
import { fetchOperations, mutateOperations } from '../_lib/api';
|
|
97
96
|
import { useMentionItems } from '../_lib/hooks/use-mention-items';
|
|
97
|
+
import { usePersistedPageSize } from '@/hooks/use-persisted-page-size';
|
|
98
98
|
import type {
|
|
99
99
|
OperationsCollaborator,
|
|
100
100
|
OperationsProjectOption,
|
|
@@ -102,7 +102,6 @@ import type {
|
|
|
102
102
|
PaginatedResponse,
|
|
103
103
|
} from '../_lib/types';
|
|
104
104
|
import { formatDate, getStatusBadgeClass } from '../_lib/utils/format';
|
|
105
|
-
import { getTaskDescriptionPreview } from '../_lib/utils/task-ui';
|
|
106
105
|
|
|
107
106
|
type TaskViewMode = 'table' | 'cards' | 'board';
|
|
108
107
|
|
|
@@ -706,13 +705,9 @@ export default function OperationsMyTasksPage() {
|
|
|
706
705
|
[inlineCreateName, request, refetchBoard]
|
|
707
706
|
);
|
|
708
707
|
|
|
709
|
-
const openCreateTaskForm = useCallback((
|
|
708
|
+
const openCreateTaskForm = useCallback(() => {
|
|
710
709
|
setEditingTaskId(null);
|
|
711
710
|
setSelectedTask(null);
|
|
712
|
-
setTaskFormData((prev) => ({
|
|
713
|
-
...EMPTY_TASK_FORM,
|
|
714
|
-
...(status ? { status } : { status: prev.status }),
|
|
715
|
-
}));
|
|
716
711
|
setTaskFormOpen(true);
|
|
717
712
|
}, []);
|
|
718
713
|
|
|
@@ -938,7 +933,7 @@ export default function OperationsMyTasksPage() {
|
|
|
938
933
|
{(isOver) => (
|
|
939
934
|
<div
|
|
940
935
|
className={[
|
|
941
|
-
'flex min-h-
|
|
936
|
+
'flex min-h-128 flex-col overflow-hidden rounded-3xl border bg-linear-to-b p-3 transition-all',
|
|
942
937
|
getColumnClassName(column.id),
|
|
943
938
|
isOver
|
|
944
939
|
? 'border-primary shadow-lg ring-2 ring-primary/15'
|
|
@@ -968,14 +963,17 @@ export default function OperationsMyTasksPage() {
|
|
|
968
963
|
<button
|
|
969
964
|
type="button"
|
|
970
965
|
className="flex size-5 cursor-pointer items-center justify-center rounded-full text-muted-foreground transition hover:bg-muted hover:text-foreground"
|
|
971
|
-
onClick={() =>
|
|
966
|
+
onClick={() => {
|
|
967
|
+
setInlineCreateColumn(column.id);
|
|
968
|
+
setInlineCreateName('');
|
|
969
|
+
}}
|
|
972
970
|
>
|
|
973
971
|
<Plus className="size-3.5" />
|
|
974
972
|
</button>
|
|
975
973
|
</div>
|
|
976
974
|
</div>
|
|
977
975
|
|
|
978
|
-
<div className="flex
|
|
976
|
+
<div className="flex flex-1 flex-col gap-2">
|
|
979
977
|
<AnimatePresence initial={false}>
|
|
980
978
|
{boardColumns[column.id].map((task) => {
|
|
981
979
|
const taskTags = task.tags
|
|
@@ -1027,8 +1025,9 @@ export default function OperationsMyTasksPage() {
|
|
|
1027
1025
|
</p>
|
|
1028
1026
|
{task.description ? (
|
|
1029
1027
|
<p className="line-clamp-2 text-xs leading-5 text-muted-foreground">
|
|
1030
|
-
{
|
|
1031
|
-
|
|
1028
|
+
{task.description.replace(
|
|
1029
|
+
/<[^>]*>/g,
|
|
1030
|
+
''
|
|
1032
1031
|
)}
|
|
1033
1032
|
</p>
|
|
1034
1033
|
) : null}
|
|
@@ -1152,14 +1151,76 @@ export default function OperationsMyTasksPage() {
|
|
|
1152
1151
|
</AnimatePresence>
|
|
1153
1152
|
</div>
|
|
1154
1153
|
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1154
|
+
{inlineCreateColumn === column.id ? (
|
|
1155
|
+
<div className="space-y-1.5 rounded-lg border bg-card p-2">
|
|
1156
|
+
<Input
|
|
1157
|
+
autoFocus
|
|
1158
|
+
placeholder={detailT('taskForm.namePlaceholder')}
|
|
1159
|
+
value={inlineCreateName}
|
|
1160
|
+
onChange={(e) =>
|
|
1161
|
+
setInlineCreateName(e.target.value)
|
|
1162
|
+
}
|
|
1163
|
+
onKeyDown={(e) => {
|
|
1164
|
+
if (e.key === 'Enter') {
|
|
1165
|
+
e.preventDefault();
|
|
1166
|
+
void handleInlineCreateTask(column.id);
|
|
1167
|
+
} else if (e.key === 'Escape') {
|
|
1168
|
+
setInlineCreateColumn(null);
|
|
1169
|
+
setInlineCreateName('');
|
|
1170
|
+
}
|
|
1171
|
+
}}
|
|
1172
|
+
onBlur={() => {
|
|
1173
|
+
if (!inlineCreateName.trim()) {
|
|
1174
|
+
setInlineCreateColumn(null);
|
|
1175
|
+
setInlineCreateName('');
|
|
1176
|
+
}
|
|
1177
|
+
}}
|
|
1178
|
+
disabled={inlineCreateLoading}
|
|
1179
|
+
className="h-8 text-sm"
|
|
1180
|
+
/>
|
|
1181
|
+
<div className="flex gap-1">
|
|
1182
|
+
<Button
|
|
1183
|
+
type="button"
|
|
1184
|
+
size="sm"
|
|
1185
|
+
className="h-7 px-2 text-xs"
|
|
1186
|
+
disabled={
|
|
1187
|
+
!inlineCreateName.trim() || inlineCreateLoading
|
|
1188
|
+
}
|
|
1189
|
+
onMouseDown={(e) => e.preventDefault()}
|
|
1190
|
+
onClick={() =>
|
|
1191
|
+
void handleInlineCreateTask(column.id)
|
|
1192
|
+
}
|
|
1193
|
+
>
|
|
1194
|
+
{t('actions.create')}
|
|
1195
|
+
</Button>
|
|
1196
|
+
<Button
|
|
1197
|
+
type="button"
|
|
1198
|
+
variant="ghost"
|
|
1199
|
+
size="sm"
|
|
1200
|
+
className="h-7 px-2 text-xs"
|
|
1201
|
+
onMouseDown={(e) => e.preventDefault()}
|
|
1202
|
+
onClick={() => {
|
|
1203
|
+
setInlineCreateColumn(null);
|
|
1204
|
+
setInlineCreateName('');
|
|
1205
|
+
}}
|
|
1206
|
+
>
|
|
1207
|
+
{commonT('actions.cancel')}
|
|
1208
|
+
</Button>
|
|
1209
|
+
</div>
|
|
1210
|
+
</div>
|
|
1211
|
+
) : (
|
|
1212
|
+
<button
|
|
1213
|
+
type="button"
|
|
1214
|
+
className="flex w-full cursor-pointer items-center gap-1 rounded-md px-1 py-1 text-xs text-muted-foreground transition hover:bg-muted hover:text-foreground"
|
|
1215
|
+
onClick={() => {
|
|
1216
|
+
setInlineCreateColumn(column.id);
|
|
1217
|
+
setInlineCreateName('');
|
|
1218
|
+
}}
|
|
1219
|
+
>
|
|
1220
|
+
<Plus className="size-3" />
|
|
1221
|
+
{t('actions.create')}
|
|
1222
|
+
</button>
|
|
1223
|
+
)}
|
|
1163
1224
|
</div>
|
|
1164
1225
|
)}
|
|
1165
1226
|
</DroppableColumn>
|
|
@@ -1215,7 +1276,7 @@ export default function OperationsMyTasksPage() {
|
|
|
1215
1276
|
|
|
1216
1277
|
{task.description ? (
|
|
1217
1278
|
<p className="line-clamp-3 text-sm text-muted-foreground">
|
|
1218
|
-
{
|
|
1279
|
+
{task.description}
|
|
1219
1280
|
</p>
|
|
1220
1281
|
) : null}
|
|
1221
1282
|
|
|
@@ -1264,7 +1325,7 @@ export default function OperationsMyTasksPage() {
|
|
|
1264
1325
|
<div className="truncate font-medium">{task.name}</div>
|
|
1265
1326
|
{task.description ? (
|
|
1266
1327
|
<div className="truncate text-xs text-muted-foreground">
|
|
1267
|
-
{
|
|
1328
|
+
{task.description}
|
|
1268
1329
|
</div>
|
|
1269
1330
|
) : null}
|
|
1270
1331
|
</div>
|
|
@@ -1465,6 +1526,7 @@ export default function OperationsMyTasksPage() {
|
|
|
1465
1526
|
</DialogContent>
|
|
1466
1527
|
</Dialog>
|
|
1467
1528
|
|
|
1529
|
+
|
|
1468
1530
|
<Sheet
|
|
1469
1531
|
open={taskFormOpen}
|
|
1470
1532
|
onOpenChange={(open) => {
|
|
@@ -194,15 +194,9 @@ function DroppableProjectColumn({
|
|
|
194
194
|
return <div ref={setNodeRef}>{children(isOver)}</div>;
|
|
195
195
|
}
|
|
196
196
|
|
|
197
|
-
function getPersonAvatarUrl(avatarId?: number | null) {
|
|
197
|
+
function getPersonAvatarUrl(avatarId?: number | null): string {
|
|
198
198
|
return typeof avatarId === 'number' && avatarId > 0
|
|
199
199
|
? `${process.env.NEXT_PUBLIC_API_BASE_URL}/person/avatar/${avatarId}`
|
|
200
|
-
: undefined;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
function getUserPhotoUrl(photoId?: number | null) {
|
|
204
|
-
return typeof photoId === 'number' && photoId > 0
|
|
205
|
-
? `${process.env.NEXT_PUBLIC_API_BASE_URL}/user/avatar/${photoId}`
|
|
206
200
|
: '/placeholder.png';
|
|
207
201
|
}
|
|
208
202
|
|
|
@@ -512,31 +506,9 @@ export default function OperationsProjectsPage() {
|
|
|
512
506
|
>
|
|
513
507
|
<CardContent className="space-y-2 p-3">
|
|
514
508
|
<div className="truncate text-sm font-semibold">{project.name}</div>
|
|
515
|
-
<div className="
|
|
516
|
-
{project.clientName
|
|
517
|
-
|
|
518
|
-
<span className="truncate">{project.code || '—'}</span>
|
|
519
|
-
<span>•</span>
|
|
520
|
-
<Avatar className="h-4 w-4 shrink-0">
|
|
521
|
-
<AvatarImage
|
|
522
|
-
src={
|
|
523
|
-
project.clientUserPhotoId
|
|
524
|
-
? getUserPhotoUrl(project.clientUserPhotoId)
|
|
525
|
-
: getPersonAvatarUrl(project.clientAvatarId)
|
|
526
|
-
}
|
|
527
|
-
alt={project.clientName}
|
|
528
|
-
/>
|
|
529
|
-
<AvatarFallback className="text-[8px] font-medium">
|
|
530
|
-
{getInitials(project.clientName)}
|
|
531
|
-
</AvatarFallback>
|
|
532
|
-
</Avatar>
|
|
533
|
-
<span className="truncate">{project.clientName}</span>
|
|
534
|
-
</>
|
|
535
|
-
) : (
|
|
536
|
-
<span className="truncate">
|
|
537
|
-
{project.code || commonT('labels.notAvailable')}
|
|
538
|
-
</span>
|
|
539
|
-
)}
|
|
509
|
+
<div className="truncate text-xs text-muted-foreground">
|
|
510
|
+
{[project.code, project.clientName].filter(Boolean).join(' • ') ||
|
|
511
|
+
commonT('labels.notAvailable')}
|
|
540
512
|
</div>
|
|
541
513
|
<div className="flex items-center justify-between gap-2 pt-1">
|
|
542
514
|
<StatusBadge
|
|
@@ -786,11 +758,7 @@ export default function OperationsProjectsPage() {
|
|
|
786
758
|
</span>
|
|
787
759
|
<Avatar className="h-5 w-5 shrink-0">
|
|
788
760
|
<AvatarImage
|
|
789
|
-
src={
|
|
790
|
-
project.clientUserPhotoId
|
|
791
|
-
? getUserPhotoUrl(project.clientUserPhotoId)
|
|
792
|
-
: getPersonAvatarUrl(project.clientAvatarId)
|
|
793
|
-
}
|
|
761
|
+
src={getPersonAvatarUrl(project.clientAvatarId)}
|
|
794
762
|
alt={project.clientName ?? ''}
|
|
795
763
|
/>
|
|
796
764
|
<AvatarFallback className="text-[9px] font-medium">
|
|
@@ -995,11 +963,7 @@ export default function OperationsProjectsPage() {
|
|
|
995
963
|
<div className="flex min-w-0 items-center gap-1.5">
|
|
996
964
|
<Avatar className="h-6 w-6 shrink-0">
|
|
997
965
|
<AvatarImage
|
|
998
|
-
src={
|
|
999
|
-
project.clientUserPhotoId
|
|
1000
|
-
? getUserPhotoUrl(project.clientUserPhotoId)
|
|
1001
|
-
: getPersonAvatarUrl(project.clientAvatarId)
|
|
1002
|
-
}
|
|
966
|
+
src={getPersonAvatarUrl(project.clientAvatarId)}
|
|
1003
967
|
alt={project.clientName ?? ''}
|
|
1004
968
|
/>
|
|
1005
969
|
<AvatarFallback className="text-[9px] font-medium">
|