Next.js Supabase V3 (#463)
Version 3 of the kit: - Radix UI replaced with Base UI (using the Shadcn UI patterns) - next-intl replaces react-i18next - enhanceAction deprecated; usage moved to next-safe-action - main layout now wrapped with [locale] path segment - Teams only mode - Layout updates - Zod v4 - Next.js 16.2 - Typescript 6 - All other dependencies updated - Removed deprecated Edge CSRF - Dynamic Github Action runner
This commit is contained in:
committed by
GitHub
parent
4912e402a3
commit
7ebff31475
@@ -1,3 +0,0 @@
|
||||
import eslintConfigBase from '@kit/eslint-config/base.js';
|
||||
|
||||
export default eslintConfigBase;
|
||||
@@ -1,33 +1,7 @@
|
||||
{
|
||||
"name": "@kit/notifications",
|
||||
"private": true,
|
||||
"version": "0.1.0",
|
||||
"scripts": {
|
||||
"clean": "git clean -xdf .turbo node_modules",
|
||||
"format": "prettier --check \"**/*.{ts,tsx}\"",
|
||||
"lint": "eslint .",
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
"exports": {
|
||||
"./api": "./src/server/api.ts",
|
||||
"./components": "./src/components/index.ts",
|
||||
"./hooks": "./src/hooks/index.ts"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@kit/eslint-config": "workspace:*",
|
||||
"@kit/prettier-config": "workspace:*",
|
||||
"@kit/supabase": "workspace:*",
|
||||
"@kit/tsconfig": "workspace:*",
|
||||
"@kit/ui": "workspace:*",
|
||||
"@supabase/supabase-js": "catalog:",
|
||||
"@tanstack/react-query": "catalog:",
|
||||
"@types/react": "catalog:",
|
||||
"lucide-react": "catalog:",
|
||||
"react": "catalog:",
|
||||
"react-dom": "catalog:",
|
||||
"react-i18next": "catalog:"
|
||||
},
|
||||
"prettier": "@kit/prettier-config",
|
||||
"private": true,
|
||||
"typesVersions": {
|
||||
"*": {
|
||||
"*": [
|
||||
@@ -35,7 +9,28 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"exports": {
|
||||
"./api": "./src/server/api.ts",
|
||||
"./components": "./src/components/index.ts",
|
||||
"./hooks": "./src/hooks/index.ts"
|
||||
},
|
||||
"scripts": {
|
||||
"clean": "git clean -xdf .turbo node_modules",
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/node": "catalog:"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@kit/supabase": "workspace:*",
|
||||
"@kit/tsconfig": "workspace:*",
|
||||
"@kit/ui": "workspace:*",
|
||||
"@supabase/supabase-js": "catalog:",
|
||||
"@tanstack/react-query": "catalog:",
|
||||
"@types/react": "catalog:",
|
||||
"lucide-react": "catalog:",
|
||||
"next-intl": "catalog:",
|
||||
"react": "catalog:",
|
||||
"react-dom": "catalog:"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
|
||||
import { Bell, CircleAlert, Info, TriangleAlert, XIcon } from 'lucide-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useLocale, useTranslations } from 'next-intl';
|
||||
|
||||
import { Button } from '@kit/ui/button';
|
||||
import { If } from '@kit/ui/if';
|
||||
@@ -19,7 +19,8 @@ export function NotificationsPopover(params: {
|
||||
accountIds: string[];
|
||||
onClick?: (notification: Notification) => void;
|
||||
}) {
|
||||
const { i18n, t } = useTranslation();
|
||||
const t = useTranslations();
|
||||
const locale = useLocale();
|
||||
|
||||
const [open, setOpen] = useState(false);
|
||||
const [notifications, setNotifications] = useState<Notification[]>([]);
|
||||
@@ -53,7 +54,7 @@ export function NotificationsPopover(params: {
|
||||
(new Date().getTime() - date.getTime()) / (1000 * 60 * 60 * 24),
|
||||
);
|
||||
|
||||
const formatter = new Intl.RelativeTimeFormat(i18n.language, {
|
||||
const formatter = new Intl.RelativeTimeFormat(locale, {
|
||||
numeric: 'auto',
|
||||
});
|
||||
|
||||
@@ -61,7 +62,7 @@ export function NotificationsPopover(params: {
|
||||
time = Math.floor((new Date().getTime() - date.getTime()) / (1000 * 60));
|
||||
|
||||
if (time < 5) {
|
||||
return t('common:justNow');
|
||||
return t('common.justNow');
|
||||
}
|
||||
|
||||
if (time < 60) {
|
||||
@@ -110,46 +111,39 @@ export function NotificationsPopover(params: {
|
||||
|
||||
return (
|
||||
<Popover modal open={open} onOpenChange={setOpen}>
|
||||
<PopoverTrigger asChild>
|
||||
<Button className={'relative h-9 w-9'} variant={'ghost'}>
|
||||
<Bell className={'min-h-4 min-w-4'} />
|
||||
<PopoverTrigger
|
||||
render={<Button size="icon-lg" variant="ghost" className="relative" />}
|
||||
>
|
||||
<Bell className={'size-4 min-h-3 min-w-3'} />
|
||||
|
||||
<span
|
||||
className={cn(
|
||||
`fade-in animate-in zoom-in absolute top-1 right-1 mt-0 flex h-3.5 w-3.5 items-center justify-center rounded-full bg-red-500 text-[0.65rem] text-white`,
|
||||
{
|
||||
hidden: !notifications.length,
|
||||
},
|
||||
)}
|
||||
>
|
||||
{notifications.length}
|
||||
</span>
|
||||
</Button>
|
||||
<span
|
||||
className={cn(
|
||||
`fade-in animate-in zoom-in absolute top-1 right-1 mt-0 flex h-3 w-3 items-center justify-center rounded-full bg-red-500 text-[0.6rem] text-white`,
|
||||
{
|
||||
hidden: !notifications.length,
|
||||
},
|
||||
)}
|
||||
>
|
||||
{notifications.length}
|
||||
</span>
|
||||
</PopoverTrigger>
|
||||
|
||||
<PopoverContent
|
||||
className={'flex w-full max-w-96 flex-col p-0 lg:min-w-64'}
|
||||
className={'flex w-full max-w-96 flex-col gap-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')}
|
||||
<div className={'flex items-center text-sm font-semibold'}>
|
||||
{t('common.notifications')}
|
||||
</div>
|
||||
|
||||
<Separator />
|
||||
|
||||
<If condition={!notifications.length}>
|
||||
<div className={'px-3 py-2 text-sm'}>
|
||||
{t('common:noNotifications')}
|
||||
</div>
|
||||
<div className={'text-sm'}>{t('common.noNotifications')}</div>
|
||||
</If>
|
||||
|
||||
<div
|
||||
className={
|
||||
'flex max-h-[60vh] flex-col divide-y divide-gray-100 overflow-y-auto dark:divide-gray-800'
|
||||
}
|
||||
>
|
||||
<div className={'flex max-h-[60vh] flex-col overflow-y-auto'}>
|
||||
{notifications.map((notification) => {
|
||||
const maxChars = 100;
|
||||
|
||||
@@ -164,11 +158,11 @@ export function NotificationsPopover(params: {
|
||||
const Icon = () => {
|
||||
switch (notification.type) {
|
||||
case 'warning':
|
||||
return <TriangleAlert className={'h-4 text-yellow-500'} />;
|
||||
return <TriangleAlert className={'size-3 text-yellow-500'} />;
|
||||
case 'error':
|
||||
return <CircleAlert className={'text-destructive h-4'} />;
|
||||
return <CircleAlert className={'text-destructive size-3'} />;
|
||||
default:
|
||||
return <Info className={'h-4 text-blue-500'} />;
|
||||
return <Info className={'size-3 text-blue-500'} />;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -176,7 +170,7 @@ export function NotificationsPopover(params: {
|
||||
<div
|
||||
key={notification.id.toString()}
|
||||
className={cn(
|
||||
'flex min-h-18 flex-col items-start justify-center gap-y-1 px-3 py-2',
|
||||
'flex min-h-14 flex-col items-start justify-center gap-y-1 px-1',
|
||||
)}
|
||||
onClick={() => {
|
||||
if (params.onClick) {
|
||||
@@ -185,15 +179,11 @@ export function NotificationsPopover(params: {
|
||||
}}
|
||||
>
|
||||
<div className={'flex w-full items-start justify-between'}>
|
||||
<div
|
||||
className={'flex items-start justify-start gap-x-3 py-2'}
|
||||
>
|
||||
<div className={'py-0.5'}>
|
||||
<Icon />
|
||||
</div>
|
||||
<div className={'flex items-start justify-start gap-x-1.5'}>
|
||||
<div className={'flex flex-col'}>
|
||||
<div className={'flex items-center gap-x-2 text-sm'}>
|
||||
<Icon />
|
||||
|
||||
<div className={'flex flex-col space-y-1'}>
|
||||
<div className={'text-sm'}>
|
||||
<If condition={notification.link} fallback={body}>
|
||||
{(link) => (
|
||||
<a href={link} className={'hover:underline'}>
|
||||
@@ -209,7 +199,7 @@ export function NotificationsPopover(params: {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={'py-2'}>
|
||||
<div className={'ml-2'}>
|
||||
<Button
|
||||
className={'max-h-6 max-w-6'}
|
||||
size={'icon'}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import 'server-only';
|
||||
|
||||
import { SupabaseClient } from '@supabase/supabase-js';
|
||||
|
||||
import { Database } from '@kit/supabase/database';
|
||||
|
||||
Reference in New Issue
Block a user