Add new pages and refactor existing code
This commit adds new Admin and Accounts pages, while also improving code by refactoring various portions such as extracting services from the join page and dynamically importing packages in logging and monitoring code. The build command is also removed from the WordPress package, and SWC minification is enabled in the Next.js configuration. Updated marketing content is also included in this commit.
This commit is contained in:
47
apps/web/app/join/_lib/server/join-team.service.ts
Normal file
47
apps/web/app/join/_lib/server/join-team.service.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import { getSupabaseServerComponentClient } from '@kit/supabase/server-component-client';
|
||||
|
||||
export class JoinTeamService {
|
||||
async isCurrentUserAlreadyInAccount(accountId: string) {
|
||||
const client = getSupabaseServerComponentClient();
|
||||
|
||||
const { data } = await client
|
||||
.from('accounts')
|
||||
.select('id')
|
||||
.eq('id', accountId)
|
||||
.maybeSingle();
|
||||
|
||||
return !!data?.id;
|
||||
}
|
||||
|
||||
async getInviteDataFromInviteToken(token: string) {
|
||||
// we use an admin client to be able to read the pending membership
|
||||
// without having to be logged in
|
||||
const adminClient = getSupabaseServerComponentClient({ admin: true });
|
||||
|
||||
const { data: invitation, error } = await adminClient
|
||||
.from('invitations')
|
||||
.select<
|
||||
string,
|
||||
{
|
||||
id: string;
|
||||
account: {
|
||||
id: string;
|
||||
name: string;
|
||||
slug: string;
|
||||
picture_url: string;
|
||||
};
|
||||
}
|
||||
>(
|
||||
'id, expires_at, account: account_id !inner (id, name, slug, picture_url)',
|
||||
)
|
||||
.eq('invite_token', token)
|
||||
.gte('expires_at', new Date().toISOString())
|
||||
.single();
|
||||
|
||||
if (!invitation ?? error) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return invitation;
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,6 @@ import { notFound, redirect } from 'next/navigation';
|
||||
|
||||
import { ArrowLeft } from 'lucide-react';
|
||||
|
||||
import { Logger } from '@kit/shared/logger';
|
||||
import { requireUser } from '@kit/supabase/require-user';
|
||||
import { getSupabaseServerComponentClient } from '@kit/supabase/server-component-client';
|
||||
import { AcceptInvitationContainer } from '@kit/team-accounts/components';
|
||||
@@ -15,6 +14,8 @@ import pathsConfig from '~/config/paths.config';
|
||||
import { createI18nServerInstance } from '~/lib/i18n/i18n.server';
|
||||
import { withI18n } from '~/lib/i18n/with-i18n';
|
||||
|
||||
import { JoinTeamService } from './_lib/server/join-team.service';
|
||||
|
||||
interface Context {
|
||||
searchParams: {
|
||||
invite_token: string;
|
||||
@@ -47,19 +48,23 @@ async function JoinTeamAccountPage({ searchParams }: Context) {
|
||||
redirect(pathsConfig.auth.signUp + '?invite_token=' + token);
|
||||
}
|
||||
|
||||
const service = new JoinTeamService();
|
||||
|
||||
// the user is logged in, we can now check if the token is valid
|
||||
const invitation = await getInviteDataFromInviteToken(token);
|
||||
const invitation = await service.getInviteDataFromInviteToken(token);
|
||||
|
||||
if (!invitation) {
|
||||
return <InviteNotFoundOrExpired />;
|
||||
}
|
||||
|
||||
// we need to verify the user isn't already in the account
|
||||
const isInAccount = await isCurrentUserAlreadyInAccount(
|
||||
const isInAccount = await service.isCurrentUserAlreadyInAccount(
|
||||
invitation.account.id,
|
||||
);
|
||||
|
||||
if (isInAccount) {
|
||||
const { Logger } = await import('@kit/shared/logger');
|
||||
|
||||
Logger.warn(
|
||||
{
|
||||
name: 'join-team-account',
|
||||
@@ -97,56 +102,6 @@ async function JoinTeamAccountPage({ searchParams }: Context) {
|
||||
|
||||
export default withI18n(JoinTeamAccountPage);
|
||||
|
||||
/**
|
||||
* Verifies that the current user is not already in the account by
|
||||
* reading the document from the `accounts` table. If the user can read it
|
||||
* it means they are already in the account.
|
||||
* @param accountId
|
||||
*/
|
||||
async function isCurrentUserAlreadyInAccount(accountId: string) {
|
||||
const client = getSupabaseServerComponentClient();
|
||||
|
||||
const { data } = await client
|
||||
.from('accounts')
|
||||
.select('id')
|
||||
.eq('id', accountId)
|
||||
.maybeSingle();
|
||||
|
||||
return !!data?.id;
|
||||
}
|
||||
|
||||
async function getInviteDataFromInviteToken(token: string) {
|
||||
// we use an admin client to be able to read the pending membership
|
||||
// without having to be logged in
|
||||
const adminClient = getSupabaseServerComponentClient({ admin: true });
|
||||
|
||||
const { data: invitation, error } = await adminClient
|
||||
.from('invitations')
|
||||
.select<
|
||||
string,
|
||||
{
|
||||
id: string;
|
||||
account: {
|
||||
id: string;
|
||||
name: string;
|
||||
slug: string;
|
||||
picture_url: string;
|
||||
};
|
||||
}
|
||||
>(
|
||||
'id, expires_at, account: account_id !inner (id, name, slug, picture_url)',
|
||||
)
|
||||
.eq('invite_token', token)
|
||||
.gte('expires_at', new Date().toISOString())
|
||||
.single();
|
||||
|
||||
if (!invitation ?? error) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return invitation;
|
||||
}
|
||||
|
||||
function InviteNotFoundOrExpired() {
|
||||
return (
|
||||
<div className={'flex flex-col space-y-4'}>
|
||||
|
||||
Reference in New Issue
Block a user