realtimex-crm 0.9.2 → 0.9.10
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/assets/{DealList-DbwJCRGl.js → DealList-Bfe4r2OW.js} +2 -2
- package/dist/assets/{DealList-DbwJCRGl.js.map → DealList-Bfe4r2OW.js.map} +1 -1
- package/dist/assets/{index-mE-upBfc.js → index-Dat6OHMH.js} +3 -3
- package/dist/assets/{index-mE-upBfc.js.map → index-Dat6OHMH.js.map} +1 -1
- package/dist/index.html +1 -1
- package/dist/stats.html +1 -1
- package/package.json +1 -1
- package/src/components/atomic-crm/contacts/TagsListEdit.tsx +4 -4
- package/supabase/migrations/20251224000000_fix_webhook_function.sql +27 -0
- package/supabase/migrations/20251224000001_fix_webhook_function_explicit.sql +164 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "realtimex-crm",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.10",
|
|
4
4
|
"description": "RealTimeX CRM - A full-featured CRM built with React, shadcn-admin-kit, and Supabase. Fork of Atomic CRM with RealTimeX App SDK integration.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -39,13 +39,13 @@ export const TagsListEdit = () => {
|
|
|
39
39
|
const [update] = useUpdate<Contact>();
|
|
40
40
|
|
|
41
41
|
const unselectedTags =
|
|
42
|
-
allTags && record && allTags.filter((tag) => !record.tags.includes(tag.id));
|
|
42
|
+
allTags && record && allTags.filter((tag) => !(record.tags ?? []).includes(tag.id));
|
|
43
43
|
|
|
44
44
|
const handleTagAdd = (id: Identifier) => {
|
|
45
45
|
if (!record) {
|
|
46
46
|
throw new Error("No contact record found");
|
|
47
47
|
}
|
|
48
|
-
const tags = [...record.tags, id];
|
|
48
|
+
const tags = [...(record.tags ?? []), id];
|
|
49
49
|
update("contacts", {
|
|
50
50
|
id: record.id,
|
|
51
51
|
data: { tags },
|
|
@@ -57,7 +57,7 @@ export const TagsListEdit = () => {
|
|
|
57
57
|
if (!record) {
|
|
58
58
|
throw new Error("No contact record found");
|
|
59
59
|
}
|
|
60
|
-
const tags = record.tags.filter((tagId) => tagId !== id);
|
|
60
|
+
const tags = (record.tags ?? []).filter((tagId) => tagId !== id);
|
|
61
61
|
await update("contacts", {
|
|
62
62
|
id: record.id,
|
|
63
63
|
data: { tags },
|
|
@@ -83,7 +83,7 @@ export const TagsListEdit = () => {
|
|
|
83
83
|
"contacts",
|
|
84
84
|
{
|
|
85
85
|
id: record.id,
|
|
86
|
-
data: { tags: [...record.tags, tag.id] },
|
|
86
|
+
data: { tags: [...(record.tags ?? []), tag.id] },
|
|
87
87
|
previousData: record,
|
|
88
88
|
},
|
|
89
89
|
{
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
-- Function to enqueue webhook events
|
|
2
|
+
create or replace function enqueue_webhook_event(
|
|
3
|
+
p_event_type text,
|
|
4
|
+
p_payload jsonb
|
|
5
|
+
) returns void
|
|
6
|
+
language plpgsql
|
|
7
|
+
security definer
|
|
8
|
+
set search_path = public
|
|
9
|
+
as $$
|
|
10
|
+
begin
|
|
11
|
+
-- Find all active webhooks that listen to this event
|
|
12
|
+
insert into public.webhook_queue (webhook_id, event_type, payload, next_retry_at)
|
|
13
|
+
select
|
|
14
|
+
id,
|
|
15
|
+
p_event_type,
|
|
16
|
+
p_payload,
|
|
17
|
+
now()
|
|
18
|
+
from public.webhooks
|
|
19
|
+
where is_active = true
|
|
20
|
+
and p_event_type = any(events);
|
|
21
|
+
end;
|
|
22
|
+
$$;
|
|
23
|
+
|
|
24
|
+
-- Grant execute permissions on webhook functions
|
|
25
|
+
grant execute on function enqueue_webhook_event(text, jsonb) to authenticated;
|
|
26
|
+
grant execute on function enqueue_webhook_event(text, jsonb) to service_role;
|
|
27
|
+
grant execute on function enqueue_webhook_event(text, jsonb) to anon;
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
-- Explicitly define function in public schema
|
|
2
|
+
create or replace function public.enqueue_webhook_event(
|
|
3
|
+
p_event_type text,
|
|
4
|
+
p_payload jsonb
|
|
5
|
+
) returns void
|
|
6
|
+
language plpgsql
|
|
7
|
+
security definer
|
|
8
|
+
set search_path = public
|
|
9
|
+
as $$
|
|
10
|
+
begin
|
|
11
|
+
-- Find all active webhooks that listen to this event
|
|
12
|
+
insert into public.webhook_queue (webhook_id, event_type, payload, next_retry_at)
|
|
13
|
+
select
|
|
14
|
+
id,
|
|
15
|
+
p_event_type,
|
|
16
|
+
p_payload,
|
|
17
|
+
now()
|
|
18
|
+
from public.webhooks
|
|
19
|
+
where is_active = true
|
|
20
|
+
and p_event_type = any(events);
|
|
21
|
+
end;
|
|
22
|
+
$$;
|
|
23
|
+
|
|
24
|
+
-- Grant execute permissions
|
|
25
|
+
grant execute on function public.enqueue_webhook_event(text, jsonb) to authenticated;
|
|
26
|
+
grant execute on function public.enqueue_webhook_event(text, jsonb) to service_role;
|
|
27
|
+
grant execute on function public.enqueue_webhook_event(text, jsonb) to anon;
|
|
28
|
+
|
|
29
|
+
-- Update triggers to use fully qualified name
|
|
30
|
+
|
|
31
|
+
-- Trigger function for contact CRUD events
|
|
32
|
+
create or replace function public.trigger_contact_webhooks()
|
|
33
|
+
returns trigger
|
|
34
|
+
language plpgsql
|
|
35
|
+
security definer
|
|
36
|
+
set search_path = public
|
|
37
|
+
as $$
|
|
38
|
+
declare
|
|
39
|
+
event_type text;
|
|
40
|
+
payload jsonb;
|
|
41
|
+
begin
|
|
42
|
+
if (TG_OP = 'INSERT') then
|
|
43
|
+
event_type := 'contact.created';
|
|
44
|
+
payload := to_jsonb(NEW);
|
|
45
|
+
elsif (TG_OP = 'UPDATE') then
|
|
46
|
+
event_type := 'contact.updated';
|
|
47
|
+
payload := jsonb_build_object('old', to_jsonb(OLD), 'new', to_jsonb(NEW));
|
|
48
|
+
elsif (TG_OP = 'DELETE') then
|
|
49
|
+
event_type := 'contact.deleted';
|
|
50
|
+
payload := to_jsonb(OLD);
|
|
51
|
+
end if;
|
|
52
|
+
|
|
53
|
+
perform public.enqueue_webhook_event(event_type, payload);
|
|
54
|
+
|
|
55
|
+
if (TG_OP = 'DELETE') then
|
|
56
|
+
return OLD;
|
|
57
|
+
else
|
|
58
|
+
return NEW;
|
|
59
|
+
end if;
|
|
60
|
+
end;
|
|
61
|
+
$$;
|
|
62
|
+
|
|
63
|
+
-- Trigger function for company CRUD events
|
|
64
|
+
create or replace function public.trigger_company_webhooks()
|
|
65
|
+
returns trigger
|
|
66
|
+
language plpgsql
|
|
67
|
+
security definer
|
|
68
|
+
set search_path = public
|
|
69
|
+
as $$
|
|
70
|
+
declare
|
|
71
|
+
event_type text;
|
|
72
|
+
payload jsonb;
|
|
73
|
+
begin
|
|
74
|
+
if (TG_OP = 'INSERT') then
|
|
75
|
+
event_type := 'company.created';
|
|
76
|
+
payload := to_jsonb(NEW);
|
|
77
|
+
elsif (TG_OP = 'UPDATE') then
|
|
78
|
+
event_type := 'company.updated';
|
|
79
|
+
payload := jsonb_build_object('old', to_jsonb(OLD), 'new', to_jsonb(NEW));
|
|
80
|
+
elsif (TG_OP = 'DELETE') then
|
|
81
|
+
event_type := 'company.deleted';
|
|
82
|
+
payload := to_jsonb(OLD);
|
|
83
|
+
end if;
|
|
84
|
+
|
|
85
|
+
perform public.enqueue_webhook_event(event_type, payload);
|
|
86
|
+
|
|
87
|
+
if (TG_OP = 'DELETE') then
|
|
88
|
+
return OLD;
|
|
89
|
+
else
|
|
90
|
+
return NEW;
|
|
91
|
+
end if;
|
|
92
|
+
end;
|
|
93
|
+
$$;
|
|
94
|
+
|
|
95
|
+
-- Trigger function for deal CRUD and stage change events
|
|
96
|
+
create or replace function public.trigger_deal_webhooks()
|
|
97
|
+
returns trigger
|
|
98
|
+
language plpgsql
|
|
99
|
+
security definer
|
|
100
|
+
set search_path = public
|
|
101
|
+
as $$
|
|
102
|
+
declare
|
|
103
|
+
event_type text;
|
|
104
|
+
payload jsonb;
|
|
105
|
+
begin
|
|
106
|
+
if (TG_OP = 'INSERT') then
|
|
107
|
+
event_type := 'deal.created';
|
|
108
|
+
payload := to_jsonb(NEW);
|
|
109
|
+
perform public.enqueue_webhook_event(event_type, payload);
|
|
110
|
+
elsif (TG_OP = 'UPDATE') then
|
|
111
|
+
-- Check for stage changes
|
|
112
|
+
if (OLD.stage <> NEW.stage) then
|
|
113
|
+
event_type := 'deal.stage_changed';
|
|
114
|
+
payload := jsonb_build_object(
|
|
115
|
+
'deal_id', NEW.id,
|
|
116
|
+
'old_stage', OLD.stage,
|
|
117
|
+
'new_stage', NEW.stage,
|
|
118
|
+
'deal', to_jsonb(NEW)
|
|
119
|
+
);
|
|
120
|
+
perform public.enqueue_webhook_event(event_type, payload);
|
|
121
|
+
|
|
122
|
+
-- Check for won/lost
|
|
123
|
+
if (NEW.stage = 'won') then
|
|
124
|
+
perform public.enqueue_webhook_event('deal.won', to_jsonb(NEW));
|
|
125
|
+
elsif (NEW.stage = 'lost') then
|
|
126
|
+
perform public.enqueue_webhook_event('deal.lost', to_jsonb(NEW));
|
|
127
|
+
end if;
|
|
128
|
+
end if;
|
|
129
|
+
|
|
130
|
+
event_type := 'deal.updated';
|
|
131
|
+
payload := jsonb_build_object('old', to_jsonb(OLD), 'new', to_jsonb(NEW));
|
|
132
|
+
perform public.enqueue_webhook_event(event_type, payload);
|
|
133
|
+
elsif (TG_OP = 'DELETE') then
|
|
134
|
+
event_type := 'deal.deleted';
|
|
135
|
+
payload := to_jsonb(OLD);
|
|
136
|
+
perform public.enqueue_webhook_event(event_type, payload);
|
|
137
|
+
end if;
|
|
138
|
+
|
|
139
|
+
if (TG_OP = 'DELETE') then
|
|
140
|
+
return OLD;
|
|
141
|
+
else
|
|
142
|
+
return NEW;
|
|
143
|
+
end if;
|
|
144
|
+
end;
|
|
145
|
+
$$;
|
|
146
|
+
|
|
147
|
+
-- Trigger function for task completion
|
|
148
|
+
create or replace function public.trigger_task_webhooks()
|
|
149
|
+
returns trigger
|
|
150
|
+
language plpgsql
|
|
151
|
+
security definer
|
|
152
|
+
set search_path = public
|
|
153
|
+
as $$
|
|
154
|
+
begin
|
|
155
|
+
if (TG_OP = 'UPDATE' and OLD.done_date is null and NEW.done_date is not null) then
|
|
156
|
+
perform public.enqueue_webhook_event('task.completed', to_jsonb(NEW));
|
|
157
|
+
end if;
|
|
158
|
+
|
|
159
|
+
return NEW;
|
|
160
|
+
end;
|
|
161
|
+
$$;
|
|
162
|
+
|
|
163
|
+
-- Reload schema cache
|
|
164
|
+
NOTIFY pgrst, 'reload schema';
|