@hed-hog/operations 0.0.2

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.
Files changed (49) hide show
  1. package/dist/index.d.ts +2 -0
  2. package/dist/index.d.ts.map +1 -0
  3. package/dist/index.js +18 -0
  4. package/dist/index.js.map +1 -0
  5. package/dist/operations.module.d.ts +3 -0
  6. package/dist/operations.module.d.ts.map +1 -0
  7. package/dist/operations.module.js +28 -0
  8. package/dist/operations.module.js.map +1 -0
  9. package/hedhog/data/menu.yaml +132 -0
  10. package/hedhog/data/role.yaml +7 -0
  11. package/hedhog/data/route.yaml +80 -0
  12. package/hedhog/frontend/app/_components/allocation-calendar.tsx.ejs +56 -0
  13. package/hedhog/frontend/app/_components/kanban-board.tsx.ejs +83 -0
  14. package/hedhog/frontend/app/_components/operations-header.tsx.ejs +29 -0
  15. package/hedhog/frontend/app/_components/section-card.tsx.ejs +32 -0
  16. package/hedhog/frontend/app/_components/status-badge.tsx.ejs +15 -0
  17. package/hedhog/frontend/app/_components/timesheet-entry-dialog.tsx.ejs +142 -0
  18. package/hedhog/frontend/app/_lib/hooks/use-operations-data.ts.ejs +41 -0
  19. package/hedhog/frontend/app/_lib/mocks/allocations.mock.ts.ejs +74 -0
  20. package/hedhog/frontend/app/_lib/mocks/contracts.mock.ts.ejs +74 -0
  21. package/hedhog/frontend/app/_lib/mocks/projects.mock.ts.ejs +60 -0
  22. package/hedhog/frontend/app/_lib/mocks/tasks.mock.ts.ejs +88 -0
  23. package/hedhog/frontend/app/_lib/mocks/timesheets.mock.ts.ejs +84 -0
  24. package/hedhog/frontend/app/_lib/mocks/users.mock.ts.ejs +67 -0
  25. package/hedhog/frontend/app/_lib/services/contracts.service.ts.ejs +10 -0
  26. package/hedhog/frontend/app/_lib/services/projects.service.ts.ejs +10 -0
  27. package/hedhog/frontend/app/_lib/services/tasks.service.ts.ejs +10 -0
  28. package/hedhog/frontend/app/_lib/services/timesheets.service.ts.ejs +10 -0
  29. package/hedhog/frontend/app/_lib/types/operations.ts.ejs +95 -0
  30. package/hedhog/frontend/app/_lib/utils/format.ts.ejs +25 -0
  31. package/hedhog/frontend/app/_lib/utils/metrics.ts.ejs +103 -0
  32. package/hedhog/frontend/app/_lib/utils/status.ts.ejs +80 -0
  33. package/hedhog/frontend/app/allocations/page.tsx.ejs +99 -0
  34. package/hedhog/frontend/app/approvals/page.tsx.ejs +147 -0
  35. package/hedhog/frontend/app/contracts/[id]/page.tsx.ejs +108 -0
  36. package/hedhog/frontend/app/contracts/page.tsx.ejs +124 -0
  37. package/hedhog/frontend/app/layout.tsx.ejs +9 -0
  38. package/hedhog/frontend/app/page.tsx.ejs +177 -0
  39. package/hedhog/frontend/app/projects/[id]/page.tsx.ejs +186 -0
  40. package/hedhog/frontend/app/projects/page.tsx.ejs +111 -0
  41. package/hedhog/frontend/app/tasks/page.tsx.ejs +47 -0
  42. package/hedhog/frontend/app/timesheets/page.tsx.ejs +126 -0
  43. package/hedhog/frontend/messages/en.json +142 -0
  44. package/hedhog/frontend/messages/pt.json +142 -0
  45. package/package.json +37 -0
  46. package/src/index.ts +1 -0
  47. package/src/language/en.json +8 -0
  48. package/src/language/pt.json +8 -0
  49. package/src/operations.module.ts +15 -0
@@ -0,0 +1,2 @@
1
+ export * from './operations.module';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./operations.module"), exports);
18
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,sDAAoC"}
@@ -0,0 +1,3 @@
1
+ export declare class OperationsModule {
2
+ }
3
+ //# sourceMappingURL=operations.module.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"operations.module.d.ts","sourceRoot":"","sources":["../src/operations.module.ts"],"names":[],"mappings":"AAMA,qBAQa,gBAAgB;CAAG"}
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.OperationsModule = void 0;
10
+ const common_1 = require("@nestjs/common");
11
+ const config_1 = require("@nestjs/config");
12
+ const api_locale_1 = require("@hed-hog/api-locale");
13
+ const api_pagination_1 = require("@hed-hog/api-pagination");
14
+ const api_prisma_1 = require("@hed-hog/api-prisma");
15
+ let OperationsModule = class OperationsModule {
16
+ };
17
+ exports.OperationsModule = OperationsModule;
18
+ exports.OperationsModule = OperationsModule = __decorate([
19
+ (0, common_1.Module)({
20
+ imports: [
21
+ config_1.ConfigModule.forRoot(),
22
+ (0, common_1.forwardRef)(() => api_pagination_1.PaginationModule),
23
+ (0, common_1.forwardRef)(() => api_prisma_1.PrismaModule),
24
+ (0, common_1.forwardRef)(() => api_locale_1.LocaleModule)
25
+ ]
26
+ })
27
+ ], OperationsModule);
28
+ //# sourceMappingURL=operations.module.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"operations.module.js","sourceRoot":"","sources":["../src/operations.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAoD;AACpD,2CAA8C;AAC9C,oDAAmD;AACnD,4DAA2D;AAC3D,oDAAmD;AAU5C,IAAM,gBAAgB,GAAtB,MAAM,gBAAgB;CAAG,CAAA;AAAnB,4CAAgB;2BAAhB,gBAAgB;IAR5B,IAAA,eAAM,EAAC;QACN,OAAO,EAAE;YACP,qBAAY,CAAC,OAAO,EAAE;YACtB,IAAA,mBAAU,EAAC,GAAG,EAAE,CAAC,iCAAgB,CAAC;YAClC,IAAA,mBAAU,EAAC,GAAG,EAAE,CAAC,yBAAY,CAAC;YAC9B,IAAA,mBAAU,EAAC,GAAG,EAAE,CAAC,yBAAY,CAAC;SAC/B;KACF,CAAC;GACW,gBAAgB,CAAG"}
@@ -0,0 +1,132 @@
1
+ - icon: briefcase-business
2
+ name:
3
+ en: Operations
4
+ pt: Operacoes
5
+ slug: /operations
6
+ url: /operations
7
+ order: 110
8
+ relations:
9
+ role:
10
+ - where:
11
+ slug: admin
12
+ - where:
13
+ slug: admin-operations
14
+
15
+ - menu_id:
16
+ where:
17
+ slug: /operations
18
+ icon: layout-dashboard
19
+ url: /operations
20
+ name:
21
+ en: Dashboard
22
+ pt: Dashboard
23
+ slug: /operations/dashboard
24
+ order: 111
25
+ relations:
26
+ role:
27
+ - where:
28
+ slug: admin
29
+ - where:
30
+ slug: admin-operations
31
+
32
+ - menu_id:
33
+ where:
34
+ slug: /operations
35
+ icon: file-text
36
+ url: /operations/contracts
37
+ name:
38
+ en: Contracts
39
+ pt: Contratos
40
+ slug: /operations/contracts
41
+ order: 112
42
+ relations:
43
+ role:
44
+ - where:
45
+ slug: admin
46
+ - where:
47
+ slug: admin-operations
48
+
49
+ - menu_id:
50
+ where:
51
+ slug: /operations
52
+ icon: folders
53
+ url: /operations/projects
54
+ name:
55
+ en: Projects
56
+ pt: Projetos
57
+ slug: /operations/projects
58
+ order: 113
59
+ relations:
60
+ role:
61
+ - where:
62
+ slug: admin
63
+ - where:
64
+ slug: admin-operations
65
+
66
+ - menu_id:
67
+ where:
68
+ slug: /operations
69
+ icon: users-round
70
+ url: /operations/allocations
71
+ name:
72
+ en: Allocations
73
+ pt: Alocacoes
74
+ slug: /operations/allocations
75
+ order: 114
76
+ relations:
77
+ role:
78
+ - where:
79
+ slug: admin
80
+ - where:
81
+ slug: admin-operations
82
+
83
+ - menu_id:
84
+ where:
85
+ slug: /operations
86
+ icon: kanban-square
87
+ url: /operations/tasks
88
+ name:
89
+ en: Tasks
90
+ pt: Tarefas
91
+ slug: /operations/tasks
92
+ order: 115
93
+ relations:
94
+ role:
95
+ - where:
96
+ slug: admin
97
+ - where:
98
+ slug: admin-operations
99
+
100
+ - menu_id:
101
+ where:
102
+ slug: /operations
103
+ icon: clock-3
104
+ url: /operations/timesheets
105
+ name:
106
+ en: Timesheets
107
+ pt: Timesheets
108
+ slug: /operations/timesheets
109
+ order: 116
110
+ relations:
111
+ role:
112
+ - where:
113
+ slug: admin
114
+ - where:
115
+ slug: admin-operations
116
+
117
+ - menu_id:
118
+ where:
119
+ slug: /operations
120
+ icon: file-check-2
121
+ url: /operations/approvals
122
+ name:
123
+ en: Approvals
124
+ pt: Aprovacoes
125
+ slug: /operations/approvals
126
+ order: 117
127
+ relations:
128
+ role:
129
+ - where:
130
+ slug: admin
131
+ - where:
132
+ slug: admin-operations
@@ -0,0 +1,7 @@
1
+ - slug: admin-operations
2
+ name:
3
+ en: Operations Administrator
4
+ pt: Administrador de Operacoes
5
+ description:
6
+ en: Administrator with full access to operations management.
7
+ pt: Administrador com acesso total a gestao de operacoes.
@@ -0,0 +1,80 @@
1
+ - url: /operations
2
+ method: GET
3
+ relations:
4
+ role:
5
+ - where:
6
+ slug: admin
7
+ - where:
8
+ slug: admin-operations
9
+
10
+ - url: /operations/contracts
11
+ method: GET
12
+ relations:
13
+ role:
14
+ - where:
15
+ slug: admin
16
+ - where:
17
+ slug: admin-operations
18
+
19
+ - url: /operations/contracts/:id
20
+ method: GET
21
+ relations:
22
+ role:
23
+ - where:
24
+ slug: admin
25
+ - where:
26
+ slug: admin-operations
27
+
28
+ - url: /operations/projects
29
+ method: GET
30
+ relations:
31
+ role:
32
+ - where:
33
+ slug: admin
34
+ - where:
35
+ slug: admin-operations
36
+
37
+ - url: /operations/projects/:id
38
+ method: GET
39
+ relations:
40
+ role:
41
+ - where:
42
+ slug: admin
43
+ - where:
44
+ slug: admin-operations
45
+
46
+ - url: /operations/allocations
47
+ method: GET
48
+ relations:
49
+ role:
50
+ - where:
51
+ slug: admin
52
+ - where:
53
+ slug: admin-operations
54
+
55
+ - url: /operations/tasks
56
+ method: GET
57
+ relations:
58
+ role:
59
+ - where:
60
+ slug: admin
61
+ - where:
62
+ slug: admin-operations
63
+
64
+ - url: /operations/timesheets
65
+ method: GET
66
+ relations:
67
+ role:
68
+ - where:
69
+ slug: admin
70
+ - where:
71
+ slug: admin-operations
72
+
73
+ - url: /operations/approvals
74
+ method: GET
75
+ relations:
76
+ role:
77
+ - where:
78
+ slug: admin
79
+ - where:
80
+ slug: admin-operations
@@ -0,0 +1,56 @@
1
+ import { formatHours } from '../_lib/utils/format';
2
+ import type {
3
+ Allocation,
4
+ OperationsUser,
5
+ Project,
6
+ } from '../_lib/types/operations';
7
+
8
+ const weekdays = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri'];
9
+
10
+ interface AllocationCalendarProps {
11
+ allocations: Allocation[];
12
+ users: OperationsUser[];
13
+ projects: Project[];
14
+ }
15
+
16
+ export function AllocationCalendar({
17
+ allocations,
18
+ users,
19
+ projects,
20
+ }: AllocationCalendarProps) {
21
+ return (
22
+ <div className="overflow-x-auto">
23
+ <div className="min-w-[760px] rounded-xl border">
24
+ <div className="grid grid-cols-6 border-b bg-muted/40 text-sm font-medium">
25
+ <div className="p-3">Team Member</div>
26
+ {weekdays.map((day) => (
27
+ <div key={day} className="border-l p-3">
28
+ {day}
29
+ </div>
30
+ ))}
31
+ </div>
32
+ {allocations.map((allocation) => {
33
+ const user = users.find((item) => item.id === allocation.userId);
34
+ const project = projects.find((item) => item.id === allocation.projectId);
35
+ const dailyHours = allocation.weeklyHours / 5;
36
+
37
+ return (
38
+ <div key={allocation.id} className="grid grid-cols-6 border-b text-sm">
39
+ <div className="p-3">
40
+ <p className="font-medium">{user?.name}</p>
41
+ <p className="text-xs text-muted-foreground">{project?.name}</p>
42
+ </div>
43
+ {weekdays.map((day) => (
44
+ <div key={day} className="border-l p-3">
45
+ <div className="rounded-md bg-blue-100 px-2 py-1 text-blue-700">
46
+ {formatHours(dailyHours)}
47
+ </div>
48
+ </div>
49
+ ))}
50
+ </div>
51
+ );
52
+ })}
53
+ </div>
54
+ </div>
55
+ );
56
+ }
@@ -0,0 +1,83 @@
1
+ import { formatDate, formatHours } from '../_lib/utils/format';
2
+ import {
3
+ getTaskBadgeClasses,
4
+ getTaskStatusLabel,
5
+ } from '../_lib/utils/status';
6
+ import type { OperationsUser, Task, TaskStatus } from '../_lib/types/operations';
7
+ import { StatusBadge } from './status-badge';
8
+
9
+ const columns: TaskStatus[] = [
10
+ 'backlog',
11
+ 'todo',
12
+ 'in-progress',
13
+ 'review',
14
+ 'done',
15
+ ];
16
+
17
+ interface KanbanBoardProps {
18
+ tasks: Task[];
19
+ users: OperationsUser[];
20
+ }
21
+
22
+ export function KanbanBoard({ tasks, users }: KanbanBoardProps) {
23
+ return (
24
+ <div className="grid gap-4 xl:grid-cols-5">
25
+ {columns.map((column) => {
26
+ const columnTasks = tasks.filter((task) => task.status === column);
27
+
28
+ return (
29
+ <div
30
+ key={column}
31
+ className="rounded-xl border bg-muted/30 p-3 shadow-sm"
32
+ >
33
+ <div className="mb-3 flex items-center justify-between">
34
+ <h3 className="text-sm font-semibold">{getTaskStatusLabel(column)}</h3>
35
+ <span className="text-xs text-muted-foreground">
36
+ {columnTasks.length}
37
+ </span>
38
+ </div>
39
+ <div className="space-y-3">
40
+ {columnTasks.map((task) => {
41
+ const assignedUser = users.find(
42
+ (user) => user.id === task.assignedUserId
43
+ );
44
+
45
+ return (
46
+ <div
47
+ key={task.id}
48
+ className="rounded-lg border bg-background p-3 shadow-sm"
49
+ >
50
+ <div className="space-y-2">
51
+ <div className="flex items-start justify-between gap-2">
52
+ <p className="text-sm font-medium">{task.title}</p>
53
+ <StatusBadge
54
+ label={getTaskStatusLabel(task.status)}
55
+ className={getTaskBadgeClasses(task.status)}
56
+ />
57
+ </div>
58
+ <div className="flex flex-wrap gap-2">
59
+ {task.labels.map((label) => (
60
+ <span
61
+ key={label}
62
+ className="rounded-full bg-slate-100 px-2 py-0.5 text-[11px] text-slate-700"
63
+ >
64
+ {label}
65
+ </span>
66
+ ))}
67
+ </div>
68
+ <div className="text-xs text-muted-foreground">
69
+ <p>{assignedUser?.name ?? 'Unassigned'}</p>
70
+ <p>{formatDate(task.dueDate)}</p>
71
+ <p>{formatHours(task.estimatedHours)}</p>
72
+ </div>
73
+ </div>
74
+ </div>
75
+ );
76
+ })}
77
+ </div>
78
+ </div>
79
+ );
80
+ })}
81
+ </div>
82
+ );
83
+ }
@@ -0,0 +1,29 @@
1
+ import { PageHeader } from '@/components/entity-list';
2
+ import { ReactNode } from 'react';
3
+
4
+ interface OperationsHeaderProps {
5
+ title: string;
6
+ description: string;
7
+ current: string;
8
+ actions?: ReactNode;
9
+ }
10
+
11
+ export function OperationsHeader({
12
+ title,
13
+ description,
14
+ current,
15
+ actions,
16
+ }: OperationsHeaderProps) {
17
+ return (
18
+ <PageHeader
19
+ title={title}
20
+ description={description}
21
+ actions={actions}
22
+ breadcrumbs={[
23
+ { label: 'Home', href: '/' },
24
+ { label: 'Operations', href: '/operations' },
25
+ { label: current },
26
+ ]}
27
+ />
28
+ );
29
+ }
@@ -0,0 +1,32 @@
1
+ import {
2
+ Card,
3
+ CardContent,
4
+ CardDescription,
5
+ CardHeader,
6
+ CardTitle,
7
+ } from '@/components/ui/card';
8
+ import { ReactNode } from 'react';
9
+
10
+ interface SectionCardProps {
11
+ title: string;
12
+ description?: string;
13
+ children: ReactNode;
14
+ className?: string;
15
+ }
16
+
17
+ export function SectionCard({
18
+ title,
19
+ description,
20
+ children,
21
+ className,
22
+ }: SectionCardProps) {
23
+ return (
24
+ <Card className={className}>
25
+ <CardHeader>
26
+ <CardTitle>{title}</CardTitle>
27
+ {description ? <CardDescription>{description}</CardDescription> : null}
28
+ </CardHeader>
29
+ <CardContent>{children}</CardContent>
30
+ </Card>
31
+ );
32
+ }
@@ -0,0 +1,15 @@
1
+ import { Badge } from '@/components/ui/badge';
2
+ import { cn } from '@/lib/utils';
3
+
4
+ interface StatusBadgeProps {
5
+ label: string;
6
+ className?: string;
7
+ }
8
+
9
+ export function StatusBadge({ label, className }: StatusBadgeProps) {
10
+ return (
11
+ <Badge variant="outline" className={cn('border-transparent', className)}>
12
+ {label}
13
+ </Badge>
14
+ );
15
+ }
@@ -0,0 +1,142 @@
1
+ 'use client';
2
+
3
+ import { Button } from '@/components/ui/button';
4
+ import {
5
+ Dialog,
6
+ DialogContent,
7
+ DialogDescription,
8
+ DialogFooter,
9
+ DialogHeader,
10
+ DialogTitle,
11
+ DialogTrigger,
12
+ } from '@/components/ui/dialog';
13
+ import { Input } from '@/components/ui/input';
14
+ import { Label } from '@/components/ui/label';
15
+ import {
16
+ Select,
17
+ SelectContent,
18
+ SelectItem,
19
+ SelectTrigger,
20
+ SelectValue,
21
+ } from '@/components/ui/select';
22
+ import { Textarea } from '@/components/ui/textarea';
23
+ import { Plus } from 'lucide-react';
24
+ import { useState } from 'react';
25
+ import type { OperationsUser, Project, Task } from '../_lib/types/operations';
26
+
27
+ interface TimesheetEntryDialogProps {
28
+ users: OperationsUser[];
29
+ projects: Project[];
30
+ tasks: Task[];
31
+ }
32
+
33
+ export function TimesheetEntryDialog({
34
+ users,
35
+ projects,
36
+ tasks,
37
+ }: TimesheetEntryDialogProps) {
38
+ const [open, setOpen] = useState(false);
39
+ const [projectId, setProjectId] = useState(projects[0]?.id ?? '');
40
+
41
+ const availableTasks = tasks.filter((task) => task.projectId === projectId);
42
+
43
+ return (
44
+ <Dialog open={open} onOpenChange={setOpen}>
45
+ <DialogTrigger asChild>
46
+ <Button>
47
+ <Plus className="mr-2 h-4 w-4" />
48
+ Add Timesheet Entry
49
+ </Button>
50
+ </DialogTrigger>
51
+ <DialogContent className="sm:max-w-2xl">
52
+ <DialogHeader>
53
+ <DialogTitle>Add Timesheet Entry</DialogTitle>
54
+ <DialogDescription>
55
+ Mock-only modal wired for a future API-backed create flow.
56
+ </DialogDescription>
57
+ </DialogHeader>
58
+ <div className="grid gap-4 md:grid-cols-2">
59
+ <div className="space-y-2">
60
+ <Label>User</Label>
61
+ <Select defaultValue={users[0]?.id}>
62
+ <SelectTrigger>
63
+ <SelectValue />
64
+ </SelectTrigger>
65
+ <SelectContent>
66
+ {users.map((user) => (
67
+ <SelectItem key={user.id} value={user.id}>
68
+ {user.name}
69
+ </SelectItem>
70
+ ))}
71
+ </SelectContent>
72
+ </Select>
73
+ </div>
74
+ <div className="space-y-2">
75
+ <Label>Date</Label>
76
+ <Input defaultValue="2026-03-16" type="date" />
77
+ </div>
78
+ <div className="space-y-2">
79
+ <Label>Project</Label>
80
+ <Select value={projectId} onValueChange={setProjectId}>
81
+ <SelectTrigger>
82
+ <SelectValue />
83
+ </SelectTrigger>
84
+ <SelectContent>
85
+ {projects.map((project) => (
86
+ <SelectItem key={project.id} value={project.id}>
87
+ {project.name}
88
+ </SelectItem>
89
+ ))}
90
+ </SelectContent>
91
+ </Select>
92
+ </div>
93
+ <div className="space-y-2">
94
+ <Label>Task</Label>
95
+ <Select defaultValue={availableTasks[0]?.id}>
96
+ <SelectTrigger>
97
+ <SelectValue />
98
+ </SelectTrigger>
99
+ <SelectContent>
100
+ {availableTasks.map((task) => (
101
+ <SelectItem key={task.id} value={task.id}>
102
+ {task.title}
103
+ </SelectItem>
104
+ ))}
105
+ </SelectContent>
106
+ </Select>
107
+ </div>
108
+ <div className="space-y-2">
109
+ <Label>Hours</Label>
110
+ <Input defaultValue="6" min="0" step="0.5" type="number" />
111
+ </div>
112
+ <div className="space-y-2">
113
+ <Label>Status</Label>
114
+ <Select defaultValue="pending">
115
+ <SelectTrigger>
116
+ <SelectValue />
117
+ </SelectTrigger>
118
+ <SelectContent>
119
+ <SelectItem value="pending">Pending</SelectItem>
120
+ <SelectItem value="approved">Approved</SelectItem>
121
+ <SelectItem value="rejected">Rejected</SelectItem>
122
+ </SelectContent>
123
+ </Select>
124
+ </div>
125
+ <div className="space-y-2 md:col-span-2">
126
+ <Label>Description</Label>
127
+ <Textarea
128
+ defaultValue="Mock entry for sprint execution and internal review."
129
+ rows={4}
130
+ />
131
+ </div>
132
+ </div>
133
+ <DialogFooter>
134
+ <Button variant="outline" onClick={() => setOpen(false)}>
135
+ Cancel
136
+ </Button>
137
+ <Button onClick={() => setOpen(false)}>Save Mock Entry</Button>
138
+ </DialogFooter>
139
+ </DialogContent>
140
+ </Dialog>
141
+ );
142
+ }