33import { Link } from "@/i18n/routing" ;
44import { useState } from "react" ;
55import { useRouter } from "next/navigation" ;
6- import { deleteResume } from "@/actions/resume" ;
6+ import { deleteResume , updateResumeShareSettings } from "@/actions/resume" ;
77import type { Resume } from "@/db/schema" ;
88import { TEMPLATES } from "@/lib/constants" ;
99
@@ -22,6 +22,7 @@ export function ResumeCard({ resume }: ResumeCardProps) {
2222 const [ shareLocation , setShareLocation ] = useState (
2323 resume . data . personalInfo . shareLocation !== false
2424 ) ;
25+ const [ isCopied , setIsCopied ] = useState ( false ) ;
2526 const router = useRouter ( ) ;
2627
2728 const template = TEMPLATES . find ( ( t ) => t . id === resume . templateId ) ;
@@ -44,21 +45,24 @@ export function ResumeCard({ resume }: ResumeCardProps) {
4445 }
4546 } ;
4647
47- const publicLink = `/r/${ resume . id } ` ;
48+ const origin = typeof window !== "undefined" ? window . location . origin : "" ;
49+ const absoluteLink = `${ origin } /r/${ resume . id } ` ;
50+
51+ const handleCopy = ( ) => {
52+ navigator . clipboard . writeText ( absoluteLink ) ;
53+ setIsCopied ( true ) ;
54+ setTimeout ( ( ) => setIsCopied ( false ) , 2000 ) ;
55+ } ;
4856
4957 const handleSaveShare = async ( ) => {
5058 setIsSavingShare ( true ) ;
5159 try {
52- const response = await fetch ( `/api/resume/${ resume . id } /share` , {
53- method : "PATCH" ,
54- headers : { "Content-Type" : "application/json" } ,
55- body : JSON . stringify ( { isPublic, shareEmail, sharePhone, shareLocation } ) ,
60+ await updateResumeShareSettings ( resume . id , {
61+ isPublic,
62+ shareEmail,
63+ sharePhone,
64+ shareLocation,
5665 } ) ;
57-
58- if ( ! response . ok ) {
59- throw new Error ( "Failed to save share settings" ) ;
60- }
61-
6266 router . refresh ( ) ;
6367 } catch ( error ) {
6468 console . error ( "Failed to save share settings:" , error ) ;
@@ -100,24 +104,25 @@ export function ResumeCard({ resume }: ResumeCardProps) {
100104
101105 { /* Actions */ }
102106 { ! showDeleteConfirm ? (
103- < div className = "flex gap-2" >
107+ < div className = "flex flex-wrap gap-2" >
104108 < Link
105109 href = { `/builder/${ resume . id } ` }
106- className = "flex-1 border border-black bg-black text-white px-4 py-2 text-xs font-bold uppercase tracking-wider text-center hover:bg-white hover:text-black transition-colors duration-150"
110+ className = "flex-grow border border-black bg-black text-white px-4 py-2 text-xs font-bold uppercase tracking-wider text-center hover:bg-white hover:text-black transition-colors duration-150"
107111 >
108112 Edit
109113 </ Link >
110114 < Link
111115 href = { `/builder/${ resume . id } ?entry=import#personal` }
112- className = "border border-gray-400 px-4 py-2 text-xs font-bold uppercase tracking-wider hover:border-black hover:bg-black hover:text-white transition-colors duration-150"
116+ className = "border border-gray-400 px-4 py-2 text-xs font-bold uppercase tracking-wider text-center hover:border-black hover:bg-black hover:text-white transition-colors duration-150"
113117 >
114118 Import
115119 </ Link >
116120 < Link
117121 href = { `/builder/${ resume . id } ` }
118122 target = "_blank"
119123 rel = "noopener noreferrer"
120- className = "border border-gray-400 px-4 py-2 text-xs font-bold uppercase tracking-wider hover:border-black hover:bg-black hover:text-white transition-colors duration-150"
124+ title = "Preview"
125+ className = "border border-gray-400 px-4 py-2 text-xs font-bold uppercase tracking-wider text-center hover:border-black hover:bg-black hover:text-white transition-colors duration-150"
121126 >
122127 ⧉
123128 </ Link >
@@ -129,6 +134,7 @@ export function ResumeCard({ resume }: ResumeCardProps) {
129134 </ button >
130135 < button
131136 onClick = { ( ) => setShowDeleteConfirm ( true ) }
137+ title = "Delete"
132138 className = "border border-gray-400 px-4 py-2 text-xs font-bold uppercase tracking-wider hover:border-red-600 hover:text-red-600 transition-colors duration-150"
133139 >
134140 ×
@@ -196,14 +202,34 @@ export function ResumeCard({ resume }: ResumeCardProps) {
196202 </ div >
197203
198204 { isPublic ? (
199- < Link
200- href = { publicLink }
201- target = "_blank"
202- rel = "noopener noreferrer"
203- className = "inline-flex border border-black px-3 py-1 text-xs font-bold uppercase tracking-wider hover:bg-black hover:text-white transition-colors duration-150"
204- >
205- Open public link
206- </ Link >
205+ < div className = "mt-2 space-y-2 border-t border-gray-200 pt-3" >
206+ < span className = "text-xs text-gray-500 font-bold uppercase tracking-wider block" > Public Link</ span >
207+ < div className = "flex gap-2" >
208+ < input
209+ type = "text"
210+ readOnly
211+ value = { absoluteLink }
212+ className = "flex-1 border border-gray-300 px-3 py-2 text-xs font-mono bg-white focus:outline-none focus:border-black"
213+ onClick = { ( e ) => e . currentTarget . select ( ) }
214+ />
215+ < button
216+ onClick = { handleCopy }
217+ className = "border border-black bg-black text-white px-4 py-2 text-xs font-bold uppercase tracking-wider hover:bg-white hover:text-black transition-colors duration-150 min-w-[80px]"
218+ >
219+ { isCopied ? "Copied" : "Copy" }
220+ </ button >
221+ </ div >
222+ < div className = "flex justify-end mt-1" >
223+ < Link
224+ href = { absoluteLink }
225+ target = "_blank"
226+ rel = "noopener noreferrer"
227+ className = "text-[10px] text-gray-500 hover:text-black hover:underline uppercase tracking-wider"
228+ >
229+ Open in new tab ↗
230+ </ Link >
231+ </ div >
232+ </ div >
207233 ) : null }
208234
209235 < div className = "flex gap-2" >
0 commit comments