diff --git a/src/app/(main)/links/LinkEditButton.tsx b/src/app/(main)/links/LinkEditButton.tsx
index bd309282f..8969ad1bd 100644
--- a/src/app/(main)/links/LinkEditButton.tsx
+++ b/src/app/(main)/links/LinkEditButton.tsx
@@ -1,16 +1,15 @@
+import { LinkButton } from '@/components/common/LinkButton';
+import { useNavigation } from '@/components/hooks';
import { useMessages } from '@/components/hooks';
import { Edit } from '@/components/icons';
-import { DialogButton } from '@/components/input/DialogButton';
-import { LinkEditForm } from './LinkEditForm';
export function LinkEditButton({ linkId }: { linkId: string }) {
const { t, labels } = useMessages();
+ const { renderUrl } = useNavigation();
return (
- } title={t(labels.link)} variant="quiet" width="800px">
- {({ close }) => {
- return ;
- }}
-
+
+
+
);
}
diff --git a/src/app/(main)/links/LinkEditForm.tsx b/src/app/(main)/links/LinkEditForm.tsx
index 50f3bdb60..4f8dcf7ae 100644
--- a/src/app/(main)/links/LinkEditForm.tsx
+++ b/src/app/(main)/links/LinkEditForm.tsx
@@ -40,7 +40,8 @@ export function LinkEditForm({
teamId,
},
);
- const { linksUrl } = useConfig();
+ const config = useConfig();
+ const linksUrl = config?.linksUrl;
const hostUrl = linksUrl || LINKS_URL;
const { data, isLoading } = useLinkQuery(linkId);
const [defaultSlug] = useState(generateId());
diff --git a/src/app/(main)/links/[linkId]/LinkHeader.tsx b/src/app/(main)/links/[linkId]/LinkHeader.tsx
index 9765dea76..ee145963d 100644
--- a/src/app/(main)/links/[linkId]/LinkHeader.tsx
+++ b/src/app/(main)/links/[linkId]/LinkHeader.tsx
@@ -2,9 +2,8 @@ import { Row } from '@umami/react-zen';
import { IconLabel } from '@/components/common/IconLabel';
import { LinkButton } from '@/components/common/LinkButton';
import { PageHeader } from '@/components/common/PageHeader';
-import { useLink, useMessages, useSlug } from '@/components/hooks';
-import { ExternalLink, Link } from '@/components/icons';
-import { LinkShareButton } from './LinkShareButton';
+import { useLink, useMessages, useNavigation, useSlug } from '@/components/hooks';
+import { Edit, ExternalLink, Link } from '@/components/icons';
export function LinkHeader({ showActions = true }: { showActions?: boolean }) {
const link = useLink();
@@ -18,11 +17,14 @@ export function LinkHeader({ showActions = true }: { showActions?: boolean }) {
function LinkHeaderActions({ linkId, slug }: { linkId: string; slug: string }) {
const { t, labels } = useMessages();
+ const { renderUrl } = useNavigation();
const { getSlugUrl } = useSlug('link');
return (
-
+
+ } label={t(labels.edit)} />
+
} label={t(labels.view)} />
diff --git a/src/app/(main)/links/[linkId]/LinkShareButton.tsx b/src/app/(main)/links/[linkId]/LinkShareButton.tsx
deleted file mode 100644
index 4c3084ed5..000000000
--- a/src/app/(main)/links/[linkId]/LinkShareButton.tsx
+++ /dev/null
@@ -1,14 +0,0 @@
-import { useMessages } from '@/components/hooks';
-import { Share } from '@/components/icons';
-import { DialogButton } from '@/components/input/DialogButton';
-import { LinkShareDialog } from './LinkShareDialog';
-
-export function LinkShareButton({ linkId }: { linkId: string }) {
- const { t, labels } = useMessages();
-
- return (
- } label={t(labels.share)} title={null} width="900px">
-
-
- );
-}
diff --git a/src/app/(main)/links/[linkId]/LinkShareDialog.tsx b/src/app/(main)/links/[linkId]/LinkShareForm.tsx
similarity index 90%
rename from src/app/(main)/links/[linkId]/LinkShareDialog.tsx
rename to src/app/(main)/links/[linkId]/LinkShareForm.tsx
index 3bf55ebf5..6e6c4f8dc 100644
--- a/src/app/(main)/links/[linkId]/LinkShareDialog.tsx
+++ b/src/app/(main)/links/[linkId]/LinkShareForm.tsx
@@ -7,19 +7,19 @@ import { Plus } from '@/components/icons';
import { SimpleShareCreateForm } from '@/components/share/SimpleShareCreateForm';
import { SimpleSharesTable } from '@/components/share/SimpleSharesTable';
-export function LinkShareDialog({ linkId }: { linkId: string }) {
+export function LinkShareForm({ linkId }: { linkId: string }) {
const { data, error, isLoading } = useLinkSharesQuery({ linkId });
const shares = data?.data || [];
const hasShares = shares.length > 0;
return (
-
+
);
}
-function LinkShareDialogContent({
+function LinkShareFormContent({
linkId,
hasShares,
shares,
diff --git a/src/app/(main)/links/[linkId]/edit/LinkEditPage.tsx b/src/app/(main)/links/[linkId]/edit/LinkEditPage.tsx
new file mode 100644
index 000000000..356873ceb
--- /dev/null
+++ b/src/app/(main)/links/[linkId]/edit/LinkEditPage.tsx
@@ -0,0 +1,46 @@
+'use client';
+import { Column } from '@umami/react-zen';
+import Link from 'next/link';
+import { LinkEditForm } from '@/app/(main)/links/LinkEditForm';
+import { LinkProvider } from '@/app/(main)/links/LinkProvider';
+import { LinkShareForm } from '@/app/(main)/links/[linkId]/LinkShareForm';
+import { Panel } from '@/components/common/Panel';
+import { IconLabel } from '@/components/common/IconLabel';
+import { PageHeader } from '@/components/common/PageHeader';
+import { useLink, useMessages, useNavigation } from '@/components/hooks';
+import { ArrowLeft, Link as LinkIcon } from '@/components/icons';
+
+export function LinkEditPage({ linkId }: { linkId: string }) {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
+function LinkEditHeader() {
+ const link = useLink();
+ const { t, labels } = useMessages();
+ const { renderUrl } = useNavigation();
+
+ return (
+ <>
+
+
+ } label={t(labels.link)} />
+
+
+ } />
+ >
+ );
+}
diff --git a/src/app/(main)/links/[linkId]/edit/page.tsx b/src/app/(main)/links/[linkId]/edit/page.tsx
new file mode 100644
index 000000000..1720b696e
--- /dev/null
+++ b/src/app/(main)/links/[linkId]/edit/page.tsx
@@ -0,0 +1,18 @@
+import type { Metadata } from 'next';
+import { getLink } from '@/queries/prisma';
+import { LinkEditPage } from './LinkEditPage';
+
+export default async function ({ params }: { params: Promise<{ linkId: string }> }) {
+ const { linkId } = await params;
+ const link = await getLink(linkId);
+
+ if (!link || link.deletedAt) {
+ return null;
+ }
+
+ return ;
+}
+
+export const metadata: Metadata = {
+ title: 'Edit Link',
+};
diff --git a/src/app/(main)/pixels/PixelEditButton.tsx b/src/app/(main)/pixels/PixelEditButton.tsx
index 2a16118c2..878387dc3 100644
--- a/src/app/(main)/pixels/PixelEditButton.tsx
+++ b/src/app/(main)/pixels/PixelEditButton.tsx
@@ -1,16 +1,15 @@
+import { LinkButton } from '@/components/common/LinkButton';
+import { useNavigation } from '@/components/hooks';
import { useMessages } from '@/components/hooks';
import { Edit } from '@/components/icons';
-import { DialogButton } from '@/components/input/DialogButton';
-import { PixelEditForm } from './PixelEditForm';
export function PixelEditButton({ pixelId }: { pixelId: string }) {
const { t, labels } = useMessages();
+ const { renderUrl } = useNavigation();
return (
- } title={t(labels.addPixel)} variant="quiet" width="600px">
- {({ close }) => {
- return ;
- }}
-
+
+
+
);
}
diff --git a/src/app/(main)/pixels/PixelEditForm.tsx b/src/app/(main)/pixels/PixelEditForm.tsx
index 54c5e64c5..aa3061d55 100644
--- a/src/app/(main)/pixels/PixelEditForm.tsx
+++ b/src/app/(main)/pixels/PixelEditForm.tsx
@@ -38,7 +38,8 @@ export function PixelEditForm({
teamId,
},
);
- const { pixelsUrl } = useConfig();
+ const config = useConfig();
+ const pixelsUrl = config?.pixelsUrl;
const hostUrl = pixelsUrl || PIXELS_URL;
const { data, isLoading } = usePixelQuery(pixelId);
const [slug, setSlug] = useState(generateId());
diff --git a/src/app/(main)/pixels/[pixelId]/PixelHeader.tsx b/src/app/(main)/pixels/[pixelId]/PixelHeader.tsx
index 89da44483..5cda3e5da 100644
--- a/src/app/(main)/pixels/[pixelId]/PixelHeader.tsx
+++ b/src/app/(main)/pixels/[pixelId]/PixelHeader.tsx
@@ -2,9 +2,8 @@ import { Row } from '@umami/react-zen';
import { IconLabel } from '@/components/common/IconLabel';
import { LinkButton } from '@/components/common/LinkButton';
import { PageHeader } from '@/components/common/PageHeader';
-import { useMessages, usePixel, useSlug } from '@/components/hooks';
-import { ExternalLink, Grid2x2 } from '@/components/icons';
-import { PixelShareButton } from './PixelShareButton';
+import { useMessages, useNavigation, usePixel, useSlug } from '@/components/hooks';
+import { Edit, ExternalLink, Grid2x2 } from '@/components/icons';
export function PixelHeader({ showActions = true }: { showActions?: boolean }) {
const pixel = usePixel();
@@ -18,11 +17,14 @@ export function PixelHeader({ showActions = true }: { showActions?: boolean }) {
function PixelHeaderActions({ pixelId, slug }: { pixelId: string; slug: string }) {
const { t, labels } = useMessages();
+ const { renderUrl } = useNavigation();
const { getSlugUrl } = useSlug('pixel');
return (
-
+
+ } label={t(labels.edit)} />
+
} label={t(labels.view)} />
diff --git a/src/app/(main)/pixels/[pixelId]/PixelShareButton.tsx b/src/app/(main)/pixels/[pixelId]/PixelShareButton.tsx
deleted file mode 100644
index 69ffd0adf..000000000
--- a/src/app/(main)/pixels/[pixelId]/PixelShareButton.tsx
+++ /dev/null
@@ -1,14 +0,0 @@
-import { useMessages } from '@/components/hooks';
-import { Share } from '@/components/icons';
-import { DialogButton } from '@/components/input/DialogButton';
-import { PixelShareDialog } from './PixelShareDialog';
-
-export function PixelShareButton({ pixelId }: { pixelId: string }) {
- const { t, labels } = useMessages();
-
- return (
- } label={t(labels.share)} title={null} width="900px">
-
-
- );
-}
diff --git a/src/app/(main)/pixels/[pixelId]/PixelShareDialog.tsx b/src/app/(main)/pixels/[pixelId]/PixelShareForm.tsx
similarity index 90%
rename from src/app/(main)/pixels/[pixelId]/PixelShareDialog.tsx
rename to src/app/(main)/pixels/[pixelId]/PixelShareForm.tsx
index 58d6b37ca..43f4d3169 100644
--- a/src/app/(main)/pixels/[pixelId]/PixelShareDialog.tsx
+++ b/src/app/(main)/pixels/[pixelId]/PixelShareForm.tsx
@@ -7,19 +7,19 @@ import { Plus } from '@/components/icons';
import { SimpleShareCreateForm } from '@/components/share/SimpleShareCreateForm';
import { SimpleSharesTable } from '@/components/share/SimpleSharesTable';
-export function PixelShareDialog({ pixelId }: { pixelId: string }) {
+export function PixelShareForm({ pixelId }: { pixelId: string }) {
const { data, error, isLoading } = usePixelSharesQuery({ pixelId });
const shares = data?.data || [];
const hasShares = shares.length > 0;
return (
-
+
);
}
-function PixelShareDialogContent({
+function PixelShareFormContent({
pixelId,
hasShares,
shares,
diff --git a/src/app/(main)/pixels/[pixelId]/edit/PixelEditPage.tsx b/src/app/(main)/pixels/[pixelId]/edit/PixelEditPage.tsx
new file mode 100644
index 000000000..4b1b05b3d
--- /dev/null
+++ b/src/app/(main)/pixels/[pixelId]/edit/PixelEditPage.tsx
@@ -0,0 +1,46 @@
+'use client';
+import { Column } from '@umami/react-zen';
+import Link from 'next/link';
+import { PixelEditForm } from '@/app/(main)/pixels/PixelEditForm';
+import { PixelProvider } from '@/app/(main)/pixels/PixelProvider';
+import { PixelShareForm } from '@/app/(main)/pixels/[pixelId]/PixelShareForm';
+import { Panel } from '@/components/common/Panel';
+import { IconLabel } from '@/components/common/IconLabel';
+import { PageHeader } from '@/components/common/PageHeader';
+import { useMessages, useNavigation, usePixel } from '@/components/hooks';
+import { ArrowLeft, Grid2x2 } from '@/components/icons';
+
+export function PixelEditPage({ pixelId }: { pixelId: string }) {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
+function PixelEditHeader() {
+ const pixel = usePixel();
+ const { t, labels } = useMessages();
+ const { renderUrl } = useNavigation();
+
+ return (
+ <>
+
+
+ } label={t(labels.pixel)} />
+
+
+ } />
+ >
+ );
+}
diff --git a/src/app/(main)/pixels/[pixelId]/edit/page.tsx b/src/app/(main)/pixels/[pixelId]/edit/page.tsx
new file mode 100644
index 000000000..2e6b1d387
--- /dev/null
+++ b/src/app/(main)/pixels/[pixelId]/edit/page.tsx
@@ -0,0 +1,18 @@
+import type { Metadata } from 'next';
+import { getPixel } from '@/queries/prisma';
+import { PixelEditPage } from './PixelEditPage';
+
+export default async function ({ params }: { params: Promise<{ pixelId: string }> }) {
+ const { pixelId } = await params;
+ const pixel = await getPixel(pixelId);
+
+ if (!pixel || pixel.deletedAt) {
+ return null;
+ }
+
+ return ;
+}
+
+export const metadata: Metadata = {
+ title: 'Edit Pixel',
+};
diff --git a/src/components/share/SimpleShareEditButton.tsx b/src/components/share/SimpleShareEditButton.tsx
new file mode 100644
index 000000000..eb56fda4d
--- /dev/null
+++ b/src/components/share/SimpleShareEditButton.tsx
@@ -0,0 +1,14 @@
+import { useMessages } from '@/components/hooks';
+import { Edit } from '@/components/icons';
+import { DialogButton } from '@/components/input/DialogButton';
+import { SimpleShareEditForm } from './SimpleShareEditForm';
+
+export function SimpleShareEditButton({ shareId }: { shareId: string }) {
+ const { t, labels } = useMessages();
+
+ return (
+ } title={t(labels.share)} variant="quiet" width="600px">
+ {({ close }) => }
+
+ );
+}
diff --git a/src/components/share/SimpleShareEditForm.tsx b/src/components/share/SimpleShareEditForm.tsx
new file mode 100644
index 000000000..3c83fed52
--- /dev/null
+++ b/src/components/share/SimpleShareEditForm.tsx
@@ -0,0 +1,104 @@
+import {
+ Button,
+ Column,
+ Form,
+ FormField,
+ FormSubmitButton,
+ Label,
+ Loading,
+ Row,
+ TextField,
+} from '@umami/react-zen';
+import { useEffect, useState } from 'react';
+import { useApi, useConfig, useMessages, useModified } from '@/components/hooks';
+
+export function SimpleShareEditForm({
+ shareId,
+ onSave,
+ onClose,
+}: {
+ shareId: string;
+ onSave?: () => void;
+ onClose?: () => void;
+}) {
+ const { t, labels, getErrorMessage } = useMessages();
+ const config = useConfig();
+ const { get, post } = useApi();
+ const { touch } = useModified();
+ const { modified } = useModified('shares');
+ const [share, setShare] = useState(null);
+ const [isLoading, setIsLoading] = useState(true);
+ const [isPending, setIsPending] = useState(false);
+ const [error, setError] = useState(null);
+
+ const getUrl = (slug: string) => {
+ return `${config?.cloudMode ? process.env.cloudUrl : window?.location.origin}${process.env.basePath || ''}/share/${slug}`;
+ };
+
+ useEffect(() => {
+ const loadShare = async () => {
+ setIsLoading(true);
+ try {
+ const data = await get(`/share/id/${shareId}`);
+ setShare(data);
+ } finally {
+ setIsLoading(false);
+ }
+ };
+
+ loadShare();
+ }, [get, modified, shareId]);
+
+ const handleSubmit = async (data: { name: string }) => {
+ setIsPending(true);
+ setError(null);
+
+ try {
+ await post(`/share/id/${shareId}`, {
+ name: data.name,
+ slug: share.slug,
+ parameters: share.parameters || {},
+ });
+
+ touch('shares');
+ onSave?.();
+ onClose?.();
+ } catch (e) {
+ setError(e);
+ } finally {
+ setIsPending(false);
+ }
+ };
+
+ if (isLoading) {
+ return ;
+ }
+
+ return (
+
+ );
+}
diff --git a/src/components/share/SimpleSharesTable.tsx b/src/components/share/SimpleSharesTable.tsx
index b938e6412..01a8dfb9d 100644
--- a/src/components/share/SimpleSharesTable.tsx
+++ b/src/components/share/SimpleSharesTable.tsx
@@ -4,10 +4,12 @@ import { CopyButton } from '@/components/common/CopyButton';
import { DateDistance } from '@/components/common/DateDistance';
import { ExternalLink } from '@/components/common/ExternalLink';
import { useConfig, useMessages, useMobile } from '@/components/hooks';
+import { SimpleShareEditButton } from './SimpleShareEditButton';
export function SimpleSharesTable(props: DataTableProps) {
const { t, labels } = useMessages();
- const { cloudMode } = useConfig();
+ const config = useConfig();
+ const cloudMode = config?.cloudMode;
const { isMobile } = useMobile();
const getUrl = (slug: string) => {
@@ -40,9 +42,10 @@ export function SimpleSharesTable(props: DataTableProps) {
{(row: any) => }
-
+
{({ id, slug }: any) => (
+
)}