Roger that, Dragon — we’ll ditch the hover/handle system and make rotation dead-simple with sliders (one under each text field). Slide → text rotates in real time. Clean and stable.

Here’s a full drop-in replacement for client/src/pages/Admin/AdminUploadLogoTemplate.tsx implementing:

Rotation via sliders (-180° … +180°) for brand-name, tagline, est-year

Click-and-drag to move text positions (kept — it was working)

No hover detection, no handles, no flicker

Clear UI with live rotation readouts

Paste this over your file:

// client/src/pages/Admin/AdminUploadLogoTemplate.tsx
import React, { useMemo, useRef, useState } from "react";

type TextId = "brand-name" | "tagline" | "est-year";

type TextPositions = Record<TextId, { x: number; y: number }>;
type TextRotations = Record<TextId, number>;

const initialPositions: TextPositions = {
  "brand-name": { x: 110, y: 110 },
  tagline: { x: 110, y: 160 },
  "est-year": { x: 110, y: 210 },
};

const initialRotations: TextRotations = {
  "brand-name": 0,
  tagline: 0,
  "est-year": 0,
};

const boxStyle: React.CSSProperties = {
  width: 400,
  height: 300,
  background: "#f8fafc",
  border: "1px solid #e5e7eb",
  borderRadius: 12,
  position: "relative",
  overflow: "hidden",
};

const textBoxBase: React.CSSProperties = {
  fontFamily:
    "Inter, ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial",
  padding: "6px 10px",
  borderRadius: 6,
  background: "rgba(255,255,255,0.9)",
  border: "1px solid rgba(0,0,0,0.2)",
  boxShadow: "0 2px 4px rgba(0,0,0,0.1)",
  cursor: "move",
  userSelect: "none",
  position: "relative",
  transformOrigin: "center center",
};

export default function AdminUploadLogoTemplate() {
  // Text values (bind these to your form / right-side panel)
  const [brandName, setBrandName] = useState("Name Attorney's At Law");
  const [tagline, setTagline] = useState("Established Year");
  const [estYear, setEstYear] = useState("2025");

  // Positions & rotations
  const [textPositions, setTextPositions] =
    useState<TextPositions>(initialPositions);
  const [textRotations, setTextRotations] =
    useState<TextRotations>(initialRotations);

  // Drag state
  const [isDragging, setIsDragging] = useState<TextId | null>(null);
  const dragOffset = useRef<{ dx: number; dy: number }>({ dx: 0, dy: 0 });

  const previewRef = useRef<HTMLDivElement | null>(null);

  // Move text (drag)
  const handleTextMouseDown = (e: React.MouseEvent, id: TextId) => {
    e.preventDefault();
    e.stopPropagation();
    const container = previewRef.current;
    if (!container) return;

    const rect = container.getBoundingClientRect();
    setIsDragging(id);

    const pos = textPositions[id];
    dragOffset.current = {
      dx: e.clientX - (rect.left + pos.x),
      dy: e.clientY - (rect.top + pos.y),
    };

    const onMove = (ev: MouseEvent) => {
      const r = container.getBoundingClientRect();
      const x = ev.clientX - r.left - dragOffset.current.dx;
      const y = ev.clientY - r.top - dragOffset.current.dy;

      setTextPositions((prev) => ({
        ...prev,
        [id]: { x, y },
      }));
    };

    const onUp = () => {
      setIsDragging(null);
      window.removeEventListener("mousemove", onMove);
      window.removeEventListener("mouseup", onUp);
    };

    window.addEventListener("mousemove", onMove);
    window.addEventListener("mouseup", onUp);
  };

  // Rotation via sliders
  const setRotation = (id: TextId, deg: number) => {
    setTextRotations((prev) => ({ ...prev, [id]: deg }));
  };

  // Convenience map for rendering
  const textMap = useMemo(
    () => [
      {
        id: "brand-name" as TextId,
        label: "Brand Name",
        value: brandName,
        setValue: setBrandName,
        style: { fontWeight: 700, fontSize: 18 } as React.CSSProperties,
      },
      {
        id: "tagline" as TextId,
        label: "Tagline",
        value: tagline,
        setValue: setTagline,
        style: { fontWeight: 500, fontSize: 14 } as React.CSSProperties,
      },
      {
        id: "est-year" as TextId,
        label: "Est. Year",
        value: estYear,
        setValue: setEstYear,
        style: { fontWeight: 600, fontSize: 14, letterSpacing: 1 } as React.CSSProperties,
      },
    ],
    [brandName, tagline, estYear]
  );

  return (
    <div className="p-6">
      <h1 className="text-2xl font-semibold mb-4">Upload Logo Template</h1>

      <div className="grid grid-cols-1 lg:grid-cols-2 gap-16">
        {/* LEFT: Preview */}
        <div>
          <h2 className="font-medium text-gray-700 mb-2">Preview</h2>
          <div
            ref={previewRef}
            style={boxStyle}
            className="select-none relative overflow-hidden"
          >
            {/* Your uploaded art would be underneath */}
            <div
              style={{
                position: "absolute",
                inset: 0,
                display: "grid",
                placeItems: "center",
                color: "#cbd5e1",
                fontSize: 20,
              }}
            >
              <span>Logo canvas</span>
            </div>

            {/* Text items (draggable; rotate via slider) */}
            {textMap.map(({ id, value, style }) => {
              const pos = textPositions[id];
              const rot = textRotations[id] || 0;
              const dragging = isDragging === id;

              return (
                <div
                  key={id}
                  className="absolute"
                  style={{
                    left: pos.x,
                    top: pos.y,
                    transform: `rotate(${rot}deg)`,
                    transformOrigin: "center center",
                    pointerEvents: "auto",
                  }}
                >
                  <div
                    className="text-box"
                    style={{
                      ...textBoxBase,
                      ...(style || {}),
                      border: dragging
                        ? "2px dashed #3b82f6"
                        : textBoxBase.border,
                      background: dragging
                        ? "rgba(59, 130, 246, 0.12)"
                        : textBoxBase.background,
                      transform: dragging ? "scale(1.05)" : "scale(1)",
                    }}
                    onMouseDown={(e) => handleTextMouseDown(e, id)}
                    title="Click & drag to move"
                  >
                    {value}
                  </div>
                </div>
              );
            })}
          </div>
        </div>

        {/* RIGHT: Controls */}
        <div className="space-y-8">
          {textMap.map(({ id, label, value, setValue }) => (
            <div key={id} className="space-y-3">
              <label className="block text-sm font-medium text-gray-700">
                {label}
              </label>
              <input
                className="w-full rounded-md border border-gray-300 px-3 py-2"
                value={value}
                onChange={(e) => setValue(e.target.value)}
              />

              {/* Rotation slider */}
              <div>
                <div className="flex items-center justify-between text-sm text-gray-600 mb-1">
                  <span>Rotation</span>
                  <span>{Math.round(textRotations[id] || 0)}°</span>
                </div>
                <input
                  type="range"
                  min={-180}
                  max={180}
                  step={1}
                  value={textRotations[id] || 0}
                  onChange={(e) => setRotation(id, Number(e.target.value))}
                  className="w-full"
                />
                <div className="flex justify-between text-xs text-gray-400">
                  <span>-180°</span>
                  <span>0°</span>
                  <span>+180°</span>
                </div>
              </div>
            </div>
          ))}

          {/* Quick reset */}
          <div className="pt-2">
            <button
              className="inline-flex items-center rounded-md bg-gray-800 text-white px-3 py-2 text-sm hover:bg-gray-900"
              onClick={() => {
                setTextPositions(initialPositions);
                setTextRotations(initialRotations);
              }}
            >
              Reset Positions & Rotations
            </button>
          </div>

          <div className="text-xs text-gray-500 pt-4">
            Tip: Drag a text block to move it. Use its slider to rotate.
          </div>
        </div>
      </div>
    </div>
  );
}

Notes for your team

Rotation is now pure state via sliders — no hover observers, no elementFromPoint, no flicker.

We kept drag-to-move exactly as before (stable, simple).

This file is self-contained. If your app has separate “PNG preview” vs “SVG preview” branches, you can keep this logic identical in both — the slider still writes to textRotations[id], and the wrapper applies transform: rotate(...) around the text group.