From 6d1603fa2853c8578bc580fd1e6a4425727a6b8a Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Sat, 21 Jun 2025 01:45:36 -0700 Subject: [PATCH] New share URL form. --- package.json | 2 +- pnpm-lock.yaml | 10 +- .../(main)/settings/teams/TeamLeaveButton.tsx | 2 +- .../(main)/settings/teams/TeamsJoinButton.tsx | 2 +- .../websites/[websiteId]/ShareUrl.tsx | 90 ------------------ .../websites/[websiteId]/WebsiteData.tsx | 6 +- .../websites/[websiteId]/WebsiteSettings.tsx | 4 +- .../websites/[websiteId]/WebsiteShareForm.tsx | 95 +++++++++++++++++++ .../websites/[websiteId]/WebsiteHeader.tsx | 33 +++++-- .../websites/[websiteId]/WebsiteProvider.tsx | 3 +- src/app/api/websites/[websiteId]/route.ts | 4 +- src/index.ts | 2 +- src/lib/constants.ts | 2 +- src/styles/global.css | 5 + 14 files changed, 144 insertions(+), 116 deletions(-) delete mode 100644 src/app/(main)/settings/websites/[websiteId]/ShareUrl.tsx create mode 100644 src/app/(main)/settings/websites/[websiteId]/WebsiteShareForm.tsx diff --git a/package.json b/package.json index 26986083..71960f91 100644 --- a/package.json +++ b/package.json @@ -80,7 +80,7 @@ "@react-spring/web": "^9.7.3", "@svgr/cli": "^8.1.0", "@tanstack/react-query": "^5.80.10", - "@umami/react-zen": "^0.138.0", + "@umami/react-zen": "^0.139.0", "@umami/redis-client": "^0.27.0", "bcryptjs": "^2.4.3", "chalk": "^4.1.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ec0aee2b..ff4f606a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -45,8 +45,8 @@ importers: specifier: ^5.80.10 version: 5.80.10(react@19.1.0) '@umami/react-zen': - specifier: ^0.138.0 - version: 0.138.0(@babel/core@7.27.1)(@types/react@19.1.8)(babel-plugin-react-compiler@19.1.0-rc.2)(immer@9.0.21)(use-sync-external-store@1.5.0(react@19.1.0)) + specifier: ^0.139.0 + version: 0.139.0(@babel/core@7.27.1)(@types/react@19.1.8)(babel-plugin-react-compiler@19.1.0-rc.2)(immer@9.0.21)(use-sync-external-store@1.5.0(react@19.1.0)) '@umami/redis-client': specifier: ^0.27.0 version: 0.27.0 @@ -2549,8 +2549,8 @@ packages: resolution: {integrity: sha512-xoh5rJ+tgsRKoXnkBPFRLZ7rjKM0AfVbC68UZ/ECXoDbfggb9RbEySN359acY1vS3qZ0jVTVWzbtfapwm5ztxw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@umami/react-zen@0.138.0': - resolution: {integrity: sha512-MlrLu21/WjmzPnYRQgQTofb7o+1fvL8XF7EbCjZFKjW+VHz5Cg6nOZWiFBxGWWCIAWfIVZpvczvK+thi4hHigg==} + '@umami/react-zen@0.139.0': + resolution: {integrity: sha512-NRf27+05z78DLFxK3aQUBfhZW7covl6qtS4OcaBUbZ71VZ7eeRVg7SU7Cn3NvkXlcI16t6bbLXGW4HjvfBhXsw==} '@umami/redis-client@0.27.0': resolution: {integrity: sha512-SbHTpxhgeZyTBUSp2zdZM+XUtpsaSL4Tad8QXIEhEtjWhvvfoornyT5kLuyYCVtzSAT4daALeGmOO1z6EE1KcA==} @@ -9733,7 +9733,7 @@ snapshots: '@typescript-eslint/types': 8.34.1 eslint-visitor-keys: 4.2.1 - '@umami/react-zen@0.138.0(@babel/core@7.27.1)(@types/react@19.1.8)(babel-plugin-react-compiler@19.1.0-rc.2)(immer@9.0.21)(use-sync-external-store@1.5.0(react@19.1.0))': + '@umami/react-zen@0.139.0(@babel/core@7.27.1)(@types/react@19.1.8)(babel-plugin-react-compiler@19.1.0-rc.2)(immer@9.0.21)(use-sync-external-store@1.5.0(react@19.1.0))': dependencies: '@fontsource/jetbrains-mono': 5.2.6 '@internationalized/date': 3.8.2 diff --git a/src/app/(main)/settings/teams/TeamLeaveButton.tsx b/src/app/(main)/settings/teams/TeamLeaveButton.tsx index 61837246..18a5c821 100644 --- a/src/app/(main)/settings/teams/TeamLeaveButton.tsx +++ b/src/app/(main)/settings/teams/TeamLeaveButton.tsx @@ -17,7 +17,7 @@ export function TeamLeaveButton({ teamId, teamName }: { teamId: string; teamName return ( - - - {formatMessage(labels.save)} - - - - )} - - ); -} diff --git a/src/app/(main)/settings/websites/[websiteId]/WebsiteData.tsx b/src/app/(main)/settings/websites/[websiteId]/WebsiteData.tsx index 2394983f..83d74d10 100644 --- a/src/app/(main)/settings/websites/[websiteId]/WebsiteData.tsx +++ b/src/app/(main)/settings/websites/[websiteId]/WebsiteData.tsx @@ -52,9 +52,7 @@ export function WebsiteData({ websiteId, onSave }: { websiteId: string; onSave?: description={formatMessage(messages.transferWebsite)} > - + {({ close }) => ( @@ -70,7 +68,7 @@ export function WebsiteData({ websiteId, onSave }: { websiteId: string; onSave?: description={formatMessage(messages.resetWebsiteWarning)} > - + {({ close }) => ( diff --git a/src/app/(main)/settings/websites/[websiteId]/WebsiteSettings.tsx b/src/app/(main)/settings/websites/[websiteId]/WebsiteSettings.tsx index 8762070d..6a0b164e 100644 --- a/src/app/(main)/settings/websites/[websiteId]/WebsiteSettings.tsx +++ b/src/app/(main)/settings/websites/[websiteId]/WebsiteSettings.tsx @@ -4,7 +4,7 @@ import { WebsiteContext } from '@/app/(main)/websites/[websiteId]/WebsiteProvide import { useMessages } from '@/components/hooks'; import { Globe, Arrow } from '@/components/icons'; import { SectionHeader } from '@/components/common/SectionHeader'; -import { ShareUrl } from './ShareUrl'; +import { WebsiteShareForm } from './WebsiteShareForm'; import { TrackingCode } from './TrackingCode'; import { WebsiteData } from './WebsiteData'; import { WebsiteEditForm } from './WebsiteEditForm'; @@ -48,7 +48,7 @@ export function WebsiteSettings({ - + diff --git a/src/app/(main)/settings/websites/[websiteId]/WebsiteShareForm.tsx b/src/app/(main)/settings/websites/[websiteId]/WebsiteShareForm.tsx new file mode 100644 index 00000000..60c71e22 --- /dev/null +++ b/src/app/(main)/settings/websites/[websiteId]/WebsiteShareForm.tsx @@ -0,0 +1,95 @@ +import { + Form, + FormButtons, + TextField, + Button, + Switch, + FormSubmitButton, + Column, + Icon, + Grid, + Label, + useToast, + TooltipTrigger, + Tooltip, +} from '@umami/react-zen'; +import { useState } from 'react'; +import { getRandomChars } from '@/lib/crypto'; +import { useApi, useMessages, useModified } from '@/components/hooks'; +import { Refresh } from '@/components/icons'; + +const generateId = () => getRandomChars(16); + +export interface WebsiteShareFormProps { + websiteId: string; + shareId: string; + onSave?: () => void; + onClose?: () => void; +} + +export function WebsiteShareForm({ websiteId, shareId, onSave, onClose }: WebsiteShareFormProps) { + const { formatMessage, labels, messages } = useMessages(); + const [id, setId] = useState(shareId); + const { post, useMutation } = useApi(); + const { mutate, error, isPending } = useMutation({ + mutationFn: (data: any) => post(`/websites/${websiteId}`, data), + }); + const { touch } = useModified(); + const { toast } = useToast(); + + const url = `${window?.location.origin || ''}${process.env.basePath || ''}/share/${id}`; + + const handleGenerate = () => { + setId(generateId()); + }; + + const handleSwitch = () => { + setId(id ? null : generateId()); + }; + + const handleSave = () => { + const data = { + shareId: id, + }; + mutate(data, { + onSuccess: async () => { + toast(formatMessage(messages.saved)); + touch(`website:${websiteId}`); + onSave?.(); + onClose?.(); + }, + }); + }; + + return ( +
+ + + {formatMessage(labels.enableShareUrl)} + + {id && ( + + + + + + + {formatMessage(labels.regenerate)} + + + + )} + + + + {formatMessage(labels.save)} + + + +
+ ); +} diff --git a/src/app/(main)/websites/[websiteId]/WebsiteHeader.tsx b/src/app/(main)/websites/[websiteId]/WebsiteHeader.tsx index 8c974189..444f0524 100644 --- a/src/app/(main)/websites/[websiteId]/WebsiteHeader.tsx +++ b/src/app/(main)/websites/[websiteId]/WebsiteHeader.tsx @@ -1,9 +1,11 @@ -import { Button, Icon, Text, Row } from '@umami/react-zen'; +import { Button, Icon, Text, Row, DialogTrigger, Dialog, Modal } from '@umami/react-zen'; import { PageHeader } from '@/components/common/PageHeader'; import { useWebsite } from '@/components/hooks/useWebsite'; import { Share, Edit } from '@/components/icons'; import { Favicon } from '@/components/common/Favicon'; import { ActiveUsers } from '@/components/metrics/ActiveUsers'; +import { WebsiteShareForm } from '@/app/(main)/settings/websites/[websiteId]/WebsiteShareForm'; +import { useMessages } from '@/components/hooks'; export function WebsiteHeader() { const website = useWebsite(); @@ -12,12 +14,7 @@ export function WebsiteHeader() { } showBorder={false}> - + + + + {({ close }) => { + return ; + }} + + +
+ ); +}; diff --git a/src/app/(main)/websites/[websiteId]/WebsiteProvider.tsx b/src/app/(main)/websites/[websiteId]/WebsiteProvider.tsx index 27cbe386..ea0a8bbe 100644 --- a/src/app/(main)/websites/[websiteId]/WebsiteProvider.tsx +++ b/src/app/(main)/websites/[websiteId]/WebsiteProvider.tsx @@ -2,8 +2,9 @@ import { createContext, ReactNode, useEffect } from 'react'; import { useModified, useWebsiteQuery } from '@/components/hooks'; import { Loading } from '@umami/react-zen'; +import { Website } from '@/generated/prisma/client'; -export const WebsiteContext = createContext(null); +export const WebsiteContext = createContext(null); export function WebsiteProvider({ websiteId, diff --git a/src/app/api/websites/[websiteId]/route.ts b/src/app/api/websites/[websiteId]/route.ts index 346e5856..4f8763b5 100644 --- a/src/app/api/websites/[websiteId]/route.ts +++ b/src/app/api/websites/[websiteId]/route.ts @@ -31,8 +31,8 @@ export async function POST( { params }: { params: Promise<{ websiteId: string }> }, ) { const schema = z.object({ - name: z.string(), - domain: z.string(), + name: z.string().optional(), + domain: z.string().optional(), shareId: z.string().regex(SHARE_ID_REGEX).nullable().optional(), }); diff --git a/src/index.ts b/src/index.ts index f6663e46..6988166b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -26,7 +26,7 @@ export * from '@/app/(main)/settings/teams/TeamsJoinButton'; export * from '@/app/(main)/settings/teams/TeamsTable'; export * from '@/app/(main)/settings/teams/WebsiteTags'; -export * from '@/app/(main)/settings/websites/[websiteId]/ShareUrl'; +export * from '@/app/(main)/settings/websites/[websiteId]/WebsiteShareForm'; export * from '@/app/(main)/settings/websites/[websiteId]/TrackingCode'; export * from '@/app/(main)/settings/websites/[websiteId]/WebsiteData'; export * from '@/app/(main)/settings/websites/[websiteId]/WebsiteDeleteForm'; diff --git a/src/lib/constants.ts b/src/lib/constants.ts index 0ab6ae2e..19006492 100644 --- a/src/lib/constants.ts +++ b/src/lib/constants.ts @@ -198,7 +198,7 @@ export const CHART_COLORS = [ export const DOMAIN_REGEX = /^(localhost(:[1-9]\d{0,4})?|((?=[a-z0-9-_]{1,63}\.)(xn--)?[a-z0-9-_]+(-[a-z0-9-_]+)*\.)+(xn--)?[a-z0-9-_]{2,63})$/; -export const SHARE_ID_REGEX = /^[a-zA-Z0-9]{8,16}$/; +export const SHARE_ID_REGEX = /^[a-zA-Z0-9]{8,50}$/; export const DATETIME_REGEX = /^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(\.[0-9]{3}(Z|\+[0-9]{2}:[0-9]{2})?)?$/; diff --git a/src/styles/global.css b/src/styles/global.css index 37b16c33..f342f8fd 100644 --- a/src/styles/global.css +++ b/src/styles/global.css @@ -6,6 +6,11 @@ body { background-color: var(--background-color); width: 100%; min-height: 100vh; + overflow: hidden; +} + +html[style*='padding-right'] { + padding-right: 0 !important; } a,