committed by
GitHub
parent
9eded69f15
commit
5e8e01e340
@@ -98,7 +98,7 @@ export function AccountSelector({
|
||||
variant="ghost"
|
||||
role="combobox"
|
||||
aria-expanded={open}
|
||||
className={cn('dark:shadow-primary/10 group w-full border px-4', {
|
||||
className={cn('dark:shadow-primary/10 group px-2', {
|
||||
'justify-between': !collapsed,
|
||||
'justify-center': collapsed,
|
||||
})}
|
||||
@@ -144,11 +144,11 @@ export function AccountSelector({
|
||||
)}
|
||||
</If>
|
||||
|
||||
<CaretSortIcon className="h-4 w-4 shrink-0 opacity-50" />
|
||||
<CaretSortIcon className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
|
||||
<PopoverContent className="w-full p-0">
|
||||
<PopoverContent className="w-full p-0" collisionPadding={20}>
|
||||
<Command>
|
||||
<CommandInput placeholder={t('searchAccount')} className="h-9" />
|
||||
|
||||
|
||||
@@ -115,15 +115,15 @@ export function PersonalAccountDropdown({
|
||||
</div>
|
||||
|
||||
<EllipsisVertical
|
||||
className={'text-muted-foreground hidden h-8 group-hover:flex'}
|
||||
className={'text-muted-foreground mr-1 hidden h-8 group-hover:flex'}
|
||||
/>
|
||||
</If>
|
||||
</DropdownMenuTrigger>
|
||||
|
||||
<DropdownMenuContent
|
||||
className={'!min-w-[15rem]'}
|
||||
className={'xl:!min-w-[15rem]'}
|
||||
collisionPadding={{ right: 20, left: 20 }}
|
||||
sideOffset={20}
|
||||
sideOffset={10}
|
||||
>
|
||||
<DropdownMenuItem className={'!h-10 rounded-none'}>
|
||||
<div
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use client';
|
||||
|
||||
import { useCallback, useState } from 'react';
|
||||
|
||||
import type { Factor } from '@supabase/supabase-js';
|
||||
@@ -25,7 +27,7 @@ import {
|
||||
import { Badge } from '@kit/ui/badge';
|
||||
import { Button } from '@kit/ui/button';
|
||||
import { If } from '@kit/ui/if';
|
||||
import Spinner from '@kit/ui/spinner';
|
||||
import { Spinner } from '@kit/ui/spinner';
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
|
||||
@@ -30,7 +30,7 @@ import {
|
||||
InputOTPSeparator,
|
||||
InputOTPSlot,
|
||||
} from '@kit/ui/input-otp';
|
||||
import Spinner from '@kit/ui/spinner';
|
||||
import { Spinner } from '@kit/ui/spinner';
|
||||
import { Trans } from '@kit/ui/trans';
|
||||
|
||||
export function MultiFactorChallengeContainer({
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
"@kit/tsconfig": "workspace:*",
|
||||
"@kit/ui": "workspace:*",
|
||||
"@supabase/supabase-js": "^2.42.7",
|
||||
"@tanstack/react-query": "5.32.0",
|
||||
"@types/react": "^18.3.1",
|
||||
"lucide-react": "^0.376.0",
|
||||
"react": "18.3.1",
|
||||
|
||||
@@ -111,12 +111,12 @@ export function NotificationsPopover(params: {
|
||||
return (
|
||||
<Popover modal open={open} onOpenChange={setOpen}>
|
||||
<PopoverTrigger asChild>
|
||||
<Button className={'h-8 w-8'} variant={'ghost'}>
|
||||
<Button className={'h-9 w-9'} variant={'ghost'}>
|
||||
<Bell className={'min-h-5 min-w-5'} />
|
||||
|
||||
<span
|
||||
className={cn(
|
||||
`fade-in animate-in zoom-in absolute right-5 top-5 flex h-3.5 w-3.5 items-center justify-center rounded-full bg-red-500 text-[0.65rem] text-white`,
|
||||
`fade-in animate-in zoom-in absolute right-5 top-5 mt-0 flex h-3.5 w-3.5 items-center justify-center rounded-full bg-red-500 text-[0.65rem] text-white`,
|
||||
{
|
||||
hidden: !unread.length,
|
||||
},
|
||||
@@ -128,8 +128,10 @@ export function NotificationsPopover(params: {
|
||||
</PopoverTrigger>
|
||||
|
||||
<PopoverContent
|
||||
className={'flex flex-col p-0'}
|
||||
collisionPadding={{ right: 20 }}
|
||||
className={'flex w-full flex-col p-0 lg:min-w-64'}
|
||||
align={'start'}
|
||||
collisionPadding={20}
|
||||
sideOffset={10}
|
||||
>
|
||||
<div className={'flex items-center px-3 py-2 text-sm font-semibold'}>
|
||||
{t('common:notifications')}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
|
||||
import { useSupabase } from '@kit/supabase/hooks/use-supabase';
|
||||
|
||||
@@ -20,8 +22,8 @@ export function useFetchNotifications({
|
||||
accountIds: string[];
|
||||
realtime: boolean;
|
||||
}) {
|
||||
const { data: notifications } = useFetchInitialNotifications({ accountIds });
|
||||
const client = useSupabase();
|
||||
const didFetchInitialData = useRef(false);
|
||||
|
||||
useEffect(() => {
|
||||
let realtimeSubscription: { unsubscribe: () => void } | null = null;
|
||||
@@ -45,10 +47,26 @@ export function useFetchNotifications({
|
||||
.subscribe();
|
||||
}
|
||||
|
||||
if (!didFetchInitialData.current) {
|
||||
const now = new Date().toISOString();
|
||||
if (notifications) {
|
||||
onNotifications(notifications);
|
||||
}
|
||||
|
||||
const initialFetch = client
|
||||
return () => {
|
||||
if (realtimeSubscription) {
|
||||
realtimeSubscription.unsubscribe();
|
||||
}
|
||||
};
|
||||
}, [client, onNotifications, accountIds, realtime, notifications]);
|
||||
}
|
||||
|
||||
function useFetchInitialNotifications(props: { accountIds: string[] }) {
|
||||
const client = useSupabase();
|
||||
const now = new Date().toISOString();
|
||||
|
||||
return useQuery({
|
||||
queryKey: ['notifications', ...props.accountIds],
|
||||
queryFn: async () => {
|
||||
const { data } = await client
|
||||
.from('notifications')
|
||||
.select(
|
||||
`id,
|
||||
@@ -59,29 +77,14 @@ export function useFetchNotifications({
|
||||
link
|
||||
`,
|
||||
)
|
||||
.in('account_id', accountIds)
|
||||
.in('account_id', props.accountIds)
|
||||
.eq('dismissed', false)
|
||||
.gt('expires_at', now)
|
||||
.order('created_at', { ascending: false })
|
||||
.limit(10);
|
||||
|
||||
didFetchInitialData.current = true;
|
||||
|
||||
void initialFetch.then(({ data, error }) => {
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
if (data) {
|
||||
onNotifications(data);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (realtimeSubscription) {
|
||||
realtimeSubscription.unsubscribe();
|
||||
}
|
||||
};
|
||||
}, [client, onNotifications, accountIds, realtime]);
|
||||
return data;
|
||||
},
|
||||
refetchOnMount: false,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -217,7 +217,6 @@ function ActionsDropdown({
|
||||
<UpdateInvitationDialog
|
||||
isOpen
|
||||
setIsOpen={setIsUpdatingRole}
|
||||
account={invitation.account_id}
|
||||
invitationId={invitation.id}
|
||||
userRole={invitation.role}
|
||||
userRoleHierarchy={permissions.currentUserRoleHierarchy}
|
||||
|
||||
@@ -36,17 +36,9 @@ export const UpdateInvitationDialog: React.FC<{
|
||||
isOpen: boolean;
|
||||
setIsOpen: (isOpen: boolean) => void;
|
||||
invitationId: number;
|
||||
account: string;
|
||||
userRole: Role;
|
||||
userRoleHierarchy: number;
|
||||
}> = ({
|
||||
isOpen,
|
||||
setIsOpen,
|
||||
invitationId,
|
||||
userRole,
|
||||
userRoleHierarchy,
|
||||
account,
|
||||
}) => {
|
||||
}> = ({ isOpen, setIsOpen, invitationId, userRole, userRoleHierarchy }) => {
|
||||
return (
|
||||
<Dialog open={isOpen} onOpenChange={setIsOpen}>
|
||||
<DialogContent>
|
||||
@@ -61,7 +53,6 @@ export const UpdateInvitationDialog: React.FC<{
|
||||
</DialogHeader>
|
||||
|
||||
<UpdateInvitationForm
|
||||
account={account}
|
||||
invitationId={invitationId}
|
||||
userRole={userRole}
|
||||
userRoleHierarchy={userRoleHierarchy}
|
||||
@@ -73,13 +64,11 @@ export const UpdateInvitationDialog: React.FC<{
|
||||
};
|
||||
|
||||
function UpdateInvitationForm({
|
||||
account,
|
||||
invitationId,
|
||||
userRole,
|
||||
userRoleHierarchy,
|
||||
setIsOpen,
|
||||
}: React.PropsWithChildren<{
|
||||
account: string;
|
||||
invitationId: number;
|
||||
userRole: Role;
|
||||
userRoleHierarchy: number;
|
||||
|
||||
Reference in New Issue
Block a user