Compare commits
No commits in common. "4734c809d75538d24508a9d1f9f681d164c07db1" and "7f2bd8bb82c52e46fc3d6a2470b325c1f1f70d58" have entirely different histories.
4734c809d7
...
7f2bd8bb82
|
|
@ -6,11 +6,13 @@ import { Button } from '@/components/ui/button';
|
||||||
|
|
||||||
interface CodeBlockProps {
|
interface CodeBlockProps {
|
||||||
code: string;
|
code: string;
|
||||||
type: 'bash' | 'sql' | 'text' | 'branch';
|
type: 'bash' | 'sql' | 'text';
|
||||||
showLineNumbers?: boolean;
|
showLineNumbers?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function CodeBlock({ code, type, showLineNumbers = true }: CodeBlockProps) {
|
export function CodeBlock({ code, type, showLineNumbers = true }: CodeBlockProps) {
|
||||||
|
console.log('[CodeBlock] Render - code length:', code?.length, 'code preview:', code?.substring(0, 50), 'type:', type);
|
||||||
|
|
||||||
const [copied, setCopied] = useState(false);
|
const [copied, setCopied] = useState(false);
|
||||||
|
|
||||||
// Compute highlighted code synchronously
|
// Compute highlighted code synchronously
|
||||||
|
|
@ -28,14 +30,17 @@ export function CodeBlock({ code, type, showLineNumbers = true }: CodeBlockProps
|
||||||
require('prismjs/components/prism-bash');
|
require('prismjs/components/prism-bash');
|
||||||
}
|
}
|
||||||
|
|
||||||
const language = type === 'text' || type === 'branch' ? 'text' : type;
|
const language = type === 'text' ? 'text' : type;
|
||||||
const grammar = Prism.languages[language] || Prism.languages.text;
|
const grammar = Prism.languages[language] || Prism.languages.text;
|
||||||
return Prism.highlight(code, grammar, language);
|
return Prism.highlight(code, grammar, language);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
console.error('[CodeBlock] Prism error:', e);
|
||||||
return code.replace(/</g, '<').replace(/>/g, '>');
|
return code.replace(/</g, '<').replace(/>/g, '>');
|
||||||
}
|
}
|
||||||
}, [code, type]);
|
}, [code, type]);
|
||||||
|
|
||||||
|
console.log('[CodeBlock] highlighted length:', highlighted?.length);
|
||||||
|
|
||||||
const handleCopy = async () => {
|
const handleCopy = async () => {
|
||||||
await navigator.clipboard.writeText(code);
|
await navigator.clipboard.writeText(code);
|
||||||
setCopied(true);
|
setCopied(true);
|
||||||
|
|
@ -47,21 +52,31 @@ export function CodeBlock({ code, type, showLineNumbers = true }: CodeBlockProps
|
||||||
const highlightedLines = highlighted ? highlighted.split('\n') : [];
|
const highlightedLines = highlighted ? highlighted.split('\n') : [];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div className="relative group">
|
||||||
className="rounded-lg bg-slate-900 p-4 text-sm overflow-x-auto"
|
<Button
|
||||||
style={{ maxWidth: '100%' }}
|
variant="ghost"
|
||||||
|
size="sm"
|
||||||
|
className="absolute top-2 right-2 opacity-0 group-hover:opacity-100 transition-opacity z-10"
|
||||||
|
onClick={handleCopy}
|
||||||
>
|
>
|
||||||
<pre className={`language-${type} m-0`} style={{ minWidth: '100%' }}>
|
{copied ? (
|
||||||
|
<Check className="w-4 h-4 text-green-500" />
|
||||||
|
) : (
|
||||||
|
<Copy className="w-4 h-4" />
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
<div className="rounded-lg bg-slate-900 p-4 overflow-x-auto text-sm">
|
||||||
|
<pre className={`language-${type} m-0`}>
|
||||||
{showLineNumbers ? (
|
{showLineNumbers ? (
|
||||||
<div className="flex" style={{ minWidth: 0 }}>
|
<div className="flex">
|
||||||
{/* Line numbers column - fixed width */}
|
{/* Line numbers column */}
|
||||||
<div className="flex flex-col text-slate-500 text-right pr-4 select-none w-12 shrink-0">
|
<div className="flex flex-col text-slate-500 text-right pr-4 select-none min-w-[3rem]">
|
||||||
{lines.map((_: string, i: number) => (
|
{lines.map((_: string, i: number) => (
|
||||||
<span key={i}>{i + 1}</span>
|
<span key={i}>{i + 1}</span>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
{/* Code column */}
|
{/* Code column */}
|
||||||
<div className="flex flex-col" style={{ minWidth: 0 }}>
|
<div className="flex flex-col">
|
||||||
{highlightedLines.map((line: string, i: number) => (
|
{highlightedLines.map((line: string, i: number) => (
|
||||||
<span
|
<span
|
||||||
key={i}
|
key={i}
|
||||||
|
|
@ -79,5 +94,6 @@ export function CodeBlock({ code, type, showLineNumbers = true }: CodeBlockProps
|
||||||
)}
|
)}
|
||||||
</pre>
|
</pre>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -73,7 +73,13 @@ export function StepDetailPanel({
|
||||||
|
|
||||||
// Sync state when step changes
|
// Sync state when step changes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
console.log('[StepDetailPanel] useEffect triggered, step?.id:', step?.id);
|
||||||
if (step) {
|
if (step) {
|
||||||
|
console.log('[StepDetailPanel] Syncing state with step:', {
|
||||||
|
stepId: step.id,
|
||||||
|
stepName: step.name,
|
||||||
|
stepContent: step.content?.substring(0, 50),
|
||||||
|
});
|
||||||
setNotes(step.notes || '');
|
setNotes(step.notes || '');
|
||||||
setEditContent(step.content || '');
|
setEditContent(step.content || '');
|
||||||
setIsEditing(false);
|
setIsEditing(false);
|
||||||
|
|
@ -83,6 +89,8 @@ export function StepDetailPanel({
|
||||||
}
|
}
|
||||||
}, [step?.id]);
|
}, [step?.id]);
|
||||||
|
|
||||||
|
console.log('[StepDetailPanel] Render - step:', step?.id, 'step.content:', step?.content?.substring(0, 50), 'editContent:', editContent?.substring(0, 50));
|
||||||
|
|
||||||
if (!step) return null;
|
if (!step) return null;
|
||||||
|
|
||||||
const isCustom = step.isCustom;
|
const isCustom = step.isCustom;
|
||||||
|
|
@ -181,7 +189,7 @@ export function StepDetailPanel({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Sheet open={isOpen} onOpenChange={onClose}>
|
<Sheet open={isOpen} onOpenChange={onClose}>
|
||||||
<SheetContent key={step?.id} className="w-[800px] sm:max-w-[800px] overflow-hidden">
|
<SheetContent key={step?.id} className="w-[600px] sm:max-w-[600px]">
|
||||||
<SheetHeader className="px-6">
|
<SheetHeader className="px-6">
|
||||||
<div className="flex items-start justify-between">
|
<div className="flex items-start justify-between">
|
||||||
<div>
|
<div>
|
||||||
|
|
@ -197,9 +205,8 @@ export function StepDetailPanel({
|
||||||
</div>
|
</div>
|
||||||
</SheetHeader>
|
</SheetHeader>
|
||||||
|
|
||||||
<div style={{ width: '752px', maxWidth: '752px' }}>
|
<ScrollArea className="h-[calc(100vh-180px)] mt-6 px-6">
|
||||||
<ScrollArea className="h-[calc(100vh-180px)] mt-6 px-6 w-full">
|
<div className="space-y-6">
|
||||||
<div className="space-y-6" style={{ width: '704px', maxWidth: '704px' }}>
|
|
||||||
{/* Status & Type */}
|
{/* Status & Type */}
|
||||||
<div className="flex items-center gap-4">
|
<div className="flex items-center gap-4">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
|
|
@ -219,7 +226,7 @@ export function StepDetailPanel({
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Content */}
|
{/* Content */}
|
||||||
<div className="min-w-0" style={{ maxWidth: '100%' }}>
|
<div>
|
||||||
<div className="flex items-center justify-between mb-2">
|
<div className="flex items-center justify-between mb-2">
|
||||||
<label className="text-sm font-medium text-slate-500">Content</label>
|
<label className="text-sm font-medium text-slate-500">Content</label>
|
||||||
{!isEditing && (
|
{!isEditing && (
|
||||||
|
|
@ -256,9 +263,7 @@ export function StepDetailPanel({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="overflow-hidden" style={{ maxWidth: '100%', width: '100%' }}>
|
|
||||||
<CodeBlock code={step.content} type={step.type} />
|
<CodeBlock code={step.content} type={step.type} />
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Show original content if overridden */}
|
{/* Show original content if overridden */}
|
||||||
|
|
@ -371,7 +376,6 @@ export function StepDetailPanel({
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</ScrollArea>
|
</ScrollArea>
|
||||||
</div>
|
|
||||||
</SheetContent>
|
</SheetContent>
|
||||||
</Sheet>
|
</Sheet>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,6 @@ function ScrollArea({
|
||||||
<ScrollAreaPrimitive.Viewport
|
<ScrollAreaPrimitive.Viewport
|
||||||
data-slot="scroll-area-viewport"
|
data-slot="scroll-area-viewport"
|
||||||
className="focus-visible:ring-ring/50 size-full rounded-[inherit] transition-[color,box-shadow] outline-none focus-visible:ring-[3px] focus-visible:outline-1"
|
className="focus-visible:ring-ring/50 size-full rounded-[inherit] transition-[color,box-shadow] outline-none focus-visible:ring-[3px] focus-visible:outline-1"
|
||||||
style={{ maxWidth: '100%' }}
|
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</ScrollAreaPrimitive.Viewport>
|
</ScrollAreaPrimitive.Viewport>
|
||||||
|
|
|
||||||
|
|
@ -82,23 +82,6 @@ export async function reorderSteps(
|
||||||
category: StepCategory,
|
category: StepCategory,
|
||||||
orderedIds: number[]
|
orderedIds: number[]
|
||||||
) {
|
) {
|
||||||
// Two-phase update to avoid unique constraint violations
|
|
||||||
// Phase 1: Move all steps to temporary high values (offset by 10000)
|
|
||||||
for (let i = 0; i < orderedIds.length; i++) {
|
|
||||||
await db.update(stepTemplates)
|
|
||||||
.set({ orderIndex: i + 10000 })
|
|
||||||
.where(eq(stepTemplates.id, orderedIds[i]));
|
|
||||||
|
|
||||||
// Also update customer steps
|
|
||||||
await db.update(customerSteps)
|
|
||||||
.set({ orderIndex: i + 10000 })
|
|
||||||
.where(and(
|
|
||||||
eq(customerSteps.templateId, orderedIds[i]),
|
|
||||||
eq(customerSteps.category, category)
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Phase 2: Move to final positions
|
|
||||||
for (let i = 0; i < orderedIds.length; i++) {
|
for (let i = 0; i < orderedIds.length; i++) {
|
||||||
await db.update(stepTemplates)
|
await db.update(stepTemplates)
|
||||||
.set({ orderIndex: i })
|
.set({ orderIndex: i })
|
||||||
|
|
@ -112,7 +95,6 @@ export async function reorderSteps(
|
||||||
eq(customerSteps.category, category)
|
eq(customerSteps.category, category)
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
revalidatePath(`/releases/${releaseId}/steps`);
|
revalidatePath(`/releases/${releaseId}/steps`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue