Refactor Supabase server client and improve team management features

Refactored Supabase server component client to improve type safety. Improved team account management by adjusting role data providers to consider account IDs, and adjusted invite member functionality to use account IDs and slugs. Enhanced invitation update dialog by adding account parameter. Fixed database webhook URLs in seed.sql. Overall, these changes enhance the robustness and usability of the team management functionalities.
This commit is contained in:
giancarlo
2024-04-03 22:34:12 +08:00
parent 53afd10f32
commit 406739d96d
14 changed files with 1074 additions and 1000 deletions

View File

@@ -197,6 +197,7 @@ function ActionsDropdown({
<UpdateInvitationDialog
isOpen
setIsOpen={setIsUpdatingRole}
account={invitation.account_id}
invitationId={invitation.id}
userRole={invitation.role}
userRoleHierarchy={permissions.currentUserRoleHierarchy}

View File

@@ -36,9 +36,17 @@ export const UpdateInvitationDialog: React.FC<{
isOpen: boolean;
setIsOpen: (isOpen: boolean) => void;
invitationId: number;
account: string;
userRole: Role;
userRoleHierarchy: number;
}> = ({ isOpen, setIsOpen, invitationId, userRole, userRoleHierarchy }) => {
}> = ({
isOpen,
setIsOpen,
invitationId,
userRole,
userRoleHierarchy,
account,
}) => {
return (
<Dialog open={isOpen} onOpenChange={setIsOpen}>
<DialogContent>
@@ -52,9 +60,13 @@ export const UpdateInvitationDialog: React.FC<{
</DialogDescription>
</DialogHeader>
<RolesDataProvider maxRoleHierarchy={userRoleHierarchy}>
<RolesDataProvider
accountId={account}
maxRoleHierarchy={userRoleHierarchy}
>
{(roles) => (
<UpdateInvitationForm
account={account}
invitationId={invitationId}
userRole={userRole}
userRoleHierarchy={roles.length}
@@ -68,11 +80,13 @@ export const UpdateInvitationDialog: React.FC<{
};
function UpdateInvitationForm({
account,
invitationId,
userRole,
userRoleHierarchy,
setIsOpen,
}: React.PropsWithChildren<{
account: string;
invitationId: number;
userRole: Role;
userRoleHierarchy: number;
@@ -136,7 +150,10 @@ function UpdateInvitationForm({
</FormLabel>
<FormControl>
<RolesDataProvider maxRoleHierarchy={userRoleHierarchy}>
<RolesDataProvider
accountId={account}
maxRoleHierarchy={userRoleHierarchy}
>
{(roles) => (
<MembershipRoleSelector
roles={roles}

View File

@@ -42,11 +42,13 @@ type InviteModel = ReturnType<typeof createEmptyInviteModel>;
type Role = string;
export function InviteMembersDialogContainer({
account,
accountSlug,
accountId,
userRoleHierarchy,
children,
}: React.PropsWithChildren<{
account: string;
accountSlug: string;
accountId: string;
userRoleHierarchy: number;
}>) {
const [pending, startTransition] = useTransition();
@@ -67,7 +69,10 @@ export function InviteMembersDialogContainer({
</DialogDescription>
</DialogHeader>
<RolesDataProvider maxRoleHierarchy={userRoleHierarchy}>
<RolesDataProvider
accountId={accountId}
maxRoleHierarchy={userRoleHierarchy}
>
{(roles) => (
<InviteMembersForm
pending={pending}
@@ -75,7 +80,7 @@ export function InviteMembersDialogContainer({
onSubmit={(data) => {
startTransition(async () => {
await createInvitationsAction({
account,
accountSlug,
invitations: data.invitations,
});

View File

@@ -5,17 +5,15 @@ import { LoadingOverlay } from '@kit/ui/loading-overlay';
export function RolesDataProvider(props: {
maxRoleHierarchy: number;
accountId: string;
children: (roles: string[]) => React.ReactNode;
}) {
const rolesQuery = useFetchRoles({
maxRoleHierarchy: props.maxRoleHierarchy,
});
const rolesQuery = useFetchRoles(props);
if (rolesQuery.isLoading) {
return <LoadingOverlay fullPage={false} />;
}
// TODO handle error
if (rolesQuery.isError) {
return null;
}
@@ -23,7 +21,7 @@ export function RolesDataProvider(props: {
return <>{props.children(rolesQuery.data ?? [])}</>;
}
function useFetchRoles(props: { maxRoleHierarchy: number }) {
function useFetchRoles(props: { maxRoleHierarchy: number; accountId: string }) {
const supabase = useSupabase();
return useQuery({
@@ -33,6 +31,7 @@ function useFetchRoles(props: { maxRoleHierarchy: number }) {
.from('roles')
.select('name')
.gte('hierarchy_level', props.maxRoleHierarchy)
.or(`account_id.eq.${props.accountId}, account_id.is.null`)
.order('hierarchy_level', { ascending: true });
if (error) {

View File

@@ -60,7 +60,10 @@ export const UpdateMemberRoleDialog: React.FC<{
</DialogDescription>
</DialogHeader>
<RolesDataProvider maxRoleHierarchy={userRoleHierarchy}>
<RolesDataProvider
accountId={accountId}
maxRoleHierarchy={userRoleHierarchy}
>
{(data) => (
<UpdateMemberForm
setIsOpen={setIsOpen}

View File

@@ -22,7 +22,7 @@ import { AccountInvitationsService } from '../services/account-invitations.servi
* Creates invitations for inviting members.
*/
export async function createInvitationsAction(params: {
account: string;
accountSlug: string;
invitations: z.infer<typeof InviteMembersSchema>['invitations'];
}) {
const client = getSupabaseServerActionClient();
@@ -35,7 +35,10 @@ export async function createInvitationsAction(params: {
const service = new AccountInvitationsService(client);
await service.sendInvitations({ invitations, account: params.account });
await service.sendInvitations({
invitations,
accountSlug: params.accountSlug,
});
revalidateMemberPage();

View File

@@ -69,21 +69,21 @@ export class AccountInvitationsService {
}
async sendInvitations({
account,
accountSlug,
invitations,
}: {
invitations: z.infer<typeof InviteMembersSchema>['invitations'];
account: string;
accountSlug: string;
}) {
Logger.info(
{ account, invitations, name: this.namespace },
{ account: accountSlug, invitations, name: this.namespace },
'Storing invitations',
);
const accountResponse = await this.client
.from('accounts')
.select('name')
.eq('slug', account)
.eq('slug', accountSlug)
.single();
if (!accountResponse.data) {
@@ -92,7 +92,7 @@ export class AccountInvitationsService {
const response = await this.client.rpc('add_invitations_to_account', {
invitations,
account_slug: account,
account_slug: accountSlug,
});
if (response.error) {
@@ -105,7 +105,7 @@ export class AccountInvitationsService {
Logger.info(
{
account,
account: accountSlug,
count: responseInvitations.length,
name: this.namespace,
},