All checks were successful
Trigger Cloudarix Deploy / call-webhook (push) Successful in 2s
Implemented Tasks T038-T046: - T038: Created useCopyToClipboard hook with toast notifications - T039: Skipped (unit tests - optional) - T040: Added copy button for Policy ID field - T041: Added copy button for Setting Name field - T042: Added tabs for Details and Raw JSON views - T043: Implemented Raw JSON tab with syntax highlighting - T044: Created getIntunePortalLink utility (8 policy types) - T045: Added Open in Intune button with URL construction - T046: Fallback to copy Policy ID if URL unavailable Files Created: - lib/hooks/useCopyToClipboard.ts (65 lines) - lib/utils/policy-table-helpers.ts (127 lines) Files Updated: - components/policy-explorer/PolicyDetailSheet.tsx (enhanced with tabs, copy buttons, Intune links) Features: - Copy-to-clipboard for all fields with visual feedback - Two-tab interface: Details (enhanced fields) and Raw JSON (full object) - Deep linking to Intune portal by policy type - Clipboard API with document.execCommand fallback - Toast notifications for user feedback
71 lines
2.0 KiB
TypeScript
71 lines
2.0 KiB
TypeScript
/**
|
|
* useCopyToClipboard Hook
|
|
*
|
|
* Wrapper for Clipboard API with success/error handling and toast notifications.
|
|
*
|
|
* Features:
|
|
* - Copy text to clipboard
|
|
* - Success/error state tracking
|
|
* - Automatic toast notifications
|
|
* - Fallback for older browsers
|
|
*/
|
|
|
|
import { useState } from 'react';
|
|
import { toast } from 'sonner';
|
|
|
|
interface CopyToClipboardResult {
|
|
copy: (text: string, successMessage?: string) => Promise<void>;
|
|
isCopied: boolean;
|
|
error: Error | null;
|
|
}
|
|
|
|
export function useCopyToClipboard(): CopyToClipboardResult {
|
|
const [isCopied, setIsCopied] = useState(false);
|
|
const [error, setError] = useState<Error | null>(null);
|
|
|
|
const copy = async (text: string, successMessage: string = 'Copied to clipboard') => {
|
|
// Reset state
|
|
setIsCopied(false);
|
|
setError(null);
|
|
|
|
try {
|
|
// Modern Clipboard API
|
|
if (navigator.clipboard && window.isSecureContext) {
|
|
await navigator.clipboard.writeText(text);
|
|
} else {
|
|
// Fallback for older browsers or non-secure contexts
|
|
const textArea = document.createElement('textarea');
|
|
textArea.value = text;
|
|
textArea.style.position = 'fixed';
|
|
textArea.style.left = '-999999px';
|
|
textArea.style.top = '-999999px';
|
|
document.body.appendChild(textArea);
|
|
textArea.focus();
|
|
textArea.select();
|
|
|
|
const successful = document.execCommand('copy');
|
|
textArea.remove();
|
|
|
|
if (!successful) {
|
|
throw new Error('Copy command failed');
|
|
}
|
|
}
|
|
|
|
setIsCopied(true);
|
|
toast.success(successMessage);
|
|
|
|
// Reset isCopied after 2 seconds
|
|
setTimeout(() => {
|
|
setIsCopied(false);
|
|
}, 2000);
|
|
} catch (err) {
|
|
const copyError = err instanceof Error ? err : new Error('Failed to copy');
|
|
setError(copyError);
|
|
toast.error('Failed to copy to clipboard');
|
|
console.error('Copy error:', copyError);
|
|
}
|
|
};
|
|
|
|
return { copy, isCopied, error };
|
|
}
|