'use client'; import React, { useEffect, useMemo, useState } from 'react'; import { Button } from '@/components/ui/buttons'; import { Input } from '@/components/ui/inputs'; import { Separator } from '@/components/ui/seperator'; import { Switch } from '@/components/ui/switch'; import { PulseLoader } from '@/components/ui/loaders'; import { useMemoizedFn } from '@/hooks'; import { createDayjsDate } from '@/lib/date'; import { BusterRoutes, createBusterRoute } from '@/routes'; import { ShareAssetType } from '@/api/asset_interfaces'; import { Text } from '@/components/ui/typography'; import { useBusterNotifications } from '@/context/BusterNotifications'; import { Link, Eye, EyeSlash } from '@/components/ui/icons'; import { DatePicker } from '@/components/ui/date'; import { useUpdateCollectionShare } from '@/api/buster_rest/collections'; import { useUpdateMetricShare } from '@/api/buster_rest/metrics'; import { useUpdateDashboardShare } from '@/api/buster_rest/dashboards'; import { SelectSingleEventHandler } from 'react-day-picker'; import { ShareMenuContentBodyProps } from './ShareMenuContentBody'; import { cn } from '@/lib/classMerge'; export const ShareMenuContentPublish: React.FC = React.memo( ({ assetType, assetId, password = '', publicly_accessible, onCopyLink, publicExpirationDate, className }) => { const { openInfoMessage } = useBusterNotifications(); const { mutateAsync: onShareMetric, isPending: isPublishingMetric } = useUpdateMetricShare(); const { mutateAsync: onShareDashboard, isPending: isPublishingDashboard } = useUpdateDashboardShare(); const { mutateAsync: onShareCollection, isPending: isPublishingCollection } = useUpdateCollectionShare(); const [isPasswordProtected, setIsPasswordProtected] = useState(!!password); const [_password, _setPassword] = React.useState(password || ''); const isPublishing = isPublishingMetric || isPublishingDashboard || isPublishingCollection; const linkExpiry = useMemo(() => { return publicExpirationDate ? new Date(publicExpirationDate) : null; }, [publicExpirationDate]); const url = useMemo(() => { let url = ''; if (assetType === ShareAssetType.METRIC) { url = createBusterRoute({ route: BusterRoutes.APP_METRIC_ID_CHART, metricId: assetId }); } else if (assetType === ShareAssetType.DASHBOARD) { url = createBusterRoute({ route: BusterRoutes.APP_DASHBOARD_ID, dashboardId: assetId }); } else if (assetType === ShareAssetType.COLLECTION) { url = createBusterRoute({ route: BusterRoutes.APP_COLLECTIONS }); } return window.location.origin + url; }, [assetId, assetType]); const onTogglePublish = useMemoizedFn(async (v?: boolean) => { const linkExp = linkExpiry ? linkExpiry.toISOString() : null; const payload: Parameters[0] = { id: assetId, params: { publicly_accessible: v === undefined ? true : !!v, public_password: _password || null, public_expiry_date: linkExp } }; if (assetType === ShareAssetType.METRIC) { await onShareMetric(payload); } else if (assetType === ShareAssetType.DASHBOARD) { await onShareDashboard(payload); } else if (assetType === ShareAssetType.COLLECTION) { await onShareCollection(payload); } }); const onSetPasswordProtected = useMemoizedFn(async (v: boolean) => { onSetPassword(null); setIsPasswordProtected(v); }); const onSetPassword = useMemoizedFn(async (password: string | null) => { const payload: Parameters[0] = { id: assetId, params: { public_password: password } }; if (assetType === ShareAssetType.METRIC) { await onShareMetric(payload); } else if (assetType === ShareAssetType.DASHBOARD) { await onShareDashboard(payload); } else if (assetType === ShareAssetType.COLLECTION) { await onShareCollection(payload); } _setPassword(password || ''); if (password) openInfoMessage('Password updated'); }); const onSetExpirationDate = useMemoizedFn(async (date: Date | null) => { const linkExp = date ? date.toISOString() : null; const payload: Parameters[0] = { id: assetId, params: { public_expiry_date: linkExp } }; if (assetType === ShareAssetType.METRIC) { await onShareMetric(payload); } else if (assetType === ShareAssetType.DASHBOARD) { await onShareDashboard(payload); } else if (assetType === ShareAssetType.COLLECTION) { await onShareCollection(payload); } }); useEffect(() => { _setPassword(password || ''); setIsPasswordProtected(!!password); }, [password]); return (
{publicly_accessible ? ( <>
) : (
Anyone with the link will be able to view.
)}
{publicly_accessible && ( <>
)}
); } ); ShareMenuContentPublish.displayName = 'ShareMenuContentPublish'; const IsPublishedInfo: React.FC<{ isPublished: boolean }> = React.memo(({ isPublished }) => { if (!isPublished) return null; return (
Live on the web
); }); IsPublishedInfo.displayName = 'IsPublishedInfo'; const LinkExpiration: React.FC<{ linkExpiry: Date | null; onChangeLinkExpiry: (date: Date | null) => void; }> = React.memo(({ onChangeLinkExpiry, linkExpiry }) => { const dateFormat = 'LL'; const now = useMemo(() => { return createDayjsDate(new Date()); }, []); const maxDate = useMemo(() => { return createDayjsDate(new Date()).add(2, 'year'); }, []); const onSelect = useMemoizedFn((date: Date | undefined) => { onChangeLinkExpiry(date || null); }); return (
Link expiration { const dateValue = createDayjsDate(date); return dateValue.isBefore(now) || dateValue.isAfter(maxDate); }} />
); }); LinkExpiration.displayName = 'LinkExpiration'; const SetAPassword: React.FC<{ password: string; onSetPassword: (password: string | null) => void; isPasswordProtected: boolean; onSetPasswordProtected: (isPasswordProtected: boolean) => void; }> = React.memo( ({ password: passwordProp, onSetPassword, isPasswordProtected, onSetPasswordProtected }) => { const [visibilityToggle, setVisibilityToggle] = useState(false); const [password, setPassword] = useState(passwordProp); const isPasswordDifferent = password !== passwordProp; const onChangeChecked = useMemoizedFn((checked: boolean) => { onSetPasswordProtected(checked); }); const onChangePassword = useMemoizedFn((e: React.ChangeEvent) => { setPassword(e.target.value); }); const onClickVisibilityToggle = useMemoizedFn(() => { setVisibilityToggle(!visibilityToggle); }); const onClickSave = useMemoizedFn(() => { onSetPassword(password); }); const memoizedVisibilityToggle = useMemo(() => { return { visible: visibilityToggle, onVisibleChange: (visible: boolean) => setVisibilityToggle(visible) }; }, [visibilityToggle]); useEffect(() => { if (isPasswordProtected) { setPassword(password); } else { setPassword(''); } }, [isPasswordProtected, password]); return (
Set a password
{isPasswordProtected && (
)}
); } ); SetAPassword.displayName = 'SetAPassword';