diff --git a/src/components/CopyableCode.tsx b/src/components/CopyableCode.tsx new file mode 100644 index 0000000..a8d9d04 --- /dev/null +++ b/src/components/CopyableCode.tsx @@ -0,0 +1,45 @@ +import { useState } from 'react' + +interface Props { + code: string + prefix?: string +} + +export default function CopyableCode({ code, prefix }: Props) { + const [copied, setCopied] = useState(false) + + const handleCopy = async (e: React.MouseEvent) => { + e.stopPropagation() + try { + await navigator.clipboard.writeText(code) + setCopied(true) + setTimeout(() => setCopied(false), 1500) + } catch { + // fallback: select text + } + } + + return ( + + {prefix}{code} + {copied && ( + + )} + + ) +} diff --git a/src/pages/MeetingDetailPage.tsx b/src/pages/MeetingDetailPage.tsx index 08df180..5af76eb 100644 --- a/src/pages/MeetingDetailPage.tsx +++ b/src/pages/MeetingDetailPage.tsx @@ -3,6 +3,7 @@ import { useParams, useNavigate } from 'react-router-dom' import api from '@/services/api' import { useAuth } from '@/hooks/useAuth' import dayjs from 'dayjs' +import CopyableCode from '@/components/CopyableCode' interface MeetingItem { id: number @@ -153,7 +154,7 @@ export default function MeetingDetailPage() {
-

📅 {meeting.meeting_code || `#${meeting.id}`}

+

📅 {meeting.meeting_code ? : `#${meeting.id}`}

{meeting.status} {meeting.project_code && Project: {meeting.project_code}} diff --git a/src/pages/MilestoneDetailPage.tsx b/src/pages/MilestoneDetailPage.tsx index a3bc37c..0f55b52 100644 --- a/src/pages/MilestoneDetailPage.tsx +++ b/src/pages/MilestoneDetailPage.tsx @@ -6,6 +6,7 @@ import dayjs from 'dayjs' import CreateTaskModal from '@/components/CreateTaskModal' import MilestoneFormModal from '@/components/MilestoneFormModal' import { useAuth } from '@/hooks/useAuth' +import CopyableCode from '@/components/CopyableCode' interface MilestoneTask { id: number @@ -155,7 +156,7 @@ export default function MilestoneDetailPage() {
-

🏁 {milestone.title}

+

🏁 {milestone.milestone_code && <> }{milestone.title}

{milestone.status} {milestone.due_date && Due {dayjs(milestone.due_date).format('YYYY-MM-DD')}} diff --git a/src/pages/ProjectDetailPage.tsx b/src/pages/ProjectDetailPage.tsx index 6ba7028..af30090 100644 --- a/src/pages/ProjectDetailPage.tsx +++ b/src/pages/ProjectDetailPage.tsx @@ -6,6 +6,7 @@ import dayjs from 'dayjs' import { useAuth } from '@/hooks/useAuth' import ProjectFormModal from '@/components/ProjectFormModal' import MilestoneFormModal from '@/components/MilestoneFormModal' +import CopyableCode from '@/components/CopyableCode' export default function ProjectDetailPage() { const { id } = useParams() @@ -74,7 +75,7 @@ export default function ProjectDetailPage() {
-

📁 {project.name} {project.project_code && {project.project_code}}

+

📁 {project.name} {project.project_code && }

{project.description || 'No description'}

{project.repo &&

📦 {project.repo}

}
Owner: {project.owner_name || 'Unknown'}
diff --git a/src/pages/ProposeDetailPage.tsx b/src/pages/ProposeDetailPage.tsx index 59c0b06..045bc56 100644 --- a/src/pages/ProposeDetailPage.tsx +++ b/src/pages/ProposeDetailPage.tsx @@ -3,6 +3,7 @@ import { useParams, useNavigate, useSearchParams } from 'react-router-dom' import api from '@/services/api' import type { Propose, Milestone } from '@/types' import dayjs from 'dayjs' +import CopyableCode from '@/components/CopyableCode' export default function ProposeDetailPage() { const { id } = useParams() @@ -124,7 +125,7 @@ export default function ProposeDetailPage() {

💡 {propose.title} - {propose.propose_code && {propose.propose_code}} + {propose.propose_code && }

{propose.status} @@ -136,7 +137,7 @@ export default function ProposeDetailPage() {

Details

-
Propose Code: {propose.propose_code || '—'}
+
Propose Code: {propose.propose_code ? : '—'}
Status: {propose.status}
Created By: {propose.created_by_username || (propose.created_by_id ? `User #${propose.created_by_id}` : '—')}
Created: {dayjs(propose.created_at).format('YYYY-MM-DD HH:mm')}
diff --git a/src/pages/SupportDetailPage.tsx b/src/pages/SupportDetailPage.tsx index 3c42fe0..f36def5 100644 --- a/src/pages/SupportDetailPage.tsx +++ b/src/pages/SupportDetailPage.tsx @@ -2,6 +2,7 @@ import { useState, useEffect } from 'react' import { useParams, useNavigate } from 'react-router-dom' import api from '@/services/api' import { useAuth } from '@/hooks/useAuth' +import CopyableCode from '@/components/CopyableCode' interface SupportItem { id: number @@ -141,7 +142,7 @@ export default function SupportDetailPage() {
-

🎫 {support.support_code || `#${support.id}`}

+

🎫 {support.support_code ? : `#${support.id}`}

{support.status} {support.priority} diff --git a/src/pages/TaskDetailPage.tsx b/src/pages/TaskDetailPage.tsx index 27f23a1..63a5fbb 100644 --- a/src/pages/TaskDetailPage.tsx +++ b/src/pages/TaskDetailPage.tsx @@ -5,6 +5,7 @@ import type { Task, Comment, Project, ProjectMember, Milestone } from '@/types' import dayjs from 'dayjs' import { useAuth } from '@/hooks/useAuth' import CreateTaskModal from '@/components/CreateTaskModal' +import CopyableCode from '@/components/CopyableCode' export default function TaskDetailPage() { const { id } = useParams() @@ -120,7 +121,7 @@ export default function TaskDetailPage() {
-

{task.task_code ? `[${task.task_code}]` : `#${task.id}`} {task.title}

+

{task.task_code ? <> : `#${task.id} `}{task.title}

{task.status} {task.priority}