realtimex-crm 0.13.8 → 0.14.1

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 (47) hide show
  1. package/dist/assets/DealList-CRBXwi1I.js +59 -0
  2. package/dist/assets/DealList-CRBXwi1I.js.map +1 -0
  3. package/dist/assets/index-5AmasdLr.css +1 -0
  4. package/dist/assets/index-D5uatqhL.js +166 -0
  5. package/dist/assets/{index-BrW7DPxi.js.map → index-D5uatqhL.js.map} +1 -1
  6. package/dist/index.html +1 -1
  7. package/dist/stats.html +1 -1
  8. package/package.json +1 -1
  9. package/src/components/atomic-crm/deals/DealCreate.tsx +5 -1
  10. package/src/components/atomic-crm/deals/DealShow.tsx +5 -1
  11. package/src/components/atomic-crm/integrations/WebhooksTab.tsx +10 -0
  12. package/src/components/atomic-crm/layout/Header.tsx +6 -0
  13. package/src/components/atomic-crm/notes/NoteCreate.tsx +3 -2
  14. package/src/components/atomic-crm/notes/NotesIterator.tsx +1 -1
  15. package/src/components/atomic-crm/providers/fakerest/dataGenerator/index.ts +4 -0
  16. package/src/components/atomic-crm/providers/fakerest/dataGenerator/taskActivity.ts +62 -0
  17. package/src/components/atomic-crm/providers/fakerest/dataGenerator/taskNotes.ts +29 -0
  18. package/src/components/atomic-crm/providers/fakerest/dataGenerator/tasks.ts +33 -8
  19. package/src/components/atomic-crm/providers/fakerest/dataGenerator/types.ts +4 -0
  20. package/src/components/atomic-crm/providers/supabase/dataProvider.ts +37 -0
  21. package/src/components/atomic-crm/root/CRM.tsx +12 -1
  22. package/src/components/atomic-crm/root/ConfigurationContext.tsx +10 -0
  23. package/src/components/atomic-crm/root/defaultConfiguration.ts +15 -0
  24. package/src/components/atomic-crm/tasks/MyTasksInput.tsx +30 -0
  25. package/src/components/atomic-crm/tasks/Task.tsx +20 -9
  26. package/src/components/atomic-crm/tasks/TaskActivityTimeline.tsx +91 -0
  27. package/src/components/atomic-crm/tasks/TaskAside.tsx +122 -0
  28. package/src/components/atomic-crm/tasks/TaskCreate.tsx +112 -0
  29. package/src/components/atomic-crm/tasks/TaskEdit.tsx +20 -1
  30. package/src/components/atomic-crm/tasks/TaskList.tsx +52 -0
  31. package/src/components/atomic-crm/tasks/TaskListTable.tsx +60 -0
  32. package/src/components/atomic-crm/tasks/TaskPriorityBadge.tsx +20 -0
  33. package/src/components/atomic-crm/tasks/TaskShow.tsx +71 -0
  34. package/src/components/atomic-crm/tasks/TaskStatusBadge.tsx +21 -0
  35. package/src/components/atomic-crm/tasks/index.ts +9 -0
  36. package/src/components/atomic-crm/types.ts +50 -0
  37. package/src/components/ui/visually-hidden.tsx +10 -0
  38. package/supabase/migrations/20251225085142_enhance_companies.sql +3 -3
  39. package/supabase/migrations/{20251226120000_fix_ambiguous_column.sql → 20251225110000_fix_ambiguous_column.sql} +2 -0
  40. package/supabase/migrations/20251225120000_enhance_tasks_schema.sql +111 -0
  41. package/supabase/migrations/20251225120001_enhance_tasks_logic.sql +109 -0
  42. package/supabase/migrations/20251225120002_enhance_tasks_webhooks.sql +72 -0
  43. package/supabase/migrations/20251225150000_add_taskNotes_attachments.sql +6 -0
  44. package/dist/assets/DealList-CyjZCmZS.js +0 -59
  45. package/dist/assets/DealList-CyjZCmZS.js.map +0 -1
  46. package/dist/assets/index-BrW7DPxi.js +0 -166
  47. package/dist/assets/index-u4GyWWrL.css +0 -1
@@ -0,0 +1,122 @@
1
+ import { Calendar, Building2, UserCircle, UserCheck, Pencil } from "lucide-react";
2
+ import { useRecordContext } from "ra-core";
3
+ import { useState, type ReactNode } from "react";
4
+ import { Button } from "@/components/ui/button";
5
+ import { DeleteButton } from "@/components/admin/delete-button";
6
+ import { ReferenceField } from "@/components/admin/reference-field";
7
+ import { TextField } from "@/components/admin/text-field";
8
+ import { DateField } from "@/components/admin/date-field";
9
+
10
+ import { AsideSection } from "../misc/AsideSection";
11
+ import type { Task } from "../types";
12
+ import { TaskEdit } from "./TaskEdit";
13
+
14
+ export const TaskAside = () => {
15
+ const record = useRecordContext<Task>();
16
+ const [editOpen, setEditOpen] = useState(false);
17
+
18
+ if (!record) return null;
19
+ return (
20
+ <div className="hidden sm:block w-64 min-w-64 text-sm">
21
+ <div className="mb-4 -ml-1">
22
+ <Button
23
+ variant="outline"
24
+ size="sm"
25
+ onClick={() => setEditOpen(true)}
26
+ className="flex items-center gap-2"
27
+ >
28
+ <Pencil className="h-4 w-4" />
29
+ Edit Task
30
+ </Button>
31
+ </div>
32
+
33
+ <TaskEdit
34
+ open={editOpen}
35
+ close={() => setEditOpen(false)}
36
+ taskId={record.id}
37
+ />
38
+
39
+ <AsideSection title="Task Info">
40
+ <InfoRow
41
+ icon={<Calendar className="w-4 h-4 text-muted-foreground" />}
42
+ label="Due Date"
43
+ value={<DateField source="due_date" />}
44
+ />
45
+ {record.done_date && (
46
+ <InfoRow
47
+ icon={<Calendar className="w-4 h-4 text-muted-foreground" />}
48
+ label="Completed"
49
+ value={<DateField source="done_date" />}
50
+ />
51
+ )}
52
+ </AsideSection>
53
+
54
+ <AsideSection title="Related">
55
+ <InfoRow
56
+ icon={<UserCircle className="w-4 h-4 text-muted-foreground" />}
57
+ label="Contact"
58
+ value={
59
+ <ReferenceField
60
+ source="contact_id"
61
+ reference="contacts"
62
+ link="show"
63
+ />
64
+ }
65
+ />
66
+ <InfoRow
67
+ icon={<Building2 className="w-4 h-4 text-muted-foreground" />}
68
+ label="Company"
69
+ value={
70
+ <ReferenceField source="contact_id" reference="contacts" link={false}>
71
+ <ReferenceField source="company_id" reference="companies" link="show">
72
+ <TextField source="name" />
73
+ </ReferenceField>
74
+ </ReferenceField>
75
+ }
76
+ />
77
+ </AsideSection>
78
+
79
+ <AsideSection title="Assignment">
80
+ <InfoRow
81
+ icon={<UserCheck className="w-4 h-4 text-muted-foreground" />}
82
+ label="Assigned To"
83
+ value={
84
+ <ReferenceField source="assigned_to" reference="sales" link={false} />
85
+ }
86
+ />
87
+ <InfoRow
88
+ icon={<UserCircle className="w-4 h-4 text-muted-foreground" />}
89
+ label="Created By"
90
+ value={
91
+ <ReferenceField source="sales_id" reference="sales" link={false} />
92
+ }
93
+ />
94
+ </AsideSection>
95
+
96
+ <div className="mt-6 pt-6 border-t hidden sm:flex flex-col gap-2 items-start">
97
+ <DeleteButton
98
+ className="h-6 cursor-pointer hover:bg-destructive/10! text-destructive! border-destructive! focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40"
99
+ size="sm"
100
+ />
101
+ </div>
102
+ </div>
103
+ );
104
+ };
105
+
106
+ const InfoRow = ({
107
+ icon,
108
+ label,
109
+ value,
110
+ }: {
111
+ icon: ReactNode;
112
+ label: string;
113
+ value: ReactNode;
114
+ }) => (
115
+ <div className="flex flex-col gap-1 mb-3">
116
+ <div className="flex items-center gap-2">
117
+ {icon}
118
+ <span className="text-xs text-muted-foreground">{label}</span>
119
+ </div>
120
+ <div className="pl-6 text-sm">{value}</div>
121
+ </div>
122
+ );
@@ -0,0 +1,112 @@
1
+ import { AutocompleteInput } from "@/components/admin/autocomplete-input";
2
+ import { DateInput } from "@/components/admin/date-input";
3
+ import { ReferenceInput } from "@/components/admin/reference-input";
4
+ import { SelectInput } from "@/components/admin/select-input";
5
+ import { TextInput } from "@/components/admin/text-input";
6
+ import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
7
+ import { Create } from "@/components/admin/create";
8
+ import {
9
+ Form,
10
+ required,
11
+ useGetIdentity,
12
+ useNotify,
13
+ useRedirect,
14
+ } from "ra-core";
15
+
16
+ import { FormToolbar } from "../layout/FormToolbar";
17
+ import { contactOptionText } from "../misc/ContactOption";
18
+ import { useConfigurationContext } from "../root/ConfigurationContext";
19
+
20
+ export const TaskCreate = () => {
21
+ const { identity } = useGetIdentity();
22
+ const { taskTypes, taskPriorities, taskStatuses } = useConfigurationContext();
23
+ const notify = useNotify();
24
+ const redirect = useRedirect();
25
+
26
+ const handleSuccess = () => {
27
+ notify("Task created");
28
+ redirect("list", "tasks");
29
+ };
30
+
31
+ return (
32
+ <div className="mt-4 max-w-2xl mx-auto">
33
+ <Card>
34
+ <CardHeader>
35
+ <CardTitle>Create Task</CardTitle>
36
+ </CardHeader>
37
+ <CardContent>
38
+ <Create
39
+ resource="tasks"
40
+ redirect="list"
41
+ mutationOptions={{ onSuccess: handleSuccess }}
42
+ transform={(data) => ({
43
+ ...data,
44
+ sales_id: identity?.id,
45
+ created_at: new Date().toISOString(),
46
+ updated_at: new Date().toISOString(),
47
+ })}
48
+ >
49
+ <Form
50
+ defaultValues={{
51
+ due_date: new Date().toISOString().slice(0, 10),
52
+ priority: "medium",
53
+ status: "todo",
54
+ assigned_to: identity?.id,
55
+ }}
56
+ >
57
+ <TextInput
58
+ autoFocus
59
+ source="text"
60
+ label="Description"
61
+ validate={required()}
62
+ multiline
63
+ className="w-full"
64
+ />
65
+ <div className="grid grid-cols-2 gap-4 mt-4">
66
+ <ReferenceInput
67
+ source="contact_id"
68
+ reference="contacts_summary"
69
+ >
70
+ <AutocompleteInput
71
+ label="Contact"
72
+ optionText={contactOptionText}
73
+ validate={required()}
74
+ />
75
+ </ReferenceInput>
76
+ <DateInput
77
+ source="due_date"
78
+ validate={required()}
79
+ />
80
+ <SelectInput
81
+ source="type"
82
+ validate={required()}
83
+ choices={taskTypes.map((type) => ({
84
+ id: type,
85
+ name: type,
86
+ }))}
87
+ />
88
+ <SelectInput
89
+ source="priority"
90
+ choices={taskPriorities}
91
+ />
92
+ <SelectInput
93
+ source="status"
94
+ choices={taskStatuses}
95
+ />
96
+ <ReferenceInput source="assigned_to" reference="sales">
97
+ <SelectInput
98
+ optionText={(record) =>
99
+ `${record.first_name} ${record.last_name}`
100
+ }
101
+ label="Assigned To"
102
+ />
103
+ </ReferenceInput>
104
+ </div>
105
+ <FormToolbar />
106
+ </Form>
107
+ </Create>
108
+ </CardContent>
109
+ </Card>
110
+ </div>
111
+ );
112
+ };
@@ -3,6 +3,7 @@ import { DeleteButton } from "@/components/admin/delete-button";
3
3
  import { TextInput } from "@/components/admin/text-input";
4
4
  import { DateInput } from "@/components/admin/date-input";
5
5
  import { SelectInput } from "@/components/admin/select-input";
6
+ import { ReferenceInput } from "@/components/admin/reference-input";
6
7
  import { SaveButton } from "@/components/admin/form";
7
8
  import {
8
9
  Dialog,
@@ -23,7 +24,7 @@ export const TaskEdit = ({
23
24
  open: boolean;
24
25
  close: () => void;
25
26
  }) => {
26
- const { taskTypes } = useConfigurationContext();
27
+ const { taskTypes, taskPriorities, taskStatuses } = useConfigurationContext();
27
28
  const notify = useNotify();
28
29
  return (
29
30
  <Dialog open={open} onOpenChange={close}>
@@ -71,6 +72,24 @@ export const TaskEdit = ({
71
72
  helperText={false}
72
73
  validate={required()}
73
74
  />
75
+ <SelectInput
76
+ source="priority"
77
+ choices={taskPriorities}
78
+ helperText={false}
79
+ />
80
+ <SelectInput
81
+ source="status"
82
+ choices={taskStatuses}
83
+ helperText={false}
84
+ />
85
+ <ReferenceInput source="assigned_to" reference="sales">
86
+ <SelectInput
87
+ optionText={(record) =>
88
+ `${record.first_name} ${record.last_name}`
89
+ }
90
+ label="Assigned To"
91
+ />
92
+ </ReferenceInput>
74
93
  </div>
75
94
  <DialogFooter className="w-full sm:justify-between gap-4">
76
95
  <DeleteButton
@@ -0,0 +1,52 @@
1
+ import { AutocompleteInput } from "@/components/admin/autocomplete-input";
2
+ import { BooleanInput } from "@/components/admin/boolean-input";
3
+ import { CreateButton } from "@/components/admin/create-button";
4
+ import { ExportButton } from "@/components/admin/export-button";
5
+ import { FilterButton } from "@/components/admin/filter-form";
6
+ import { List } from "@/components/admin/list";
7
+ import { ReferenceInput } from "@/components/admin/reference-input";
8
+ import { SearchInput } from "@/components/admin/search-input";
9
+ import { SelectInput } from "@/components/admin/select-input";
10
+
11
+ import { useConfigurationContext } from "../root/ConfigurationContext";
12
+ import { TopToolbar } from "../layout/TopToolbar";
13
+ import { MyTasksInput } from "./MyTasksInput";
14
+ import { TaskListTable } from "./TaskListTable";
15
+
16
+ const TaskList = () => {
17
+ const { taskStatuses, taskPriorities } = useConfigurationContext();
18
+
19
+ const taskFilters = [
20
+ <SearchInput source="q" alwaysOn />,
21
+ <SelectInput source="status" choices={taskStatuses} alwaysOn />,
22
+ <ReferenceInput source="contact_id" reference="contacts">
23
+ <AutocompleteInput label={false} placeholder="Contact" />
24
+ </ReferenceInput>,
25
+ <SelectInput source="priority" choices={taskPriorities} />,
26
+ <MyTasksInput source="assigned_to" label="My Tasks" alwaysOn />,
27
+ <BooleanInput source="archived" label="Archived" />,
28
+ ];
29
+
30
+ return (
31
+ <List
32
+ perPage={25}
33
+ sort={{ field: "due_date", order: "ASC" }}
34
+ filters={taskFilters}
35
+ filterDefaultValues={{ archived: false }}
36
+ actions={<TaskActions />}
37
+ title="Tasks"
38
+ >
39
+ <TaskListTable />
40
+ </List>
41
+ );
42
+ };
43
+
44
+ const TaskActions = () => (
45
+ <TopToolbar>
46
+ <FilterButton />
47
+ <ExportButton />
48
+ <CreateButton label="New Task" />
49
+ </TopToolbar>
50
+ );
51
+
52
+ export default TaskList;
@@ -0,0 +1,60 @@
1
+ import { DataTable } from "@/components/admin/data-table";
2
+ import { DateField } from "@/components/admin/date-field";
3
+ import { ReferenceField } from "@/components/admin/reference-field";
4
+ import { TextField } from "@/components/admin/text-field";
5
+
6
+ import type { Task } from "../types";
7
+ import { TaskPriorityBadge } from "./TaskPriorityBadge";
8
+ import { TaskStatusBadge } from "./TaskStatusBadge";
9
+
10
+ export const TaskListTable = () => {
11
+ return (
12
+ <DataTable rowClick="show">
13
+ <DataTable.Col
14
+ source="text"
15
+ label="Task"
16
+ className="w-[35%]"
17
+ cellClassName="max-w-md"
18
+ render={(record: Task) => (
19
+ <div className="truncate" title={record.text}>
20
+ {record.type && record.type !== "None" && (
21
+ <span className="font-semibold">{record.type}: </span>
22
+ )}
23
+ {record.text}
24
+ </div>
25
+ )}
26
+ />
27
+ <DataTable.Col label="Contact" className="w-[15%]">
28
+ <ReferenceField source="contact_id" reference="contacts" link="show" />
29
+ </DataTable.Col>
30
+ <DataTable.Col label="Company" className="w-[15%]">
31
+ <ReferenceField
32
+ source="company_id"
33
+ reference="companies"
34
+ link="show"
35
+ sortable={false}
36
+ >
37
+ <TextField source="name" />
38
+ </ReferenceField>
39
+ </DataTable.Col>
40
+ <DataTable.Col label="Due Date" className="w-[12%]">
41
+ <DateField source="due_date" />
42
+ </DataTable.Col>
43
+ <DataTable.Col
44
+ label="Priority"
45
+ className="w-[10%]"
46
+ render={(record: Task) => (
47
+ <TaskPriorityBadge priority={record.priority} />
48
+ )}
49
+ />
50
+ <DataTable.Col
51
+ label="Status"
52
+ className="w-[10%]"
53
+ render={(record: Task) => <TaskStatusBadge status={record.status} />}
54
+ />
55
+ <DataTable.Col label="Assigned To" className="w-[13%]">
56
+ <ReferenceField source="assigned_to" reference="sales" link={false} />
57
+ </DataTable.Col>
58
+ </DataTable>
59
+ );
60
+ };
@@ -0,0 +1,20 @@
1
+ import { Badge } from "@/components/ui/badge";
2
+ import { cn } from "@/lib/utils";
3
+
4
+ const priorityColors = {
5
+ low: "bg-slate-500 hover:bg-slate-600",
6
+ medium: "bg-blue-500 hover:bg-blue-600",
7
+ high: "bg-orange-500 hover:bg-orange-600",
8
+ urgent: "bg-red-500 hover:bg-red-600",
9
+ };
10
+
11
+ export const TaskPriorityBadge = ({ priority }: { priority?: string }) => {
12
+ if (!priority) return null;
13
+ const colorClass = priorityColors[priority as keyof typeof priorityColors] || "bg-slate-500";
14
+
15
+ return (
16
+ <Badge className={cn("capitalize", colorClass)}>
17
+ {priority}
18
+ </Badge>
19
+ );
20
+ };
@@ -0,0 +1,71 @@
1
+ import { ShowBase, useShowContext } from "ra-core";
2
+ import { Card, CardContent } from "@/components/ui/card";
3
+ import { ReferenceManyField } from "@/components/admin/reference-many-field";
4
+
5
+ import { NoteCreate, NotesIterator } from "../notes";
6
+ import type { Task } from "../types";
7
+ import { TaskAside } from "./TaskAside";
8
+ import { TaskPriorityBadge } from "./TaskPriorityBadge";
9
+ import { TaskStatusBadge } from "./TaskStatusBadge";
10
+ import { TaskActivityTimeline } from "./TaskActivityTimeline";
11
+
12
+ export const TaskShow = () => (
13
+ <ShowBase>
14
+ <TaskShowContent />
15
+ </ShowBase>
16
+ );
17
+
18
+ const TaskShowContent = () => {
19
+ const { record, isPending } = useShowContext<Task>();
20
+ if (isPending || !record) return null;
21
+
22
+ return (
23
+ <div className="mt-2 mb-2 flex gap-8">
24
+ <div className="flex-1">
25
+ <Card>
26
+ <CardContent>
27
+ {/* Task Header */}
28
+ <div className="mb-6">
29
+ <h5 className="text-xl font-semibold mb-3">{record.type}</h5>
30
+ <div className="flex gap-3 mb-4">
31
+ <TaskStatusBadge status={record.status} />
32
+ <TaskPriorityBadge priority={record.priority} />
33
+ </div>
34
+ {record.text && (
35
+ <p className="text-sm text-muted-foreground whitespace-pre-line">
36
+ {record.text}
37
+ </p>
38
+ )}
39
+ </div>
40
+
41
+ {/* Activity Timeline */}
42
+ <div className="mt-8">
43
+ <h3 className="text-lg font-semibold mb-4">Activity Timeline</h3>
44
+ <p className="text-sm text-muted-foreground mb-3">
45
+ Track all status changes and updates for this task
46
+ </p>
47
+ <TaskActivityTimeline taskId={record.id} />
48
+ </div>
49
+
50
+ {/* Notes */}
51
+ <div className="mt-8">
52
+ <h3 className="text-lg font-semibold mb-4">Notes</h3>
53
+ <p className="text-sm text-muted-foreground mb-3">
54
+ Add notes and updates to this task
55
+ </p>
56
+ <ReferenceManyField
57
+ reference="taskNotes"
58
+ target="task_id"
59
+ sort={{ field: "date", order: "DESC" }}
60
+ empty={<NoteCreate reference="tasks" showStatus className="mt-4" />}
61
+ >
62
+ <NotesIterator reference="tasks" showStatus />
63
+ </ReferenceManyField>
64
+ </div>
65
+ </CardContent>
66
+ </Card>
67
+ </div>
68
+ <TaskAside />
69
+ </div>
70
+ );
71
+ };
@@ -0,0 +1,21 @@
1
+ import { Badge } from "@/components/ui/badge";
2
+ import { cn } from "@/lib/utils";
3
+
4
+ const statusColors = {
5
+ todo: "bg-slate-500 hover:bg-slate-600",
6
+ in_progress: "bg-blue-500 hover:bg-blue-600",
7
+ blocked: "bg-red-500 hover:bg-red-600",
8
+ done: "bg-green-500 hover:bg-green-600",
9
+ cancelled: "bg-gray-400 hover:bg-gray-500",
10
+ };
11
+
12
+ export const TaskStatusBadge = ({ status }: { status?: string }) => {
13
+ if (!status) return null;
14
+ const colorClass = statusColors[status as keyof typeof statusColors] || "bg-slate-500";
15
+
16
+ return (
17
+ <Badge className={cn("capitalize", colorClass)}>
18
+ {status.replace("_", " ")}
19
+ </Badge>
20
+ );
21
+ };
@@ -0,0 +1,9 @@
1
+ import { TaskCreate } from "./TaskCreate";
2
+ import TaskList from "./TaskList";
3
+ import { TaskShow } from "./TaskShow";
4
+
5
+ export default {
6
+ list: TaskList,
7
+ show: TaskShow,
8
+ create: TaskCreate,
9
+ };
@@ -216,6 +216,15 @@ export type Tag = {
216
216
  color: string;
217
217
  } & Pick<RaRecord, "id">;
218
218
 
219
+ export type TaskPriority = "low" | "medium" | "high" | "urgent";
220
+
221
+ export type TaskStatus =
222
+ | "todo"
223
+ | "in_progress"
224
+ | "blocked"
225
+ | "done"
226
+ | "cancelled";
227
+
219
228
  export type Task = {
220
229
  contact_id: Identifier;
221
230
  type: string;
@@ -223,6 +232,47 @@ export type Task = {
223
232
  due_date: string;
224
233
  done_date?: string | null;
225
234
  sales_id?: Identifier;
235
+ priority?: TaskPriority;
236
+ assigned_to?: Identifier;
237
+ status?: TaskStatus;
238
+ created_at?: string;
239
+ updated_at?: string;
240
+ archived?: boolean;
241
+ archived_at?: string;
242
+ } & Pick<RaRecord, "id">;
243
+
244
+ export type TaskSummary = Task & {
245
+ contact_first_name?: string;
246
+ contact_last_name?: string;
247
+ contact_email?: string;
248
+ company_id?: Identifier;
249
+ company_name?: string;
250
+ assigned_first_name?: string;
251
+ assigned_last_name?: string;
252
+ creator_first_name?: string;
253
+ creator_last_name?: string;
254
+ nb_notes?: number;
255
+ last_note_date?: string;
256
+ };
257
+
258
+ export type TaskNote = {
259
+ task_id: Identifier;
260
+ text: string;
261
+ date: string;
262
+ sales_id: Identifier;
263
+ status?: string;
264
+ created_at?: string;
265
+ updated_at?: string;
266
+ } & Pick<RaRecord, "id">;
267
+
268
+ export type TaskActivity = {
269
+ task_id: Identifier;
270
+ sales_id: Identifier;
271
+ action: string;
272
+ field_name?: string;
273
+ old_value?: string;
274
+ new_value?: string;
275
+ created_at: string;
226
276
  } & Pick<RaRecord, "id">;
227
277
 
228
278
  export type ActivityCompanyCreated = {
@@ -0,0 +1,10 @@
1
+ import * as React from "react"
2
+ import * as VisuallyHiddenPrimitive from "@radix-ui/react-visually-hidden"
3
+
4
+ function VisuallyHidden({
5
+ ...props
6
+ }: React.ComponentProps<typeof VisuallyHiddenPrimitive.Root>) {
7
+ return <VisuallyHiddenPrimitive.Root data-slot="visually-hidden" {...props} />
8
+ }
9
+
10
+ export { VisuallyHidden }
@@ -110,7 +110,7 @@ CREATE TRIGGER set_internal_heartbeat_timestamp
110
110
  -- Phase 5: Internal Heartbeat Computation Function
111
111
  -- ============================================================================
112
112
 
113
- CREATE OR REPLACE FUNCTION compute_company_internal_heartbeat(company_id bigint)
113
+ CREATE OR REPLACE FUNCTION compute_company_internal_heartbeat(p_company_id bigint)
114
114
  RETURNS void AS $$
115
115
  DECLARE
116
116
  score integer := 0;
@@ -131,7 +131,7 @@ BEGIN
131
131
  LEFT JOIN deals d ON c.id = d.company_id
132
132
  LEFT JOIN contacts co ON c.id = co.company_id
133
133
  LEFT JOIN tasks t ON co.id = t.contact_id
134
- WHERE c.id = company_id;
134
+ WHERE c.id = p_company_id;
135
135
 
136
136
  -- Scoring algorithm (simple recency-based, 0-100 scale)
137
137
  score := 100;
@@ -166,7 +166,7 @@ BEGIN
166
166
  internal_heartbeat_score = score,
167
167
  internal_heartbeat_status = status,
168
168
  internal_heartbeat_updated_at = now()
169
- WHERE id = company_id;
169
+ WHERE id = p_company_id;
170
170
  END;
171
171
  $$ LANGUAGE plpgsql;
172
172
 
@@ -1,4 +1,6 @@
1
1
  -- Fix ambiguous column reference in compute_company_internal_heartbeat by renaming parameter
2
+ -- This fix is moved earlier in the migration sequence to ensure it's available
3
+ -- before the tasks enhancement migration runs its updates.
2
4
 
3
5
  DROP FUNCTION IF EXISTS compute_company_internal_heartbeat(bigint);
4
6