@@ -12,7 +12,7 @@ import {
1212 Input ,
1313 Label ,
1414} from "@nasa-jpl/stellar-react" ;
15- import { ArrowDown , ArrowUp , Trash2 } from "lucide-react" ;
15+ import { ArrowDown , ArrowUp , Copy , Trash2 } from "lucide-react" ;
1616import { FormEvent , useState } from "react" ;
1717import { useOutletContext } from "react-router-dom" ;
1818import { z } from "zod" ;
@@ -22,8 +22,12 @@ import { Tooltip } from "../components/ui/Tooltip";
2222import { Product } from "../types/api" ;
2323import { ProductPreview } from "../types/page" ;
2424import { PageGroup , Page as PageType , View } from "../types/view" ;
25- import { downloadJSON } from "../utilities/generic" ;
26- import { createViewPage , createViewPageGroup } from "../utilities/view" ;
25+ import { downloadJSON , generateUUID } from "../utilities/generic" ;
26+ import {
27+ createViewPage ,
28+ createViewPageGroup ,
29+ duplicateSection ,
30+ } from "../utilities/view" ;
2731
2832export default function ManagementPage ( ) {
2933 // TODO type outlet context instead of duplicating
@@ -114,6 +118,28 @@ export default function ManagementPage() {
114118 setView ( newView ) ;
115119 } ;
116120
121+ const duplicatePageGroup = ( pageGroup : PageGroup , insertAfter : number ) => {
122+ const newPageGroup : PageGroup = {
123+ ...pageGroup ,
124+ id : generateUUID ( ) ,
125+ title : `${ pageGroup . title } Copy` ,
126+ url : `${ pageGroup . url } _copy` ,
127+ pages : pageGroup . pages . map ( ( page ) => ( {
128+ ...page ,
129+ sections : page . sections . map ( duplicateSection ) ,
130+ } ) ) ,
131+ } ;
132+ const newView = {
133+ ...view ,
134+ pageGroups : [
135+ ...view . pageGroups . slice ( 0 , insertAfter + 1 ) ,
136+ newPageGroup ,
137+ ...view . pageGroups . slice ( insertAfter + 1 ) ,
138+ ] ,
139+ } ;
140+ setView ( newView ) ;
141+ } ;
142+
117143 const deletePage = ( pageGroupId : string , pageId : string ) => {
118144 const newView = {
119145 ...view ,
@@ -187,6 +213,29 @@ export default function ManagementPage() {
187213 setView ( { ...view , pageGroups : newPageGroups } ) ;
188214 } ;
189215
216+ const duplicatePage = (
217+ page : PageType ,
218+ pageGroup : PageGroup ,
219+ insertAfter : number
220+ ) => {
221+ const newPage : PageType = {
222+ ...page ,
223+ id : generateUUID ( ) ,
224+ title : `${ page . title } Copy` ,
225+ url : `${ page . url } _copy` ,
226+ sections : page . sections . map ( duplicateSection ) ,
227+ } ;
228+ const newPageGroup : PageGroup = {
229+ ...pageGroup ,
230+ pages : [
231+ ...pageGroup . pages . slice ( 0 , insertAfter + 1 ) ,
232+ newPage ,
233+ ...pageGroup . pages . slice ( insertAfter + 1 ) ,
234+ ] ,
235+ } ;
236+ updatePageGroup ( newPageGroup ) ;
237+ } ;
238+
190239 const movePage = (
191240 page : PageType ,
192241 pageGroup : PageGroup ,
@@ -348,6 +397,15 @@ export default function ManagementPage() {
348397 }
349398 />
350399 < div className = "flex self-end" >
400+ < Tooltip content = "Duplicate Page" >
401+ < Button
402+ variant = "ghost"
403+ size = "icon"
404+ onClick = { ( ) => duplicatePageGroup ( pageGroup , i ) }
405+ >
406+ < Copy size = { 16 } />
407+ </ Button >
408+ </ Tooltip >
351409 { renderDeletionConfirmation ( "page group" , ( ) =>
352410 deletePageGroup ( pageGroup . id )
353411 ) }
@@ -443,6 +501,15 @@ export default function ManagementPage() {
443501 }
444502 />
445503 < div className = "flex self-end" >
504+ < Tooltip content = "Duplicate Page Group" >
505+ < Button
506+ variant = "ghost"
507+ size = "icon"
508+ onClick = { ( ) => duplicatePage ( page , pageGroup , j ) }
509+ >
510+ < Copy size = { 16 } />
511+ </ Button >
512+ </ Tooltip >
446513 { renderDeletionConfirmation ( "page" , ( ) =>
447514 deletePage ( pageGroup . id , page . id )
448515 ) }
0 commit comments