@@ -12,6 +12,7 @@ import {
1212 GlobeIcon ,
1313 InfoIcon ,
1414 PencilIcon ,
15+ ShareIcon ,
1516 SlidersIcon ,
1617 TableIcon ,
1718 TrashIcon ,
@@ -47,7 +48,7 @@ import {
4748 TooltipTrigger ,
4849} from '@/components/ui/tooltip' ;
4950import { WebsiteDialog } from '@/components/website-dialog' ;
50- import { useDeleteWebsite } from '@/hooks/use-websites' ;
51+ import { useDeleteWebsite , useUpdateWebsite } from '@/hooks/use-websites' ;
5152import {
5253 generateNpmCode ,
5354 generateNpmComponentCode ,
@@ -75,8 +76,10 @@ export function WebsiteSettingsTab({
7576 const [ installMethod ] = useState < 'script' | 'npm' > ( 'script' ) ;
7677 const [ showDeleteDialog , setShowDeleteDialog ] = useState ( false ) ;
7778 const [ showEditDialog , setShowEditDialog ] = useState ( false ) ;
79+ const [ isPublic , setIsPublic ] = useState ( websiteData . isPublic ) ;
80+ const updateWebsiteMutation = useUpdateWebsite ( ) ;
7881 const [ activeTab , setActiveTab ] = useState <
79- 'tracking' | 'basic' | 'advanced' | 'optimization'
82+ 'tracking' | 'basic' | 'advanced' | 'optimization' | 'privacy'
8083 > ( 'tracking' ) ;
8184 const [ trackingOptions , setTrackingOptions ] =
8285 useState < TrackingOptions > ( RECOMMENDED_DEFAULTS ) ;
@@ -89,6 +92,23 @@ export function WebsiteSettingsTab({
8992 setTimeout ( ( ) => setCopiedBlockId ( null ) , 2000 ) ;
9093 } ;
9194
95+ const handleTogglePublic = ( ) => {
96+ const newIsPublic = ! isPublic ;
97+ setIsPublic ( newIsPublic ) ;
98+ toast . promise (
99+ updateWebsiteMutation . mutateAsync ( {
100+ id : websiteId ,
101+ isPublic : newIsPublic ,
102+ name : websiteData . name ,
103+ } ) ,
104+ {
105+ loading : 'Updating privacy settings...' ,
106+ success : 'Privacy settings updated!' ,
107+ error : 'Failed to update settings.' ,
108+ }
109+ ) ;
110+ } ;
111+
92112 const handleToggleOption = ( option : keyof TrackingOptions ) => {
93113 setTrackingOptions ( ( prev ) => toggleTrackingOption ( prev , option ) ) ;
94114 } ;
@@ -167,6 +187,14 @@ export function WebsiteSettingsTab({
167187 />
168188 ) }
169189
190+ { activeTab === 'privacy' && (
191+ < PrivacyTab
192+ isPublic = { isPublic }
193+ onTogglePublic = { handleTogglePublic }
194+ websiteId = { websiteId }
195+ />
196+ ) }
197+
170198 { activeTab !== 'tracking' && (
171199 < TabActions
172200 installMethod = { installMethod }
@@ -327,7 +355,7 @@ function SettingsNavigation({
327355} : {
328356 activeTab : string ;
329357 setActiveTab : (
330- tab : 'tracking' | 'basic' | 'advanced' | 'optimization'
358+ tab : 'tracking' | 'basic' | 'advanced' | 'optimization' | 'privacy'
331359 ) => void ;
332360 onDeleteClick : ( ) => void ;
333361 trackingOptions : TrackingOptions ;
@@ -434,6 +462,17 @@ function SettingsNavigation({
434462 </ Badge >
435463 </ Button >
436464
465+ < Button
466+ className = "h-10 w-full justify-between gap-2 transition-all duration-200"
467+ onClick = { ( ) => setActiveTab ( 'privacy' ) }
468+ variant = { activeTab === 'privacy' ? 'default' : 'ghost' }
469+ >
470+ < div className = "flex items-center gap-2" >
471+ < ShareIcon className = "h-4 w-4" />
472+ < span > Sharing</ span >
473+ </ div >
474+ </ Button >
475+
437476 < div className = "border-t pt-4" >
438477 < div className = "px-3 py-2" >
439478 < h3 className = "font-semibold text-muted-foreground text-xs uppercase tracking-wide" >
@@ -1449,3 +1488,75 @@ function DeleteWebsiteDialog({
14491488 </ AlertDialog >
14501489 ) ;
14511490}
1491+
1492+ function PrivacyTab ( {
1493+ isPublic,
1494+ onTogglePublic,
1495+ websiteId,
1496+ } : {
1497+ isPublic : boolean ;
1498+ onTogglePublic : ( ) => void ;
1499+ websiteId : string ;
1500+ } ) {
1501+ const shareableLink = `${ window . location . origin } /demo/${ websiteId } ` ;
1502+
1503+ const handleCopyLink = ( ) => {
1504+ navigator . clipboard . writeText ( shareableLink ) ;
1505+ toast . success ( 'Shareable link copied to clipboard!' ) ;
1506+ } ;
1507+
1508+ return (
1509+ < div className = "space-y-4" >
1510+ < div className = "flex flex-col space-y-1.5" >
1511+ < h3 className = "font-semibold text-lg" > Sharing & Privacy </ h3 >
1512+ < p className = "text-muted-foreground text-sm" >
1513+ Manage your website's public visibility and shareable link.
1514+ </ p >
1515+ </ div >
1516+ < div className = "rounded border p-4" >
1517+ < div className = "flex items-start justify-between" >
1518+ < div className = "space-y-1" >
1519+ < Label className = "font-medium" htmlFor = "public-access" >
1520+ Public Access
1521+ </ Label >
1522+ < p className = "text-muted-foreground text-xs" >
1523+ Allow anyone with the link to view your website's dashboard.
1524+ </ p >
1525+ </ div >
1526+ < Switch
1527+ checked = { isPublic }
1528+ id = "public-access"
1529+ onCheckedChange = { onTogglePublic }
1530+ />
1531+ </ div >
1532+
1533+ { isPublic && (
1534+ < div className = "mt-4 space-y-2 border-t pt-4" >
1535+ < Label htmlFor = "shareable-link" > Shareable Link</ Label >
1536+ < div className = "flex items-center gap-2" >
1537+ < input
1538+ className = "flex-grow rounded border bg-background px-3 py-1.5 text-sm"
1539+ id = "shareable-link"
1540+ readOnly
1541+ type = "text"
1542+ value = { shareableLink }
1543+ />
1544+ < Button
1545+ className = "h-8 gap-1.5 px-3 text-xs"
1546+ onClick = { handleCopyLink }
1547+ size = "sm"
1548+ variant = "outline"
1549+ >
1550+ < ClipboardIcon className = "h-3.5 w-3.5" />
1551+ Copy
1552+ </ Button >
1553+ </ div >
1554+ < p className = "text-muted-foreground text-xs" >
1555+ Anyone with this link can view the analytics for this website.
1556+ </ p >
1557+ </ div >
1558+ ) }
1559+ </ div >
1560+ </ div >
1561+ ) ;
1562+ }
0 commit comments