mirror of
https://github.com/R0m1k3/Socialflow.git
synced 2026-10-21 14:08:10 +02:00
Update image editor to display currency symbol correctly after price
Remove ribbon generation functionality and adjust currency symbol placement in image editor. Replit-Commit-Author: Agent Replit-Commit-Session-Id: ae4037a0-2a6f-4530-9bac-79b543286bda Replit-Commit-Checkpoint-Type: full_checkpoint Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/397bca8c-984f-43ff-841a-10897aeb8140/ae4037a0-2a6f-4530-9bac-79b543286bda/mIWJXC9
This commit is contained in:
1 parent
afe35de610
commit
dd43753d6d
5 files changed
+3
-142
No files matched your search
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 69 KiB |
@@ -89,10 +89,6 @@ export default function ImageEditor() {
|
|||||||
const previewRef = useRef<HTMLDivElement>(null);
|
const previewRef = useRef<HTMLDivElement>(null);
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
|
|
||||||
// State for generated ribbon overlay
|
|
||||||
const [ribbonPublicId, setRibbonPublicId] = useState<string | null>(null);
|
|
||||||
const [isGeneratingRibbon, setIsGeneratingRibbon] = useState(false);
|
|
||||||
|
|
||||||
// Save edited image mutation - DOM capture with html-to-image
|
// Save edited image mutation - DOM capture with html-to-image
|
||||||
const saveImageMutation = useMutation({
|
const saveImageMutation = useMutation({
|
||||||
mutationFn: async () => {
|
mutationFn: async () => {
|
||||||
@@ -159,34 +155,6 @@ export default function ImageEditor() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Generate ribbon when settings change
|
|
||||||
useEffect(() => {
|
|
||||||
if (!ribbon.enabled || !ribbon.text) {
|
|
||||||
setRibbonPublicId(null);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const generateRibbon = async () => {
|
|
||||||
setIsGeneratingRibbon(true);
|
|
||||||
try {
|
|
||||||
const response = await apiRequest("POST", "/api/media/generate-ribbon", {
|
|
||||||
text: ribbon.text,
|
|
||||||
color: ribbon.color,
|
|
||||||
position: ribbon.position
|
|
||||||
});
|
|
||||||
const data = await response.json();
|
|
||||||
setRibbonPublicId(data.publicId);
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error generating ribbon:", error);
|
|
||||||
} finally {
|
|
||||||
setIsGeneratingRibbon(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Debounce to avoid too many requests
|
|
||||||
const timer = setTimeout(generateRibbon, 500);
|
|
||||||
return () => clearTimeout(timer);
|
|
||||||
}, [ribbon.enabled, ribbon.text, ribbon.color, ribbon.position]);
|
|
||||||
|
|
||||||
// Generate Cloudinary transformation URL
|
// Generate Cloudinary transformation URL
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -537,13 +505,13 @@ export default function ImageEditor() {
|
|||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
<RadioGroupItem value="red" id="price-red" data-testid="radio-price-red" />
|
<RadioGroupItem value="red" id="price-red" data-testid="radio-price-red" />
|
||||||
<Label htmlFor="price-red" className="flex items-center gap-2">
|
<Label htmlFor="price-red" className="flex items-center gap-2">
|
||||||
Rouge <Badge className="bg-red-600">€19.99</Badge>
|
Rouge <Badge className="bg-red-600">19.99€</Badge>
|
||||||
</Label>
|
</Label>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
<RadioGroupItem value="yellow" id="price-yellow" data-testid="radio-price-yellow" />
|
<RadioGroupItem value="yellow" id="price-yellow" data-testid="radio-price-yellow" />
|
||||||
<Label htmlFor="price-yellow" className="flex items-center gap-2">
|
<Label htmlFor="price-yellow" className="flex items-center gap-2">
|
||||||
Jaune <Badge className="bg-yellow-500">€19.99</Badge>
|
Jaune <Badge className="bg-yellow-500">19.99€</Badge>
|
||||||
</Label>
|
</Label>
|
||||||
</div>
|
</div>
|
||||||
</RadioGroup>
|
</RadioGroup>
|
||||||
@@ -701,7 +669,7 @@ export default function ImageEditor() {
|
|||||||
}`}
|
}`}
|
||||||
style={{ fontSize: `${priceBadge.size}px` }}
|
style={{ fontSize: `${priceBadge.size}px` }}
|
||||||
>
|
>
|
||||||
€{priceBadge.price}
|
{priceBadge.price}€
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1341,26 +1341,6 @@ export async function registerRoutes(app: Express): Promise<Server> {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Generate ribbon overlay
|
|
||||||
app.post("/api/media/generate-ribbon", requireAuth, async (req, res) => {
|
|
||||||
const { text, color, position } = req.body;
|
|
||||||
|
|
||||||
if (!text || !color || !position) {
|
|
||||||
return res.status(400).json({ error: "Text, color and position are required" });
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const { RibbonGenerator } = await import("./services/ribbonGenerator");
|
|
||||||
const generator = RibbonGenerator.getInstance();
|
|
||||||
|
|
||||||
const publicId = await generator.generateTriangularRibbon(text, color, position);
|
|
||||||
res.json({ publicId });
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error generating ribbon:", error);
|
|
||||||
res.status(500).json({ error: "Failed to generate ribbon overlay" });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const httpServer = createServer(app);
|
const httpServer = createServer(app);
|
||||||
|
|
||||||
return httpServer;
|
return httpServer;
|
||||||
|
|||||||
@@ -1,87 +0,0 @@
|
|||||||
import { createCanvas } from "canvas";
|
|
||||||
import cloudinary from "cloudinary";
|
|
||||||
|
|
||||||
export class RibbonGenerator {
|
|
||||||
private static instance: RibbonGenerator;
|
|
||||||
|
|
||||||
static getInstance(): RibbonGenerator {
|
|
||||||
if (!RibbonGenerator.instance) {
|
|
||||||
RibbonGenerator.instance = new RibbonGenerator();
|
|
||||||
}
|
|
||||||
return RibbonGenerator.instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate a triangular corner ribbon
|
|
||||||
async generateTriangularRibbon(
|
|
||||||
text: string,
|
|
||||||
color: "red" | "yellow",
|
|
||||||
position: "north_west" | "north_east"
|
|
||||||
): Promise<string> {
|
|
||||||
const width = 200;
|
|
||||||
const height = 200;
|
|
||||||
|
|
||||||
const canvas = createCanvas(width, height);
|
|
||||||
const ctx = canvas.getContext("2d");
|
|
||||||
|
|
||||||
// Clear canvas with transparency
|
|
||||||
ctx.clearRect(0, 0, width, height);
|
|
||||||
|
|
||||||
// Set ribbon color
|
|
||||||
const ribbonColor = color === "red" ? "#FF0000" : "#FFC107";
|
|
||||||
ctx.fillStyle = ribbonColor;
|
|
||||||
|
|
||||||
// Draw triangle based on position
|
|
||||||
ctx.beginPath();
|
|
||||||
if (position === "north_west") {
|
|
||||||
// Triangle in top-left corner
|
|
||||||
ctx.moveTo(0, 0);
|
|
||||||
ctx.lineTo(150, 0);
|
|
||||||
ctx.lineTo(0, 150);
|
|
||||||
} else {
|
|
||||||
// Triangle in top-right corner
|
|
||||||
ctx.moveTo(width, 0);
|
|
||||||
ctx.lineTo(width - 150, 0);
|
|
||||||
ctx.lineTo(width, 150);
|
|
||||||
}
|
|
||||||
ctx.closePath();
|
|
||||||
ctx.fill();
|
|
||||||
|
|
||||||
// Add text on the diagonal
|
|
||||||
ctx.save();
|
|
||||||
ctx.fillStyle = "white";
|
|
||||||
ctx.font = "bold 24px Arial";
|
|
||||||
ctx.textAlign = "center";
|
|
||||||
ctx.textBaseline = "middle";
|
|
||||||
|
|
||||||
if (position === "north_west") {
|
|
||||||
// Rotate for northwest corner
|
|
||||||
ctx.translate(50, 50);
|
|
||||||
ctx.rotate(-Math.PI / 4);
|
|
||||||
} else {
|
|
||||||
// Rotate for northeast corner
|
|
||||||
ctx.translate(width - 50, 50);
|
|
||||||
ctx.rotate(Math.PI / 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.fillText(text, 0, 0);
|
|
||||||
ctx.restore();
|
|
||||||
|
|
||||||
// Convert to base64
|
|
||||||
const buffer = canvas.toBuffer("image/png");
|
|
||||||
const base64 = `data:image/png;base64,${buffer.toString("base64")}`;
|
|
||||||
|
|
||||||
// Upload to Cloudinary
|
|
||||||
try {
|
|
||||||
const result = await cloudinary.v2.uploader.upload(base64, {
|
|
||||||
folder: "ribbon-overlays",
|
|
||||||
public_id: `ribbon_${text}_${color}_${position}_${Date.now()}`,
|
|
||||||
resource_type: "image"
|
|
||||||
});
|
|
||||||
|
|
||||||
return result.public_id;
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error uploading ribbon to Cloudinary:", error);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in new issue
Block a user