From 396acf3bbbe00a192cb0ea0a9ccf91b1d8d2850b Mon Sep 17 00:00:00 2001 From: Fuwn <50817549+Fuwn@users.noreply.github.com> Date: Sat, 24 Jan 2026 13:09:50 +0000 Subject: Initial commit Created from https://vercel.com/new --- src/app/(main)/admin/AdminLayout.tsx | 33 ++++++++ src/app/(main)/admin/AdminNav.tsx | 48 ++++++++++++ src/app/(main)/admin/layout.tsx | 17 +++++ src/app/(main)/admin/teams/AdminTeamsDataTable.tsx | 19 +++++ src/app/(main)/admin/teams/AdminTeamsPage.tsx | 19 +++++ src/app/(main)/admin/teams/AdminTeamsTable.tsx | 86 +++++++++++++++++++++ .../(main)/admin/teams/[teamId]/AdminTeamPage.tsx | 11 +++ src/app/(main)/admin/teams/[teamId]/page.tsx | 12 +++ src/app/(main)/admin/teams/page.tsx | 9 +++ src/app/(main)/admin/users/UserAddButton.tsx | 32 ++++++++ src/app/(main)/admin/users/UserAddForm.tsx | 71 +++++++++++++++++ src/app/(main)/admin/users/UserDeleteButton.tsx | 35 +++++++++ src/app/(main)/admin/users/UserDeleteForm.tsx | 41 ++++++++++ src/app/(main)/admin/users/UsersDataTable.tsx | 14 ++++ src/app/(main)/admin/users/UsersPage.tsx | 24 ++++++ src/app/(main)/admin/users/UsersTable.tsx | 84 ++++++++++++++++++++ .../(main)/admin/users/[userId]/UserEditForm.tsx | 73 ++++++++++++++++++ src/app/(main)/admin/users/[userId]/UserHeader.tsx | 9 +++ src/app/(main)/admin/users/[userId]/UserPage.tsx | 19 +++++ .../(main)/admin/users/[userId]/UserProvider.tsx | 20 +++++ .../(main)/admin/users/[userId]/UserSettings.tsx | 25 ++++++ .../(main)/admin/users/[userId]/UserWebsites.tsx | 15 ++++ src/app/(main)/admin/users/[userId]/page.tsx | 12 +++ src/app/(main)/admin/users/page.tsx | 9 +++ .../admin/websites/AdminWebsitesDataTable.tsx | 13 ++++ .../(main)/admin/websites/AdminWebsitesPage.tsx | 19 +++++ .../(main)/admin/websites/AdminWebsitesTable.tsx | 89 ++++++++++++++++++++++ .../websites/[websiteId]/AdminWebsitePage.tsx | 14 ++++ src/app/(main)/admin/websites/[websiteId]/page.tsx | 12 +++ src/app/(main)/admin/websites/page.tsx | 9 +++ 30 files changed, 893 insertions(+) create mode 100644 src/app/(main)/admin/AdminLayout.tsx create mode 100644 src/app/(main)/admin/AdminNav.tsx create mode 100644 src/app/(main)/admin/layout.tsx create mode 100644 src/app/(main)/admin/teams/AdminTeamsDataTable.tsx create mode 100644 src/app/(main)/admin/teams/AdminTeamsPage.tsx create mode 100644 src/app/(main)/admin/teams/AdminTeamsTable.tsx create mode 100644 src/app/(main)/admin/teams/[teamId]/AdminTeamPage.tsx create mode 100644 src/app/(main)/admin/teams/[teamId]/page.tsx create mode 100644 src/app/(main)/admin/teams/page.tsx create mode 100644 src/app/(main)/admin/users/UserAddButton.tsx create mode 100644 src/app/(main)/admin/users/UserAddForm.tsx create mode 100644 src/app/(main)/admin/users/UserDeleteButton.tsx create mode 100644 src/app/(main)/admin/users/UserDeleteForm.tsx create mode 100644 src/app/(main)/admin/users/UsersDataTable.tsx create mode 100644 src/app/(main)/admin/users/UsersPage.tsx create mode 100644 src/app/(main)/admin/users/UsersTable.tsx create mode 100644 src/app/(main)/admin/users/[userId]/UserEditForm.tsx create mode 100644 src/app/(main)/admin/users/[userId]/UserHeader.tsx create mode 100644 src/app/(main)/admin/users/[userId]/UserPage.tsx create mode 100644 src/app/(main)/admin/users/[userId]/UserProvider.tsx create mode 100644 src/app/(main)/admin/users/[userId]/UserSettings.tsx create mode 100644 src/app/(main)/admin/users/[userId]/UserWebsites.tsx create mode 100644 src/app/(main)/admin/users/[userId]/page.tsx create mode 100644 src/app/(main)/admin/users/page.tsx create mode 100644 src/app/(main)/admin/websites/AdminWebsitesDataTable.tsx create mode 100644 src/app/(main)/admin/websites/AdminWebsitesPage.tsx create mode 100644 src/app/(main)/admin/websites/AdminWebsitesTable.tsx create mode 100644 src/app/(main)/admin/websites/[websiteId]/AdminWebsitePage.tsx create mode 100644 src/app/(main)/admin/websites/[websiteId]/page.tsx create mode 100644 src/app/(main)/admin/websites/page.tsx (limited to 'src/app/(main)/admin') diff --git a/src/app/(main)/admin/AdminLayout.tsx b/src/app/(main)/admin/AdminLayout.tsx new file mode 100644 index 0000000..3c8fa20 --- /dev/null +++ b/src/app/(main)/admin/AdminLayout.tsx @@ -0,0 +1,33 @@ +'use client'; +import { Column, Grid } from '@umami/react-zen'; +import type { ReactNode } from 'react'; +import { PageBody } from '@/components/common/PageBody'; +import { useLoginQuery } from '@/components/hooks'; +import { AdminNav } from './AdminNav'; + +export function AdminLayout({ children }: { children: ReactNode }) { + const { user } = useLoginQuery(); + + if (!user.isAdmin || process.env.cloudMode) { + return null; + } + + return ( + + + + + + {children} + + + ); +} diff --git a/src/app/(main)/admin/AdminNav.tsx b/src/app/(main)/admin/AdminNav.tsx new file mode 100644 index 0000000..20c0115 --- /dev/null +++ b/src/app/(main)/admin/AdminNav.tsx @@ -0,0 +1,48 @@ +import { SideMenu } from '@/components/common/SideMenu'; +import { useMessages, useNavigation } from '@/components/hooks'; +import { Globe, User, Users } from '@/components/icons'; + +export function AdminNav({ onItemClick }: { onItemClick?: () => void }) { + const { formatMessage, labels } = useMessages(); + const { pathname } = useNavigation(); + + const items = [ + { + label: formatMessage(labels.manage), + items: [ + { + id: 'users', + label: formatMessage(labels.users), + path: '/admin/users', + icon: , + }, + { + id: 'websites', + label: formatMessage(labels.websites), + path: '/admin/websites', + icon: , + }, + { + id: 'teams', + label: formatMessage(labels.teams), + path: '/admin/teams', + icon: , + }, + ], + }, + ]; + + const selectedKey = items + .flatMap(e => e.items) + ?.find(({ path }) => path && pathname.startsWith(path))?.id; + + return ( + + ); +} diff --git a/src/app/(main)/admin/layout.tsx b/src/app/(main)/admin/layout.tsx new file mode 100644 index 0000000..34cdd0b --- /dev/null +++ b/src/app/(main)/admin/layout.tsx @@ -0,0 +1,17 @@ +import type { Metadata } from 'next'; +import { AdminLayout } from './AdminLayout'; + +export default function ({ children }) { + if (process.env.cloudMode) { + return null; + } + + return {children}; +} + +export const metadata: Metadata = { + title: { + template: '%s | Admin | Umami', + default: 'Admin | Umami', + }, +}; diff --git a/src/app/(main)/admin/teams/AdminTeamsDataTable.tsx b/src/app/(main)/admin/teams/AdminTeamsDataTable.tsx new file mode 100644 index 0000000..7da8531 --- /dev/null +++ b/src/app/(main)/admin/teams/AdminTeamsDataTable.tsx @@ -0,0 +1,19 @@ +import type { ReactNode } from 'react'; +import { DataGrid } from '@/components/common/DataGrid'; +import { useTeamsQuery } from '@/components/hooks'; +import { AdminTeamsTable } from './AdminTeamsTable'; + +export function AdminTeamsDataTable({ + showActions, +}: { + showActions?: boolean; + children?: ReactNode; +}) { + const queryResult = useTeamsQuery(); + + return ( + + {({ data }) => } + + ); +} diff --git a/src/app/(main)/admin/teams/AdminTeamsPage.tsx b/src/app/(main)/admin/teams/AdminTeamsPage.tsx new file mode 100644 index 0000000..41e6f4a --- /dev/null +++ b/src/app/(main)/admin/teams/AdminTeamsPage.tsx @@ -0,0 +1,19 @@ +'use client'; +import { Column } from '@umami/react-zen'; +import { PageHeader } from '@/components/common/PageHeader'; +import { Panel } from '@/components/common/Panel'; +import { useMessages } from '@/components/hooks'; +import { AdminTeamsDataTable } from './AdminTeamsDataTable'; + +export function AdminTeamsPage() { + const { formatMessage, labels } = useMessages(); + + return ( + + + + + + + ); +} diff --git a/src/app/(main)/admin/teams/AdminTeamsTable.tsx b/src/app/(main)/admin/teams/AdminTeamsTable.tsx new file mode 100644 index 0000000..9f2abd5 --- /dev/null +++ b/src/app/(main)/admin/teams/AdminTeamsTable.tsx @@ -0,0 +1,86 @@ +import { DataColumn, DataTable, Dialog, Icon, MenuItem, Modal, Row, Text } from '@umami/react-zen'; +import Link from 'next/link'; +import { useState } from 'react'; +import { DateDistance } from '@/components/common/DateDistance'; +import { useMessages } from '@/components/hooks'; +import { Edit, Trash } from '@/components/icons'; +import { MenuButton } from '@/components/input/MenuButton'; +import { TeamDeleteForm } from '../../teams/[teamId]/TeamDeleteForm'; + +export function AdminTeamsTable({ + data = [], + showActions = true, +}: { + data: any[]; + showActions?: boolean; +}) { + const { formatMessage, labels } = useMessages(); + const [deleteTeam, setDeleteTeam] = useState(null); + + return ( + <> + + + {(row: any) => {row.name}} + + + {(row: any) => row?._count?.members} + + + {(row: any) => row?._count?.websites} + + + {(row: any) => { + const name = row?.members?.[0]?.user?.username; + + return ( + + {name} + + ); + }} + + + {(row: any) => } + + {showActions && ( + + {(row: any) => { + const { id } = row; + + return ( + + + + + + + {formatMessage(labels.edit)} + + + setDeleteTeam(id)} + data-test="link-button-delete" + > + + + + + {formatMessage(labels.delete)} + + + + ); + }} + + )} + + + + setDeleteTeam(null)} /> + + + + ); +} diff --git a/src/app/(main)/admin/teams/[teamId]/AdminTeamPage.tsx b/src/app/(main)/admin/teams/[teamId]/AdminTeamPage.tsx new file mode 100644 index 0000000..2150197 --- /dev/null +++ b/src/app/(main)/admin/teams/[teamId]/AdminTeamPage.tsx @@ -0,0 +1,11 @@ +'use client'; +import { TeamSettings } from '@/app/(main)/teams/[teamId]/TeamSettings'; +import { TeamProvider } from '@/app/(main)/teams/TeamProvider'; + +export function AdminTeamPage({ teamId }: { teamId: string }) { + return ( + + + + ); +} diff --git a/src/app/(main)/admin/teams/[teamId]/page.tsx b/src/app/(main)/admin/teams/[teamId]/page.tsx new file mode 100644 index 0000000..104766a --- /dev/null +++ b/src/app/(main)/admin/teams/[teamId]/page.tsx @@ -0,0 +1,12 @@ +import type { Metadata } from 'next'; +import { AdminTeamPage } from './AdminTeamPage'; + +export default async function ({ params }: { params: Promise<{ teamId: string }> }) { + const { teamId } = await params; + + return ; +} + +export const metadata: Metadata = { + title: 'Team', +}; diff --git a/src/app/(main)/admin/teams/page.tsx b/src/app/(main)/admin/teams/page.tsx new file mode 100644 index 0000000..987f02b --- /dev/null +++ b/src/app/(main)/admin/teams/page.tsx @@ -0,0 +1,9 @@ +import type { Metadata } from 'next'; +import { AdminTeamsPage } from './AdminTeamsPage'; + +export default function () { + return ; +} +export const metadata: Metadata = { + title: 'Teams', +}; diff --git a/src/app/(main)/admin/users/UserAddButton.tsx b/src/app/(main)/admin/users/UserAddButton.tsx new file mode 100644 index 0000000..0525082 --- /dev/null +++ b/src/app/(main)/admin/users/UserAddButton.tsx @@ -0,0 +1,32 @@ +import { Button, Dialog, DialogTrigger, Icon, Modal, Text, useToast } from '@umami/react-zen'; +import { useMessages, useModified } from '@/components/hooks'; +import { Plus } from '@/components/icons'; +import { UserAddForm } from './UserAddForm'; + +export function UserAddButton({ onSave }: { onSave?: () => void }) { + const { formatMessage, labels, messages } = useMessages(); + const { toast } = useToast(); + const { touch } = useModified(); + + const handleSave = () => { + toast(formatMessage(messages.saved)); + touch('users'); + onSave?.(); + }; + + return ( + + + + + {({ close }) => } + + + + ); +} diff --git a/src/app/(main)/admin/users/UserAddForm.tsx b/src/app/(main)/admin/users/UserAddForm.tsx new file mode 100644 index 0000000..6c36551 --- /dev/null +++ b/src/app/(main)/admin/users/UserAddForm.tsx @@ -0,0 +1,71 @@ +import { + Button, + Form, + FormButtons, + FormField, + FormSubmitButton, + ListItem, + PasswordField, + Select, + TextField, +} from '@umami/react-zen'; +import { useMessages, useUpdateQuery } from '@/components/hooks'; +import { ROLES } from '@/lib/constants'; + +export function UserAddForm({ onSave, onClose }) { + const { mutateAsync, error, isPending } = useUpdateQuery(`/users`); + const { formatMessage, labels, getErrorMessage } = useMessages(); + + const handleSubmit = async (data: any) => { + await mutateAsync(data, { + onSuccess: async () => { + onSave(data); + onClose(); + }, + }); + }; + + return ( +
+ + + + + + + + + + + + + {formatMessage(labels.save)} + + +
+ ); +} diff --git a/src/app/(main)/admin/users/UserDeleteButton.tsx b/src/app/(main)/admin/users/UserDeleteButton.tsx new file mode 100644 index 0000000..ee8f2c1 --- /dev/null +++ b/src/app/(main)/admin/users/UserDeleteButton.tsx @@ -0,0 +1,35 @@ +import { Button, Dialog, DialogTrigger, Icon, Modal, Text } from '@umami/react-zen'; +import { useLoginQuery, useMessages } from '@/components/hooks'; +import { Trash } from '@/components/icons'; +import { UserDeleteForm } from './UserDeleteForm'; + +export function UserDeleteButton({ + userId, + username, + onDelete, +}: { + userId: string; + username: string; + onDelete?: () => void; +}) { + const { formatMessage, labels } = useMessages(); + const { user } = useLoginQuery(); + + return ( + + + + + {({ close }) => ( + + )} + + + + ); +} diff --git a/src/app/(main)/admin/users/UserDeleteForm.tsx b/src/app/(main)/admin/users/UserDeleteForm.tsx new file mode 100644 index 0000000..8f6fd50 --- /dev/null +++ b/src/app/(main)/admin/users/UserDeleteForm.tsx @@ -0,0 +1,41 @@ +import { AlertDialog, Row } from '@umami/react-zen'; +import { useDeleteQuery, useMessages, useModified } from '@/components/hooks'; + +export function UserDeleteForm({ + userId, + username, + onSave, + onClose, +}: { + userId: string; + username: string; + onSave?: () => void; + onClose?: () => void; +}) { + const { messages, labels, formatMessage } = useMessages(); + const { mutateAsync } = useDeleteQuery(`/users/${userId}`); + const { touch } = useModified(); + + const handleConfirm = async () => { + await mutateAsync(null, { + onSuccess: async () => { + touch('users'); + touch(`users:${userId}`); + onSave?.(); + onClose?.(); + }, + }); + }; + + return ( + + {formatMessage(messages.confirmDelete, { target: username })} + + ); +} diff --git a/src/app/(main)/admin/users/UsersDataTable.tsx b/src/app/(main)/admin/users/UsersDataTable.tsx new file mode 100644 index 0000000..8467bd2 --- /dev/null +++ b/src/app/(main)/admin/users/UsersDataTable.tsx @@ -0,0 +1,14 @@ +import type { ReactNode } from 'react'; +import { DataGrid } from '@/components/common/DataGrid'; +import { useUsersQuery } from '@/components/hooks'; +import { UsersTable } from './UsersTable'; + +export function UsersDataTable({ showActions }: { showActions?: boolean; children?: ReactNode }) { + const queryResult = useUsersQuery(); + + return ( + + {({ data }) => } + + ); +} diff --git a/src/app/(main)/admin/users/UsersPage.tsx b/src/app/(main)/admin/users/UsersPage.tsx new file mode 100644 index 0000000..7e1b0f4 --- /dev/null +++ b/src/app/(main)/admin/users/UsersPage.tsx @@ -0,0 +1,24 @@ +'use client'; +import { Column } from '@umami/react-zen'; +import { PageHeader } from '@/components/common/PageHeader'; +import { Panel } from '@/components/common/Panel'; +import { useMessages } from '@/components/hooks'; +import { UserAddButton } from './UserAddButton'; +import { UsersDataTable } from './UsersDataTable'; + +export function UsersPage() { + const { formatMessage, labels } = useMessages(); + + const handleSave = () => {}; + + return ( + + + + + + + + + ); +} diff --git a/src/app/(main)/admin/users/UsersTable.tsx b/src/app/(main)/admin/users/UsersTable.tsx new file mode 100644 index 0000000..9c10f3e --- /dev/null +++ b/src/app/(main)/admin/users/UsersTable.tsx @@ -0,0 +1,84 @@ +import { DataColumn, DataTable, Icon, MenuItem, Modal, Row, Text } from '@umami/react-zen'; +import Link from 'next/link'; +import { useState } from 'react'; +import { DateDistance } from '@/components/common/DateDistance'; +import { useMessages } from '@/components/hooks'; +import { Edit, Trash } from '@/components/icons'; +import { MenuButton } from '@/components/input/MenuButton'; +import { ROLES } from '@/lib/constants'; +import { UserDeleteForm } from './UserDeleteForm'; + +export function UsersTable({ + data = [], + showActions = true, +}: { + data: any[]; + showActions?: boolean; +}) { + const { formatMessage, labels } = useMessages(); + const [deleteUser, setDeleteUser] = useState(null); + + return ( + <> + + + {(row: any) => {row.username}} + + + {(row: any) => + formatMessage( + labels[Object.keys(ROLES).find(key => ROLES[key] === row.role)] || labels.unknown, + ) + } + + + {(row: any) => row._count.websites} + + + {(row: any) => } + + {showActions && ( + + {(row: any) => { + const { id } = row; + + return ( + + + + + + + {formatMessage(labels.edit)} + + + setDeleteUser(row)} + data-test="link-button-delete" + > + + + + + {formatMessage(labels.delete)} + + + + ); + }} + + )} + + + { + setDeleteUser(null); + }} + /> + + + ); +} diff --git a/src/app/(main)/admin/users/[userId]/UserEditForm.tsx b/src/app/(main)/admin/users/[userId]/UserEditForm.tsx new file mode 100644 index 0000000..28bf030 --- /dev/null +++ b/src/app/(main)/admin/users/[userId]/UserEditForm.tsx @@ -0,0 +1,73 @@ +import { + Form, + FormButtons, + FormField, + FormSubmitButton, + ListItem, + PasswordField, + Select, + TextField, +} from '@umami/react-zen'; +import { useLoginQuery, useMessages, useUpdateQuery, useUser } from '@/components/hooks'; +import { ROLES } from '@/lib/constants'; + +export function UserEditForm({ userId, onSave }: { userId: string; onSave?: () => void }) { + const { formatMessage, labels, messages, getMessage } = useMessages(); + const user = useUser(); + const { user: login } = useLoginQuery(); + + const { mutateAsync, error, toast, touch } = useUpdateQuery(`/users/${userId}`); + + const handleSubmit = async (data: any) => { + await mutateAsync(data, { + onSuccess: async () => { + toast(formatMessage(messages.saved)); + touch('users'); + touch(`user:${user.id}`); + onSave?.(); + }, + }); + }; + + return ( +
+ + + + + + + + {user.id !== login.id && ( + + + + )} + + + {formatMessage(labels.save)} + + +
+ ); +} diff --git a/src/app/(main)/admin/users/[userId]/UserHeader.tsx b/src/app/(main)/admin/users/[userId]/UserHeader.tsx new file mode 100644 index 0000000..1f82897 --- /dev/null +++ b/src/app/(main)/admin/users/[userId]/UserHeader.tsx @@ -0,0 +1,9 @@ +import { PageHeader } from '@/components/common/PageHeader'; +import { useUser } from '@/components/hooks'; +import { User } from '@/components/icons'; + +export function UserHeader() { + const user = useUser(); + + return } />; +} diff --git a/src/app/(main)/admin/users/[userId]/UserPage.tsx b/src/app/(main)/admin/users/[userId]/UserPage.tsx new file mode 100644 index 0000000..5e0f8d1 --- /dev/null +++ b/src/app/(main)/admin/users/[userId]/UserPage.tsx @@ -0,0 +1,19 @@ +'use client'; +import { Column } from '@umami/react-zen'; +import { UserHeader } from '@/app/(main)/admin/users/[userId]/UserHeader'; +import { Panel } from '@/components/common/Panel'; +import { UserProvider } from './UserProvider'; +import { UserSettings } from './UserSettings'; + +export function UserPage({ userId }: { userId: string }) { + return ( + + + + + + + + + ); +} diff --git a/src/app/(main)/admin/users/[userId]/UserProvider.tsx b/src/app/(main)/admin/users/[userId]/UserProvider.tsx new file mode 100644 index 0000000..ea01915 --- /dev/null +++ b/src/app/(main)/admin/users/[userId]/UserProvider.tsx @@ -0,0 +1,20 @@ +import { Loading } from '@umami/react-zen'; +import { createContext, type ReactNode } from 'react'; +import { useUserQuery } from '@/components/hooks/queries/useUserQuery'; +import type { User } from '@/generated/prisma/client'; + +export const UserContext = createContext(null); + +export function UserProvider({ userId, children }: { userId: string; children: ReactNode }) { + const { data: user, isFetching, isLoading } = useUserQuery(userId); + + if (isFetching && isLoading) { + return ; + } + + if (!user) { + return null; + } + + return {children}; +} diff --git a/src/app/(main)/admin/users/[userId]/UserSettings.tsx b/src/app/(main)/admin/users/[userId]/UserSettings.tsx new file mode 100644 index 0000000..3f17f3e --- /dev/null +++ b/src/app/(main)/admin/users/[userId]/UserSettings.tsx @@ -0,0 +1,25 @@ +import { Column, Tab, TabList, TabPanel, Tabs } from '@umami/react-zen'; +import { useMessages } from '@/components/hooks'; +import { UserEditForm } from './UserEditForm'; +import { UserWebsites } from './UserWebsites'; + +export function UserSettings({ userId }: { userId: string }) { + const { formatMessage, labels } = useMessages(); + + return ( + + + + {formatMessage(labels.details)} + {formatMessage(labels.websites)} + + + + + + + + + + ); +} diff --git a/src/app/(main)/admin/users/[userId]/UserWebsites.tsx b/src/app/(main)/admin/users/[userId]/UserWebsites.tsx new file mode 100644 index 0000000..eeb173e --- /dev/null +++ b/src/app/(main)/admin/users/[userId]/UserWebsites.tsx @@ -0,0 +1,15 @@ +import { WebsitesTable } from '@/app/(main)/websites/WebsitesTable'; +import { DataGrid } from '@/components/common/DataGrid'; +import { useUserWebsitesQuery } from '@/components/hooks'; + +export function UserWebsites({ userId }) { + const queryResult = useUserWebsitesQuery({ userId }); + + return ( + + {({ data }) => ( + + )} + + ); +} diff --git a/src/app/(main)/admin/users/[userId]/page.tsx b/src/app/(main)/admin/users/[userId]/page.tsx new file mode 100644 index 0000000..16c9f36 --- /dev/null +++ b/src/app/(main)/admin/users/[userId]/page.tsx @@ -0,0 +1,12 @@ +import type { Metadata } from 'next'; +import { UserPage } from './UserPage'; + +export default async function ({ params }: { params: Promise<{ userId: string }> }) { + const { userId } = await params; + + return ; +} + +export const metadata: Metadata = { + title: 'User', +}; diff --git a/src/app/(main)/admin/users/page.tsx b/src/app/(main)/admin/users/page.tsx new file mode 100644 index 0000000..96e69eb --- /dev/null +++ b/src/app/(main)/admin/users/page.tsx @@ -0,0 +1,9 @@ +import type { Metadata } from 'next'; +import { UsersPage } from './UsersPage'; + +export default function () { + return ; +} +export const metadata: Metadata = { + title: 'Users', +}; diff --git a/src/app/(main)/admin/websites/AdminWebsitesDataTable.tsx b/src/app/(main)/admin/websites/AdminWebsitesDataTable.tsx new file mode 100644 index 0000000..2105992 --- /dev/null +++ b/src/app/(main)/admin/websites/AdminWebsitesDataTable.tsx @@ -0,0 +1,13 @@ +import { DataGrid } from '@/components/common/DataGrid'; +import { useWebsitesQuery } from '@/components/hooks'; +import { AdminWebsitesTable } from './AdminWebsitesTable'; + +export function AdminWebsitesDataTable() { + const query = useWebsitesQuery(); + + return ( + + {props => } + + ); +} diff --git a/src/app/(main)/admin/websites/AdminWebsitesPage.tsx b/src/app/(main)/admin/websites/AdminWebsitesPage.tsx new file mode 100644 index 0000000..1c2ac92 --- /dev/null +++ b/src/app/(main)/admin/websites/AdminWebsitesPage.tsx @@ -0,0 +1,19 @@ +'use client'; +import { Column } from '@umami/react-zen'; +import { PageHeader } from '@/components/common/PageHeader'; +import { Panel } from '@/components/common/Panel'; +import { useMessages } from '@/components/hooks'; +import { AdminWebsitesDataTable } from './AdminWebsitesDataTable'; + +export function AdminWebsitesPage() { + const { formatMessage, labels } = useMessages(); + + return ( + + + + + + + ); +} diff --git a/src/app/(main)/admin/websites/AdminWebsitesTable.tsx b/src/app/(main)/admin/websites/AdminWebsitesTable.tsx new file mode 100644 index 0000000..cfda595 --- /dev/null +++ b/src/app/(main)/admin/websites/AdminWebsitesTable.tsx @@ -0,0 +1,89 @@ +import { DataColumn, DataTable, Dialog, Icon, MenuItem, Modal, Row, Text } from '@umami/react-zen'; +import Link from 'next/link'; +import { useState } from 'react'; +import { WebsiteDeleteForm } from '@/app/(main)/websites/[websiteId]/settings/WebsiteDeleteForm'; +import { DateDistance } from '@/components/common/DateDistance'; +import { useMessages } from '@/components/hooks'; +import { Edit, Trash, Users } from '@/components/icons'; +import { MenuButton } from '@/components/input/MenuButton'; + +export function AdminWebsitesTable({ data = [] }: { data: any[] }) { + const { formatMessage, labels } = useMessages(); + const [deleteWebsite, setDeleteWebsite] = useState(null); + + return ( + <> + + + {(row: any) => ( + + {row.name} + + )} + + + {(row: any) => {row.domain}} + + + {(row: any) => { + if (row?.team) { + return ( + + + + + + {row?.team?.name} + + + ); + } + return ( + + {row?.user?.username} + + ); + }} + + + {(row: any) => } + + + {(row: any) => { + const { id } = row; + + return ( + + + + + + + {formatMessage(labels.edit)} + + + setDeleteWebsite(id)} + data-test="link-button-delete" + > + + + + + {formatMessage(labels.delete)} + + + + ); + }} + + + + + setDeleteWebsite(null)} /> + + + + ); +} diff --git a/src/app/(main)/admin/websites/[websiteId]/AdminWebsitePage.tsx b/src/app/(main)/admin/websites/[websiteId]/AdminWebsitePage.tsx new file mode 100644 index 0000000..5da82af --- /dev/null +++ b/src/app/(main)/admin/websites/[websiteId]/AdminWebsitePage.tsx @@ -0,0 +1,14 @@ +'use client'; +import { WebsiteSettings } from '@/app/(main)/websites/[websiteId]/settings/WebsiteSettings'; +import { WebsiteProvider } from '@/app/(main)/websites/WebsiteProvider'; +import { Panel } from '@/components/common/Panel'; + +export function AdminWebsitePage({ websiteId }: { websiteId: string }) { + return ( + + + + + + ); +} diff --git a/src/app/(main)/admin/websites/[websiteId]/page.tsx b/src/app/(main)/admin/websites/[websiteId]/page.tsx new file mode 100644 index 0000000..557adbd --- /dev/null +++ b/src/app/(main)/admin/websites/[websiteId]/page.tsx @@ -0,0 +1,12 @@ +import type { Metadata } from 'next'; +import { WebsiteSettingsPage } from '@/app/(main)/settings/websites/[websiteId]/WebsiteSettingsPage'; + +export default async function ({ params }: { params: Promise<{ websiteId: string }> }) { + const { websiteId } = await params; + + return ; +} + +export const metadata: Metadata = { + title: 'Website', +}; diff --git a/src/app/(main)/admin/websites/page.tsx b/src/app/(main)/admin/websites/page.tsx new file mode 100644 index 0000000..d6da9f6 --- /dev/null +++ b/src/app/(main)/admin/websites/page.tsx @@ -0,0 +1,9 @@ +import type { Metadata } from 'next'; +import { AdminWebsitesPage } from './AdminWebsitesPage'; + +export default function () { + return ; +} +export const metadata: Metadata = { + title: 'Websites', +}; -- cgit v1.2.3