Remove use-toast.tsx, refactor auth methods, and update UI components

This commit removes the redundant `use-toast.tsx` file and refactors the authentication methods in `personal-accounts-server-actions.ts` to enhance service integration and data handling. UI components like `LanguageDropdownSwitcher` and `site-header-account-section.tsx` have been refined for better readability. Changes in the `config.tsx` file aim to improve the overall code cleanliness. The `schema.sql` file under migrations was updated to cascade delete for 'invited_by' reference.
This commit is contained in:
giancarlo
2024-03-27 01:37:34 +08:00
parent 8a614bd6fc
commit 6699399864
10 changed files with 28 additions and 208 deletions

View File

@@ -26,9 +26,7 @@ export function SiteHeaderAccountSection(
return <AuthButtons />;
}
return (
<SuspendedPersonalAccountDropdown session={props.session} />
);
return <SuspendedPersonalAccountDropdown session={props.session} />;
}
function SuspendedPersonalAccountDropdown(props: { session: Session | null }) {

View File

@@ -82,4 +82,4 @@
]
},
"prettier": "@kit/prettier-config"
}
}

View File

@@ -1 +1 @@
{}
{}

View File

@@ -1 +1 @@
{}
{}

View File

@@ -33,9 +33,15 @@ export async function deletePersonalAccountAction(formData: FormData) {
`Deleting personal account...`,
);
const deleteAccountResponse = await service.deletePersonalAccount({
userId,
});
const deleteAccountResponse = await service.deletePersonalAccount(
getSupabaseServerActionClient({ admin: true }),
{
userId,
},
);
//
// also delete any associated data and subscriptions
if (deleteAccountResponse.error) {
Logger.error(

View File

@@ -13,5 +13,10 @@ import { Database } from '@kit/supabase/database';
export class PersonalAccountsService {
constructor(private readonly client: SupabaseClient<Database>) {}
async deletePersonalAccount(param: { userId: string }) {}
async deletePersonalAccount(
adminClient: SupabaseClient<Database>,
params: { userId: string },
) {
return adminClient.auth.admin.deleteUser(params.userId);
}
}

View File

@@ -2,9 +2,9 @@
import { useCallback, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useRouter } from 'next/navigation';
import useRefresh from '@kit/shared/hooks/use-refresh';
import { useTranslation } from 'react-i18next';
import {
Select,
@@ -14,11 +14,11 @@ import {
SelectValue,
} from '../shadcn/select';
const LanguageDropdownSwitcher: React.FC<{
export const LanguageDropdownSwitcher: React.FC<{
onChange?: (locale: string) => unknown;
}> = ({ onChange }) => {
const { i18n } = useTranslation();
const refresh = useRefresh();
const router = useRouter();
const { language: currentLanguage, options } = i18n;
@@ -43,9 +43,10 @@ const LanguageDropdownSwitcher: React.FC<{
}
await i18n.changeLanguage(locale);
return refresh();
return router.refresh();
},
[i18n, onChange, refresh],
[i18n, onChange, router],
);
return (
@@ -77,5 +78,3 @@ const LanguageDropdownSwitcher: React.FC<{
function capitalize(lang: string) {
return lang.slice(0, 1).toUpperCase() + lang.slice(1);
}
export default LanguageDropdownSwitcher;

View File

@@ -42,6 +42,7 @@ const CommandInput = React.forwardRef<
React.ElementRef<typeof CommandPrimitive.Input>,
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Input>
>(({ className, ...props }, ref) => (
// eslint-disable-next-line react/no-unknown-property
<div className="flex items-center border-b px-3" cmdk-input-wrapper="">
<MagnifyingGlassIcon className="mr-2 h-4 w-4 shrink-0 opacity-50" />
<CommandPrimitive.Input

View File

@@ -1,189 +0,0 @@
// Inspired by react-hot-toast library
import * as React from 'react';
import type { ToastActionElement, ToastProps } from './toast';
const TOAST_LIMIT = 1;
const TOAST_REMOVE_DELAY = 1000000;
type ToasterToast = ToastProps & {
id: string;
title?: React.ReactNode;
description?: React.ReactNode;
action?: ToastActionElement;
};
const actionTypes = {
ADD_TOAST: 'ADD_TOAST',
UPDATE_TOAST: 'UPDATE_TOAST',
DISMISS_TOAST: 'DISMISS_TOAST',
REMOVE_TOAST: 'REMOVE_TOAST',
} as const;
let count = 0;
function genId() {
count = (count + 1) % Number.MAX_VALUE;
return count.toString();
}
type ActionType = typeof actionTypes;
type Action =
| {
type: ActionType['ADD_TOAST'];
toast: ToasterToast;
}
| {
type: ActionType['UPDATE_TOAST'];
toast: Partial<ToasterToast>;
}
| {
type: ActionType['DISMISS_TOAST'];
toastId?: ToasterToast['id'];
}
| {
type: ActionType['REMOVE_TOAST'];
toastId?: ToasterToast['id'];
};
interface State {
toasts: ToasterToast[];
}
const toastTimeouts = new Map<string, ReturnType<typeof setTimeout>>();
const addToRemoveQueue = (toastId: string) => {
if (toastTimeouts.has(toastId)) {
return;
}
const timeout = setTimeout(() => {
toastTimeouts.delete(toastId);
dispatch({
type: 'REMOVE_TOAST',
toastId: toastId,
});
}, TOAST_REMOVE_DELAY);
toastTimeouts.set(toastId, timeout);
};
export const reducer = (state: State, action: Action): State => {
switch (action.type) {
case 'ADD_TOAST':
return {
...state,
toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT),
};
case 'UPDATE_TOAST':
return {
...state,
toasts: state.toasts.map((t) =>
t.id === action.toast.id ? { ...t, ...action.toast } : t,
),
};
case 'DISMISS_TOAST': {
const { toastId } = action;
// ! Side effects ! - This could be extracted into a dismissToast() action,
// but I'll keep it here for simplicity
if (toastId) {
addToRemoveQueue(toastId);
} else {
state.toasts.forEach((toast) => {
addToRemoveQueue(toast.id);
});
}
return {
...state,
toasts: state.toasts.map((t) =>
t.id === toastId || toastId === undefined
? {
...t,
open: false,
}
: t,
),
};
}
case 'REMOVE_TOAST':
if (action.toastId === undefined) {
return {
...state,
toasts: [],
};
}
return {
...state,
toasts: state.toasts.filter((t) => t.id !== action.toastId),
};
}
};
const listeners: ((state: State) => void)[] = [];
let memoryState: State = { toasts: [] };
function dispatch(action: Action) {
memoryState = reducer(memoryState, action);
listeners.forEach((listener) => {
listener(memoryState);
});
}
type Toast = Omit<ToasterToast, 'id'>;
function toast({ ...props }: Toast) {
const id = genId();
const update = (props: ToasterToast) =>
dispatch({
type: 'UPDATE_TOAST',
toast: { ...props, id },
});
const dismiss = () => dispatch({ type: 'DISMISS_TOAST', toastId: id });
dispatch({
type: 'ADD_TOAST',
toast: {
...props,
id,
open: true,
onOpenChange: (open) => {
if (!open) dismiss();
},
},
});
return {
id: id,
dismiss,
update,
};
}
function useToast() {
const [state, setState] = React.useState<State>(memoryState);
React.useEffect(() => {
listeners.push(setState);
return () => {
const index = listeners.indexOf(setState);
if (index > -1) {
listeners.splice(index, 1);
}
};
}, [state]);
return {
...state,
toast,
dismiss: (toastId?: string) => dispatch({ type: 'DISMISS_TOAST', toastId }),
};
}
export { useToast, toast };

View File

@@ -671,7 +671,7 @@ create table if not exists
id serial primary key,
email varchar(255) not null,
account_id uuid references public.accounts (id) on delete cascade not null,
invited_by uuid references auth.users not null,
invited_by uuid references auth.users on delete cascade not null,
role public.account_role not null,
invite_token varchar(255) unique not null,
created_at timestamp default current_timestamp not null,