Translate notifications
This commit is contained in:
parent
e1f673be47
commit
d4afb0e9bb
7 changed files with 386 additions and 21 deletions
|
|
@ -523,7 +523,10 @@ describe("Tablo Endpoint", () => {
|
|||
expect(latestNotification?.user_id).toBe(temporaryUser.userId);
|
||||
expect(latestNotification?.actor_id).toBe(ownerUser.userId);
|
||||
expect(latestNotification?.action_type).toBe("created");
|
||||
expect(latestNotification?.message).toContain("invited");
|
||||
// Message is now a JSONB object with en/fr keys
|
||||
expect(latestNotification?.message).toBeDefined();
|
||||
expect((latestNotification?.message as any)?.en).toContain("invited");
|
||||
expect((latestNotification?.message as any)?.fr).toContain("invité");
|
||||
expect(latestNotification?.read_at).toBeNull();
|
||||
});
|
||||
|
||||
|
|
@ -566,7 +569,9 @@ describe("Tablo Endpoint", () => {
|
|||
|
||||
// Should create notification for the newly created temporary user
|
||||
expect(notificationsForInvite?.length || 0).toBeGreaterThan(0);
|
||||
expect(notificationsForInvite?.[0].message).toContain("invited");
|
||||
// Message is now a JSONB object with en/fr keys
|
||||
expect((notificationsForInvite?.[0].message as any)?.en).toContain("invited");
|
||||
expect((notificationsForInvite?.[0].message as any)?.fr).toContain("invité");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -119,6 +119,7 @@ interface NotificationItemProps {
|
|||
}
|
||||
|
||||
function NotificationItem({ notification, onMarkAsRead, onClose }: NotificationItemProps) {
|
||||
const { i18n } = useTranslation();
|
||||
const link = getNotificationLink(notification);
|
||||
|
||||
const handleClick = () => {
|
||||
|
|
@ -126,6 +127,16 @@ function NotificationItem({ notification, onMarkAsRead, onClose }: NotificationI
|
|||
onClose();
|
||||
};
|
||||
|
||||
// Extract the message based on current locale
|
||||
const getMessage = () => {
|
||||
const locale = i18n.language.startsWith("fr") ? "fr" : "en";
|
||||
return (
|
||||
(notification.message as Record<string, string>)[locale] ||
|
||||
(notification.message as Record<string, string>)["en"] ||
|
||||
""
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Link to={link} onClick={handleClick}>
|
||||
<DropdownMenuItem className="cursor-pointer p-3 focus:bg-gray-700 hover:bg-gray-700 text-gray-200">
|
||||
|
|
@ -137,7 +148,7 @@ function NotificationItem({ notification, onMarkAsRead, onClose }: NotificationI
|
|||
</div>
|
||||
<div className="flex-1 min-w-0">
|
||||
<TypographySmall className="font-medium text-gray-100 line-clamp-2">
|
||||
{notification.message}
|
||||
{getMessage()}
|
||||
</TypographySmall>
|
||||
<TypographyMuted className="text-xs mt-1 text-gray-400">
|
||||
{formatRelativeTime(notification.created_at)}
|
||||
|
|
|
|||
|
|
@ -353,7 +353,7 @@ export type Database = {
|
|||
entity_id: string
|
||||
entity_type: string
|
||||
id: string
|
||||
message: string
|
||||
message: Json
|
||||
metadata: Json | null
|
||||
read_at: string | null
|
||||
user_id: string
|
||||
|
|
@ -365,7 +365,7 @@ export type Database = {
|
|||
entity_id: string
|
||||
entity_type: string
|
||||
id?: string
|
||||
message: string
|
||||
message?: Json
|
||||
metadata?: Json | null
|
||||
read_at?: string | null
|
||||
user_id: string
|
||||
|
|
@ -377,7 +377,7 @@ export type Database = {
|
|||
entity_id?: string
|
||||
entity_type?: string
|
||||
id?: string
|
||||
message?: string
|
||||
message?: Json
|
||||
metadata?: Json | null
|
||||
read_at?: string | null
|
||||
user_id?: string
|
||||
|
|
|
|||
|
|
@ -0,0 +1,344 @@
|
|||
-- ============================================================================
|
||||
-- Step 1: Alter the message column to JSONB
|
||||
-- ============================================================================
|
||||
|
||||
-- First, migrate existing data (if any) to JSONB format with en/fr keys
|
||||
ALTER TABLE public.notifications
|
||||
ADD COLUMN message_jsonb JSONB;
|
||||
|
||||
-- Migrate existing English messages to JSONB format
|
||||
-- This assumes existing messages are in English
|
||||
UPDATE public.notifications
|
||||
SET message_jsonb = jsonb_build_object(
|
||||
'en', message,
|
||||
'fr', message -- Placeholder, will be replaced by trigger going forward
|
||||
);
|
||||
|
||||
-- Drop old column and rename new one
|
||||
ALTER TABLE public.notifications
|
||||
DROP COLUMN message;
|
||||
|
||||
ALTER TABLE public.notifications
|
||||
RENAME COLUMN message_jsonb TO message;
|
||||
|
||||
-- Make it NOT NULL
|
||||
ALTER TABLE public.notifications
|
||||
ALTER COLUMN message SET NOT NULL;
|
||||
|
||||
-- Set default to empty object
|
||||
ALTER TABLE public.notifications
|
||||
ALTER COLUMN message SET DEFAULT '{}'::jsonb;
|
||||
|
||||
-- ============================================================================
|
||||
-- Step 2: Update the notify_users function with translations and no RAISE LOG
|
||||
-- ============================================================================
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.notify_users()
|
||||
RETURNS TRIGGER AS $$
|
||||
DECLARE
|
||||
affected_users UUID[];
|
||||
affected_user UUID;
|
||||
actor UUID;
|
||||
action TEXT;
|
||||
msg JSONB; -- Changed from TEXT to JSONB
|
||||
meta JSONB;
|
||||
entity_type_name TEXT;
|
||||
entity_identifier TEXT;
|
||||
BEGIN
|
||||
-- Determine if this is an INSERT or UPDATE
|
||||
IF TG_OP = 'INSERT' THEN
|
||||
action := 'created';
|
||||
ELSE
|
||||
action := 'updated';
|
||||
-- Skip if soft delete happened (only for tables with deleted_at column)
|
||||
IF TG_TABLE_NAME IN ('tablos', 'events', 'notes', 'event_types') THEN
|
||||
IF NEW.deleted_at IS NOT NULL AND OLD.deleted_at IS NULL THEN
|
||||
RETURN NEW;
|
||||
END IF;
|
||||
END IF;
|
||||
END IF;
|
||||
|
||||
-- Get the actor (current user)
|
||||
-- Note: auth.uid() may return NULL when called via service role (API calls)
|
||||
-- In such cases, we won't exclude anyone from notifications based on actor
|
||||
actor := auth.uid();
|
||||
|
||||
-- Set entity type and ID based on the table
|
||||
entity_type_name := TG_TABLE_NAME;
|
||||
|
||||
-- Determine entity ID and affected users based on table
|
||||
CASE TG_TABLE_NAME
|
||||
WHEN 'tablos' THEN
|
||||
entity_identifier := NEW.id;
|
||||
|
||||
IF TG_OP = 'INSERT' THEN
|
||||
msg := jsonb_build_object(
|
||||
'en', 'New tablo "' || NEW.name || '" was created',
|
||||
'fr', 'Nouveau tablo "' || NEW.name || '" a été créé'
|
||||
);
|
||||
-- Notify owner (but not if they are the creator)
|
||||
IF actor IS NULL OR NEW.owner_id != actor THEN
|
||||
affected_users := ARRAY[NEW.owner_id];
|
||||
END IF;
|
||||
ELSE
|
||||
msg := jsonb_build_object(
|
||||
'en', 'Tablo "' || NEW.name || '" was updated',
|
||||
'fr', 'Le tablo "' || NEW.name || '" a été mis à jour'
|
||||
);
|
||||
-- Notify owner and all collaborators (exclude actor if set)
|
||||
affected_users := ARRAY(
|
||||
SELECT DISTINCT user_id
|
||||
FROM public.tablo_access
|
||||
WHERE tablo_id = NEW.id
|
||||
AND is_active = true
|
||||
AND (actor IS NULL OR user_id != actor)
|
||||
UNION
|
||||
SELECT NEW.owner_id
|
||||
WHERE actor IS NULL OR NEW.owner_id != actor
|
||||
);
|
||||
END IF;
|
||||
|
||||
meta := jsonb_build_object(
|
||||
'tablo_name', NEW.name,
|
||||
'status', NEW.status,
|
||||
'color', NEW.color
|
||||
);
|
||||
|
||||
WHEN 'tasks' THEN
|
||||
entity_identifier := NEW.id;
|
||||
|
||||
IF TG_OP = 'INSERT' THEN
|
||||
msg := jsonb_build_object(
|
||||
'en', 'New task "' || NEW.title || '" was created',
|
||||
'fr', 'Nouvelle tâche "' || NEW.title || '" a été créée'
|
||||
);
|
||||
ELSE
|
||||
IF OLD.status != NEW.status THEN
|
||||
msg := jsonb_build_object(
|
||||
'en', 'Task "' || NEW.title || '" status changed to ' || NEW.status,
|
||||
'fr', 'Le statut de la tâche "' || NEW.title || '" a changé à ' || NEW.status
|
||||
);
|
||||
ELSIF OLD.assignee_id != NEW.assignee_id OR (OLD.assignee_id IS NULL AND NEW.assignee_id IS NOT NULL) THEN
|
||||
msg := jsonb_build_object(
|
||||
'en', 'You were assigned to task "' || NEW.title || '"',
|
||||
'fr', 'Vous avez été assigné à la tâche "' || NEW.title || '"'
|
||||
);
|
||||
ELSE
|
||||
msg := jsonb_build_object(
|
||||
'en', 'Task "' || NEW.title || '" was updated',
|
||||
'fr', 'La tâche "' || NEW.title || '" a été mise à jour'
|
||||
);
|
||||
END IF;
|
||||
END IF;
|
||||
|
||||
-- Notify tablo collaborators and assignee (exclude actor if set)
|
||||
affected_users := ARRAY(
|
||||
SELECT DISTINCT user_id
|
||||
FROM public.tablo_access
|
||||
WHERE tablo_id = NEW.tablo_id
|
||||
AND is_active = true
|
||||
AND (actor IS NULL OR user_id != actor)
|
||||
UNION
|
||||
SELECT t.owner_id
|
||||
FROM public.tablos t
|
||||
WHERE t.id = NEW.tablo_id
|
||||
AND (actor IS NULL OR t.owner_id != actor)
|
||||
UNION
|
||||
SELECT NEW.assignee_id
|
||||
WHERE NEW.assignee_id IS NOT NULL
|
||||
AND (actor IS NULL OR NEW.assignee_id != actor)
|
||||
);
|
||||
|
||||
meta := jsonb_build_object(
|
||||
'task_title', NEW.title,
|
||||
'status', NEW.status,
|
||||
'tablo_id', NEW.tablo_id,
|
||||
'assignee_id', NEW.assignee_id
|
||||
);
|
||||
|
||||
WHEN 'events' THEN
|
||||
entity_identifier := NEW.id;
|
||||
|
||||
IF TG_OP = 'INSERT' THEN
|
||||
msg := jsonb_build_object(
|
||||
'en', 'New event "' || NEW.title || '" was created',
|
||||
'fr', 'Nouvel événement "' || NEW.title || '" a été créé'
|
||||
);
|
||||
ELSE
|
||||
msg := jsonb_build_object(
|
||||
'en', 'Event "' || NEW.title || '" was updated',
|
||||
'fr', 'L''événement "' || NEW.title || '" a été mis à jour'
|
||||
);
|
||||
END IF;
|
||||
|
||||
-- Notify tablo collaborators (exclude actor if set)
|
||||
affected_users := ARRAY(
|
||||
SELECT DISTINCT user_id
|
||||
FROM public.tablo_access
|
||||
WHERE tablo_id = NEW.tablo_id
|
||||
AND is_active = true
|
||||
AND (actor IS NULL OR user_id != actor)
|
||||
UNION
|
||||
SELECT t.owner_id
|
||||
FROM public.tablos t
|
||||
WHERE t.id = NEW.tablo_id
|
||||
AND (actor IS NULL OR t.owner_id != actor)
|
||||
UNION
|
||||
SELECT NEW.created_by
|
||||
WHERE actor IS NULL OR NEW.created_by != actor
|
||||
);
|
||||
|
||||
meta := jsonb_build_object(
|
||||
'event_title', NEW.title,
|
||||
'start_date', NEW.start_date,
|
||||
'start_time', NEW.start_time,
|
||||
'tablo_id', NEW.tablo_id
|
||||
);
|
||||
|
||||
WHEN 'notes' THEN
|
||||
entity_identifier := NEW.id;
|
||||
|
||||
IF TG_OP = 'INSERT' THEN
|
||||
msg := jsonb_build_object(
|
||||
'en', 'New note "' || NEW.title || '" was created',
|
||||
'fr', 'Nouvelle note "' || NEW.title || '" a été créée'
|
||||
);
|
||||
ELSE
|
||||
msg := jsonb_build_object(
|
||||
'en', 'Note "' || NEW.title || '" was updated',
|
||||
'fr', 'La note "' || NEW.title || '" a été mise à jour'
|
||||
);
|
||||
END IF;
|
||||
|
||||
-- Notify note owner and users with access (exclude actor if set)
|
||||
affected_users := ARRAY(
|
||||
SELECT DISTINCT user_id
|
||||
FROM public.note_access
|
||||
WHERE note_id = NEW.id
|
||||
AND is_active = true
|
||||
AND (actor IS NULL OR user_id != actor)
|
||||
UNION
|
||||
SELECT NEW.user_id
|
||||
WHERE actor IS NULL OR NEW.user_id != actor
|
||||
);
|
||||
|
||||
meta := jsonb_build_object(
|
||||
'note_title', NEW.title,
|
||||
'note_owner', NEW.user_id
|
||||
);
|
||||
|
||||
WHEN 'tablo_access' THEN
|
||||
entity_identifier := NEW.id::TEXT;
|
||||
|
||||
IF TG_OP = 'INSERT' THEN
|
||||
msg := jsonb_build_object(
|
||||
'en', 'You were granted access to a tablo',
|
||||
'fr', 'Vous avez obtenu l''accès à un tablo'
|
||||
);
|
||||
ELSE
|
||||
IF OLD.is_admin != NEW.is_admin AND NEW.is_admin = true THEN
|
||||
msg := jsonb_build_object(
|
||||
'en', 'You were promoted to admin on a tablo',
|
||||
'fr', 'Vous avez été promu administrateur d''un tablo'
|
||||
);
|
||||
ELSIF OLD.is_active != NEW.is_active AND NEW.is_active = false THEN
|
||||
msg := jsonb_build_object(
|
||||
'en', 'Your access to a tablo was revoked',
|
||||
'fr', 'Votre accès à un tablo a été révoqué'
|
||||
);
|
||||
ELSE
|
||||
msg := jsonb_build_object(
|
||||
'en', 'Your tablo access was updated',
|
||||
'fr', 'Votre accès au tablo a été mis à jour'
|
||||
);
|
||||
END IF;
|
||||
END IF;
|
||||
|
||||
-- Notify the user being granted/modified access (exclude actor if set)
|
||||
IF actor IS NULL OR NEW.user_id != actor THEN
|
||||
affected_users := ARRAY[NEW.user_id];
|
||||
END IF;
|
||||
|
||||
meta := jsonb_build_object(
|
||||
'tablo_id', NEW.tablo_id,
|
||||
'is_admin', NEW.is_admin,
|
||||
'is_active', NEW.is_active,
|
||||
'granted_by', NEW.granted_by
|
||||
);
|
||||
|
||||
WHEN 'tablo_invites' THEN
|
||||
entity_identifier := NEW.id::TEXT;
|
||||
|
||||
-- For tablo_invites, use invited_by as actor instead of auth.uid()
|
||||
-- because this is called via service role which has no auth context
|
||||
actor := NEW.invited_by;
|
||||
|
||||
IF TG_OP = 'INSERT' THEN
|
||||
msg := jsonb_build_object(
|
||||
'en', 'You were invited to collaborate on a tablo',
|
||||
'fr', 'Vous avez été invité à collaborer sur un tablo'
|
||||
);
|
||||
ELSE
|
||||
IF OLD.is_pending != NEW.is_pending AND NEW.is_pending = false THEN
|
||||
msg := jsonb_build_object(
|
||||
'en', 'Your tablo invitation status changed',
|
||||
'fr', 'Le statut de votre invitation au tablo a changé'
|
||||
);
|
||||
ELSE
|
||||
msg := jsonb_build_object(
|
||||
'en', 'Your tablo invitation was updated',
|
||||
'fr', 'Votre invitation au tablo a été mise à jour'
|
||||
);
|
||||
END IF;
|
||||
END IF;
|
||||
|
||||
-- Find the user by email and notify them (case-insensitive)
|
||||
-- Only notify if user exists in the system
|
||||
-- Don't notify the person who sent the invite (exclude actor if set)
|
||||
affected_users := ARRAY(
|
||||
SELECT id
|
||||
FROM auth.users
|
||||
WHERE LOWER(email) = LOWER(NEW.invited_email)
|
||||
AND (actor IS NULL OR id != actor)
|
||||
);
|
||||
|
||||
meta := jsonb_build_object(
|
||||
'tablo_id', NEW.tablo_id,
|
||||
'invited_email', NEW.invited_email,
|
||||
'invited_by', NEW.invited_by,
|
||||
'is_pending', NEW.is_pending
|
||||
);
|
||||
|
||||
ELSE
|
||||
-- Unknown table, skip
|
||||
RETURN NEW;
|
||||
END CASE;
|
||||
|
||||
-- Insert notifications for all affected users
|
||||
IF affected_users IS NOT NULL AND array_length(affected_users, 1) > 0 THEN
|
||||
FOREACH affected_user IN ARRAY affected_users
|
||||
LOOP
|
||||
INSERT INTO public.notifications (
|
||||
user_id,
|
||||
actor_id,
|
||||
entity_type,
|
||||
entity_id,
|
||||
action_type,
|
||||
message,
|
||||
metadata
|
||||
) VALUES (
|
||||
affected_user,
|
||||
actor,
|
||||
entity_type_name,
|
||||
entity_identifier,
|
||||
action,
|
||||
msg,
|
||||
meta
|
||||
);
|
||||
END LOOP;
|
||||
END IF;
|
||||
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql SECURITY DEFINER;
|
||||
|
||||
|
|
@ -28,7 +28,7 @@ SELECT col_type_is('public', 'notifications', 'actor_id', 'uuid', 'notifications
|
|||
SELECT col_type_is('public', 'notifications', 'entity_type', 'text', 'notifications.entity_type should be text');
|
||||
SELECT col_type_is('public', 'notifications', 'entity_id', 'text', 'notifications.entity_id should be text');
|
||||
SELECT col_type_is('public', 'notifications', 'action_type', 'text', 'notifications.action_type should be text');
|
||||
SELECT col_type_is('public', 'notifications', 'message', 'text', 'notifications.message should be text');
|
||||
SELECT col_type_is('public', 'notifications', 'message', 'jsonb', 'notifications.message should be jsonb');
|
||||
SELECT col_type_is('public', 'notifications', 'metadata', 'jsonb', 'notifications.metadata should be jsonb');
|
||||
SELECT col_type_is('public', 'notifications', 'read_at', 'timestamp with time zone', 'notifications.read_at should be timestamptz');
|
||||
SELECT col_type_is('public', 'notifications', 'created_at', 'timestamp with time zone', 'notifications.created_at should be timestamptz');
|
||||
|
|
@ -478,7 +478,7 @@ SELECT is(
|
|||
(SELECT COUNT(*)::integer FROM public.notifications
|
||||
WHERE entity_type = 'tablos'
|
||||
AND entity_id = current_setting('test.tablo_id')
|
||||
AND message = 'Tablo "Tablo Updated for Multiple Users Test" was updated'
|
||||
AND message->>'en' = 'Tablo "Tablo Updated for Multiple Users Test" was updated'
|
||||
AND user_id = current_setting('test.user2_id')::uuid),
|
||||
1,
|
||||
'User2 should receive exactly 1 notification for tablo update'
|
||||
|
|
@ -488,7 +488,7 @@ SELECT is(
|
|||
(SELECT COUNT(*)::integer FROM public.notifications
|
||||
WHERE entity_type = 'tablos'
|
||||
AND entity_id = current_setting('test.tablo_id')
|
||||
AND message = 'Tablo "Tablo Updated for Multiple Users Test" was updated'
|
||||
AND message->>'en' = 'Tablo "Tablo Updated for Multiple Users Test" was updated'
|
||||
AND user_id = current_setting('test.user4_id')::uuid),
|
||||
1,
|
||||
'User4 should receive exactly 1 notification for tablo update'
|
||||
|
|
@ -498,7 +498,7 @@ SELECT is(
|
|||
(SELECT COUNT(*)::integer FROM public.notifications
|
||||
WHERE entity_type = 'tablos'
|
||||
AND entity_id = current_setting('test.tablo_id')
|
||||
AND message = 'Tablo "Tablo Updated for Multiple Users Test" was updated'
|
||||
AND message->>'en' = 'Tablo "Tablo Updated for Multiple Users Test" was updated'
|
||||
AND user_id = current_setting('test.user5_id')::uuid),
|
||||
1,
|
||||
'User5 should receive exactly 1 notification for tablo update'
|
||||
|
|
@ -508,7 +508,7 @@ SELECT is(
|
|||
(SELECT COUNT(*)::integer FROM public.notifications
|
||||
WHERE entity_type = 'tablos'
|
||||
AND entity_id = current_setting('test.tablo_id')
|
||||
AND message = 'Tablo "Tablo Updated for Multiple Users Test" was updated'
|
||||
AND message->>'en' = 'Tablo "Tablo Updated for Multiple Users Test" was updated'
|
||||
AND user_id = current_setting('test.user3_id')::uuid),
|
||||
1,
|
||||
'User3 should receive exactly 1 notification for tablo update'
|
||||
|
|
@ -519,7 +519,7 @@ SELECT is(
|
|||
(SELECT COUNT(*)::integer FROM public.notifications
|
||||
WHERE entity_type = 'tablos'
|
||||
AND entity_id = current_setting('test.tablo_id')
|
||||
AND message = 'Tablo "Tablo Updated for Multiple Users Test" was updated'
|
||||
AND message->>'en' = 'Tablo "Tablo Updated for Multiple Users Test" was updated'
|
||||
AND actor_id = current_setting('test.user1_id')::uuid
|
||||
AND user_id = current_setting('test.user1_id')::uuid),
|
||||
0,
|
||||
|
|
@ -592,7 +592,7 @@ SELECT is(
|
|||
WHERE entity_type = 'tasks'
|
||||
AND entity_id = current_setting('test.multi_task_id')
|
||||
AND user_id = current_setting('test.user5_id')::uuid
|
||||
AND message LIKE '%assigned%'),
|
||||
AND message->>'en' LIKE '%assigned%'),
|
||||
1,
|
||||
'Assignee should receive exactly 1 notification about task assignment'
|
||||
);
|
||||
|
|
@ -608,10 +608,14 @@ SELECT ok(
|
|||
'Notification metadata should be valid JSONB objects'
|
||||
);
|
||||
|
||||
-- Test: Messages should be non-empty
|
||||
-- Test: Messages should be non-empty and have both en and fr translations
|
||||
SELECT ok(
|
||||
(SELECT COUNT(*) FROM public.notifications WHERE length(message) = 0) = 0,
|
||||
'All notification messages should be non-empty'
|
||||
(SELECT COUNT(*) FROM public.notifications
|
||||
WHERE length(message->>'en') = 0
|
||||
OR length(message->>'fr') = 0
|
||||
OR message->>'en' IS NULL
|
||||
OR message->>'fr' IS NULL) = 0,
|
||||
'All notification messages should have non-empty en and fr translations'
|
||||
);
|
||||
|
||||
-- Test: Action types should be valid
|
||||
|
|
|
|||
|
|
@ -135,8 +135,9 @@ SELECT ok(
|
|||
(SELECT COUNT(*) FROM public.notifications
|
||||
WHERE user_id = current_setting('test.rls_user2_id')::uuid
|
||||
AND message IS NOT NULL
|
||||
AND length(message) > 0) > 0,
|
||||
'Notifications should have non-empty messages'
|
||||
AND length(message->>'en') > 0
|
||||
AND length(message->>'fr') > 0) > 0,
|
||||
'Notifications should have non-empty messages in both en and fr'
|
||||
);
|
||||
|
||||
SELECT ok(
|
||||
|
|
|
|||
|
|
@ -353,7 +353,7 @@ export type Database = {
|
|||
entity_id: string
|
||||
entity_type: string
|
||||
id: string
|
||||
message: string
|
||||
message: Json
|
||||
metadata: Json | null
|
||||
read_at: string | null
|
||||
user_id: string
|
||||
|
|
@ -365,7 +365,7 @@ export type Database = {
|
|||
entity_id: string
|
||||
entity_type: string
|
||||
id?: string
|
||||
message: string
|
||||
message?: Json
|
||||
metadata?: Json | null
|
||||
read_at?: string | null
|
||||
user_id: string
|
||||
|
|
@ -377,7 +377,7 @@ export type Database = {
|
|||
entity_id?: string
|
||||
entity_type?: string
|
||||
id?: string
|
||||
message?: string
|
||||
message?: Json
|
||||
metadata?: Json | null
|
||||
read_at?: string | null
|
||||
user_id?: string
|
||||
|
|
|
|||
Loading…
Reference in a new issue