+
;
-}
-
-export default AdminGuard(AccountPage);
diff --git a/apps/web/app/admin/accounts/[id]/page.tsx b/apps/web/app/admin/accounts/[id]/page.tsx
new file mode 100644
index 000000000..79bb0a8eb
--- /dev/null
+++ b/apps/web/app/admin/accounts/[id]/page.tsx
@@ -0,0 +1,45 @@
+import { cache } from 'react';
+
+import { AdminAccountPage } from '@kit/admin/components/admin-account-page';
+import { AdminGuard } from '@kit/admin/components/admin-guard';
+import { getSupabaseServerComponentClient } from '@kit/supabase/server-component-client';
+
+interface Params {
+ params: {
+ id: string;
+ };
+}
+
+export const generateMetadata = async ({ params }: Params) => {
+ const account = await loadAccount(params.id);
+
+ return {
+ title: `Admin | ${account.name}`,
+ };
+};
+
+async function AccountPage({ params }: Params) {
+ const account = await loadAccount(params.id);
+
+ return
;
+}
+
+export default AdminGuard(AccountPage);
+
+const loadAccount = cache(async (id: string) => {
+ const client = getSupabaseServerComponentClient({
+ admin: true,
+ });
+
+ const { data, error } = await client
+ .from('accounts')
+ .select('*, memberships: accounts_memberships (*)')
+ .eq('id', id)
+ .single();
+
+ if (error) {
+ throw error;
+ }
+
+ return data;
+});
diff --git a/apps/web/app/admin/accounts/page.tsx b/apps/web/app/admin/accounts/page.tsx
index 38dd8b17b..a59403f6d 100644
--- a/apps/web/app/admin/accounts/page.tsx
+++ b/apps/web/app/admin/accounts/page.tsx
@@ -1,6 +1,6 @@
import { ServerDataLoader } from '@makerkit/data-loader-supabase-nextjs';
-import { AccountsTable } from '@kit/admin/components/accounts-table';
+import { AdminAccountsTable } from '@kit/admin/components/admin-accounts-table';
import { AdminGuard } from '@kit/admin/components/admin-guard';
import { getSupabaseServerComponentClient } from '@kit/supabase/server-component-client';
import { PageBody, PageHeader } from '@kit/ui/page';
@@ -8,8 +8,13 @@ import { PageBody, PageHeader } from '@kit/ui/page';
interface SearchParams {
page?: string;
account_type?: 'all' | 'team' | 'personal';
+ query?: string;
}
+export const metadata = {
+ title: `Accounts`,
+};
+
function AccountsPage({ searchParams }: { searchParams: SearchParams }) {
const client = getSupabaseServerComponentClient({
admin: true,
@@ -34,7 +39,7 @@ function AccountsPage({ searchParams }: { searchParams: SearchParams }) {
>
{({ data, page, pageSize, pageCount }) => {
return (
-
}>{props.children};
}
diff --git a/packages/features/admin/src/components/admin-account-page.tsx b/packages/features/admin/src/components/admin-account-page.tsx
new file mode 100644
index 000000000..743b8c0ce
--- /dev/null
+++ b/packages/features/admin/src/components/admin-account-page.tsx
@@ -0,0 +1,104 @@
+import { Database } from '@kit/supabase/database';
+import { getSupabaseServerComponentClient } from '@kit/supabase/server-component-client';
+import { Heading } from '@kit/ui/heading';
+import { PageBody, PageHeader } from '@kit/ui/page';
+
+import { AdminMembersTable } from './admin-members-table';
+import { AdminMembershipsTable } from './admin-memberships-table';
+
+type Db = Database['public']['Tables'];
+type Account = Db['accounts']['Row'];
+type Membership = Db['accounts_memberships']['Row'];
+
+export function AdminAccountPage(props: {
+ account: Account & { memberships: Membership[] };
+}) {
+ if (props.account.is_personal_account) {
+ return
;
+ }
+
+ return
;
+}
+
+async function PersonalAccountPage(props: { account: Account }) {
+ const memberships = await getMemberships(props.account.id);
+
+ return (
+ <>
+
+
+
+
+
+ >
+ );
+}
+
+async function TeamAccountPage(props: {
+ account: Account & { memberships: Membership[] };
+}) {
+ const members = await getMembers(props.account.slug ?? '');
+
+ return (
+ <>
+
+
+
+
+
+ >
+ );
+}
+
+async function getMemberships(userId: string) {
+ const client = getSupabaseServerComponentClient({
+ admin: true,
+ });
+
+ const memberships = await client
+ .from('accounts_memberships')
+ .select<
+ string,
+ Membership & {
+ account: {
+ id: string;
+ name: string;
+ };
+ }
+ >('*, account: account_id !inner (id, name)')
+ .eq('user_id', userId);
+
+ if (memberships.error) {
+ throw memberships.error;
+ }
+
+ return memberships.data;
+}
+
+async function getMembers(accountSlug: string) {
+ const client = getSupabaseServerComponentClient({
+ admin: true,
+ });
+
+ const members = await client.rpc('get_account_members', {
+ account_slug: accountSlug,
+ });
+
+ if (members.error) {
+ throw members.error;
+ }
+
+ return members.data;
+}
diff --git a/packages/features/admin/src/components/accounts-table.tsx b/packages/features/admin/src/components/admin-accounts-table.tsx
similarity index 53%
rename from packages/features/admin/src/components/accounts-table.tsx
rename to packages/features/admin/src/components/admin-accounts-table.tsx
index 33b5e3140..26be21e10 100644
--- a/packages/features/admin/src/components/accounts-table.tsx
+++ b/packages/features/admin/src/components/admin-accounts-table.tsx
@@ -20,20 +20,27 @@ import {
DropdownMenuTrigger,
} from '@kit/ui/dropdown-menu';
import { DataTable } from '@kit/ui/enhanced-data-table';
+import { Form, FormControl, FormField, FormItem } from '@kit/ui/form';
+import { If } from '@kit/ui/if';
+import { Input } from '@kit/ui/input';
import {
Select,
SelectContent,
+ SelectGroup,
SelectItem,
+ SelectLabel,
SelectTrigger,
+ SelectValue,
} from '@kit/ui/select';
type Account = Database['public']['Tables']['accounts']['Row'];
const FiltersSchema = z.object({
type: z.enum(['all', 'team', 'personal']),
+ query: z.string().optional(),
});
-export function AccountsTable(
+export function AdminAccountsTable(
props: React.PropsWithChildren<{
data: Account[];
pageCount: number;
@@ -66,6 +73,7 @@ function AccountsTableFilters(props: {
resolver: zodResolver(FiltersSchema),
defaultValues: {
type: props.filters?.type ?? 'all',
+ query: '',
},
mode: 'onChange',
reValidateMode: 'onChange',
@@ -74,9 +82,10 @@ function AccountsTableFilters(props: {
const router = useRouter();
const pathName = usePathname();
- const onSubmit = ({ type }: z.infer
) => {
+ const onSubmit = ({ type, query }: z.infer) => {
const params = new URLSearchParams({
account_type: type,
+ query: query ?? '',
});
const url = `${pathName}?${params.toString()}`;
@@ -85,35 +94,59 @@ function AccountsTableFilters(props: {
};
return (
-