feat(mobile): add QrTemplate3 Pizzeria, fix PDF export for component system
This commit is contained in:
@@ -14,11 +14,10 @@ import * as Clipboard from "expo-clipboard";
|
||||
import ViewShot from "react-native-view-shot";
|
||||
import * as Sharing from "expo-sharing";
|
||||
import * as Print from "expo-print";
|
||||
import { SvgXml } from "react-native-svg";
|
||||
import { QR_STAND_PRESETS, type QrStandPreset } from "@menulio/shared";
|
||||
import { api } from "@/lib/api";
|
||||
import { getActiveRestaurant, type ActiveRestaurant } from "@/lib/active-restaurant";
|
||||
import { MOBILE_SVG_TEMPLATES } from "@/lib/svg-templates";
|
||||
import { COMPONENT_TEMPLATES, TEMPLATE_ASPECT_RATIOS } from "@/components/qr-templates";
|
||||
|
||||
interface QrResponse {
|
||||
id: string;
|
||||
@@ -42,54 +41,12 @@ export default function QrScreen() {
|
||||
const viewShotRef = useRef<any>(null);
|
||||
|
||||
const activePreset: QrStandPreset = (QR_STAND_PRESETS[selectedKey] || Object.values(QR_STAND_PRESETS)[0])!;
|
||||
const currentSvgRaw = MOBILE_SVG_TEMPLATES[selectedKey] || MOBILE_SVG_TEMPLATES["qr-4"] || Object.values(MOBILE_SVG_TEMPLATES)[0] || "";
|
||||
|
||||
// Inject the live QR code image directly into the SVG's #qr-placeholder group
|
||||
// Yorum satırlarını temizle + iç içe <g> taglarını doğru saymak için depth tracking kullan
|
||||
function buildSvgWithQr(svgRaw: string, pngBase64: string): string {
|
||||
// react-native-svg XML parser <!-- yorum satırlarını --> desteklemez
|
||||
// Ayrıca text node'lardaki & < gibi entity'leri decode et
|
||||
const clean = svgRaw
|
||||
.replace(/<!--[\s\S]*?-->/g, "")
|
||||
.replace(/&/g, "&")
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">")
|
||||
.replace(/'/g, "'")
|
||||
.replace(/"/g, '"');
|
||||
|
||||
|
||||
const startMarker = '<g id="qr-placeholder"';
|
||||
const startIdx = clean.indexOf(startMarker);
|
||||
if (startIdx === -1) return clean;
|
||||
|
||||
// İç içe <g> taglarını sayarak doğru kapanış </g>'yi bul
|
||||
let depth = 0;
|
||||
let i = startIdx;
|
||||
let endIdx = -1;
|
||||
while (i < clean.length - 3) {
|
||||
if (clean[i] === "<") {
|
||||
if (clean[i + 1] === "g" && (clean[i + 2] === " " || clean[i + 2] === ">")) {
|
||||
depth++;
|
||||
} else if (clean[i + 1] === "/" && clean[i + 2] === "g" && clean[i + 3] === ">") {
|
||||
depth--;
|
||||
if (depth === 0) {
|
||||
endIdx = i + 4; // </g> uzunluğu = 4
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
if (endIdx === -1) return clean;
|
||||
|
||||
const qrGroup = `<g id="qr-placeholder" transform="translate(90, 215)">
|
||||
<rect width="220" height="220" rx="20" fill="#FFFFFF"/>
|
||||
<image href="data:image/png;base64,${pngBase64}" x="10" y="10" width="200" height="200" preserveAspectRatio="xMidYMid meet"/>
|
||||
</g>`;
|
||||
|
||||
return clean.substring(0, startIdx) + qrGroup + clean.substring(endIdx);
|
||||
}
|
||||
// Component-based template sistemi
|
||||
const TemplateComponent = COMPONENT_TEMPLATES[selectedKey] ?? Object.values(COMPONENT_TEMPLATES)[0];
|
||||
const aspectRatio = TEMPLATE_ASPECT_RATIOS[selectedKey] ?? 1;
|
||||
const displayWidth = 300;
|
||||
const displayHeight = Math.round(displayWidth / aspectRatio);
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
@@ -153,19 +110,12 @@ export default function QrScreen() {
|
||||
}
|
||||
|
||||
async function handleExportPdf() {
|
||||
if (!qr || !active || !currentSvgRaw) return;
|
||||
if (!qr || !active) return;
|
||||
setExportingPdf(true);
|
||||
try {
|
||||
const qrImageUri = `data:image/png;base64,${qr.pngBase64}`;
|
||||
|
||||
// Inject QR code directly into the SVG for print-ready A5 PDF
|
||||
const svgWithQr = currentSvgRaw.replace(
|
||||
/<g id="qr-placeholder"[^>]*>[\s\S]*?<\/g>/,
|
||||
`<g id="qr-placeholder" transform="translate(90, 215)">
|
||||
<rect width="220" height="220" rx="20" fill="#FFFFFF" stroke="#E4E4E7" stroke-width="2"/>
|
||||
<image href="${qrImageUri}" x="10" y="10" width="200" height="200" preserveAspectRatio="xMidYMid meet" />
|
||||
</g>`
|
||||
);
|
||||
// ViewShot ile önce PNG al, sonra A5 HTML içine göm
|
||||
const pngUri = await viewShotRef.current?.capture();
|
||||
if (!pngUri) throw new Error("Görsel alınamadı");
|
||||
|
||||
const htmlContent = `
|
||||
<!DOCTYPE html>
|
||||
@@ -181,14 +131,14 @@ export default function QrScreen() {
|
||||
background: #000000;
|
||||
-webkit-print-color-adjust: exact;
|
||||
}
|
||||
.svg-wrap {
|
||||
.img-wrap {
|
||||
width: 140mm;
|
||||
height: 200mm;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
svg {
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
@@ -196,8 +146,8 @@ export default function QrScreen() {
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="svg-wrap">
|
||||
${svgWithQr}
|
||||
<div class="img-wrap">
|
||||
<img src="${pngUri}" />
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -220,6 +170,7 @@ export default function QrScreen() {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<View style={{ flex: 1, backgroundColor: "#FAF8F5", alignItems: "center", justifyContent: "center" }}>
|
||||
@@ -347,24 +298,23 @@ export default function QrScreen() {
|
||||
}}
|
||||
>
|
||||
<ViewShot ref={viewShotRef} options={{ format: "png", quality: 1.0 }}>
|
||||
{/* SVG with QR code injected directly — no overlay, pixel-perfect */}
|
||||
{/* Component-based template — no XML parsing, pixel-perfect */}
|
||||
<View
|
||||
style={{
|
||||
width: 300,
|
||||
height: 487.5,
|
||||
width: displayWidth,
|
||||
height: displayHeight,
|
||||
borderRadius: 24,
|
||||
overflow: "hidden",
|
||||
backgroundColor: "#0C0C0E",
|
||||
}}
|
||||
>
|
||||
{currentSvgRaw ? (
|
||||
<SvgXml
|
||||
xml={buildSvgWithQr(currentSvgRaw, qr.pngBase64)}
|
||||
width={300}
|
||||
height={487.5}
|
||||
{TemplateComponent ? (
|
||||
<TemplateComponent
|
||||
qrBase64={qr.pngBase64}
|
||||
width={displayWidth}
|
||||
height={displayHeight}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
</View>
|
||||
</ViewShot>
|
||||
</View>
|
||||
|
||||
@@ -0,0 +1,348 @@
|
||||
import * as React from "react";
|
||||
import Svg, {
|
||||
SvgProps,
|
||||
Defs,
|
||||
LinearGradient,
|
||||
Stop,
|
||||
Pattern,
|
||||
Path,
|
||||
G,
|
||||
Rect,
|
||||
Ellipse,
|
||||
Circle,
|
||||
Text,
|
||||
TSpan,
|
||||
Image,
|
||||
} from "react-native-svg";
|
||||
|
||||
interface QrTemplate3Props extends SvgProps {
|
||||
qrBase64?: string;
|
||||
}
|
||||
|
||||
const QrTemplate3 = ({ qrBase64, ...props }: QrTemplate3Props) => (
|
||||
<Svg
|
||||
viewBox="0 0 400 650"
|
||||
width={300}
|
||||
height={487.5}
|
||||
{...props}
|
||||
>
|
||||
<Defs>
|
||||
<LinearGradient id="card-grad-3" x1="0%" x2="0%" y1="0%" y2="100%">
|
||||
<Stop offset="0%" stopColor="#fdfbf5" />
|
||||
<Stop offset="50%" stopColor="#faf4e6" />
|
||||
<Stop offset="100%" stopColor="#f7eed8" />
|
||||
</LinearGradient>
|
||||
<LinearGradient id="oven-fire-3" x1="0%" x2="0%" y1="100%" y2="0%">
|
||||
<Stop offset="0%" stopColor="#c0392b" />
|
||||
<Stop offset="50%" stopColor="#e67e22" />
|
||||
<Stop offset="100%" stopColor="#f1c40f" />
|
||||
</LinearGradient>
|
||||
<LinearGradient id="wine-grad-3" x1="0%" x2="0%" y1="0%" y2="100%">
|
||||
<Stop offset="0%" stopColor="#7b1123" />
|
||||
<Stop offset="100%" stopColor="#4a0512" />
|
||||
</LinearGradient>
|
||||
<Pattern
|
||||
id="checkered-border-3"
|
||||
width={20}
|
||||
height={20}
|
||||
patternUnits="userSpaceOnUse"
|
||||
>
|
||||
<Path fill="#b92b27" d="M0 0h20v20H0z" />
|
||||
<Path fill="#fdf8ee" d="M0 0h10v10H0zm10 10h10v10H10z" />
|
||||
</Pattern>
|
||||
</Defs>
|
||||
|
||||
{/* Arka Plan */}
|
||||
<Path fill="url(#checkered-border-3)" d="M0 0h400v650H0z" />
|
||||
|
||||
{/* Kart */}
|
||||
<G id="layer-card">
|
||||
<Rect
|
||||
width={356}
|
||||
height={606}
|
||||
x={22}
|
||||
y={22}
|
||||
fill="url(#card-grad-3)"
|
||||
stroke="#b92b27"
|
||||
strokeWidth={2}
|
||||
rx={20}
|
||||
/>
|
||||
<Rect
|
||||
width={344}
|
||||
height={594}
|
||||
x={28}
|
||||
y={28}
|
||||
fill="none"
|
||||
stroke="#8c6239"
|
||||
strokeOpacity={0.5}
|
||||
strokeWidth={0.8}
|
||||
rx={15}
|
||||
/>
|
||||
<Rect
|
||||
width={140}
|
||||
height={24}
|
||||
x={130}
|
||||
y={34}
|
||||
fill="#faf4e6"
|
||||
stroke="#8c6239"
|
||||
strokeWidth={1.2}
|
||||
rx={12}
|
||||
/>
|
||||
</G>
|
||||
|
||||
{/* İkonlar */}
|
||||
<G id="layer-icons">
|
||||
<Path
|
||||
fill="none"
|
||||
stroke="#c5a880"
|
||||
strokeLinecap="round"
|
||||
strokeWidth={1.5}
|
||||
d="M68 88q12-13 28-2 6 5 10 2m226 0q-12-13-28-2-6 5-10 2"
|
||||
/>
|
||||
{/* Pizza Fırını */}
|
||||
<G id="pizza-oven" transform="translate(200 192)">
|
||||
<Rect
|
||||
width={88}
|
||||
height={6}
|
||||
x={-44}
|
||||
fill="#d7ccc8"
|
||||
stroke="#5d4037"
|
||||
rx={2}
|
||||
/>
|
||||
<Path
|
||||
fill="#d7ccc8"
|
||||
stroke="#5d4037"
|
||||
strokeWidth={1.5}
|
||||
d="M-40 0a40 38 0 0 1 80 0Z"
|
||||
/>
|
||||
<Path stroke="#8d6e63" d="m-30-25 7 6m6-15 4 8M0-38v9m17-5-4 8m17 1-7 6" />
|
||||
<Path fill="#3e2723" stroke="#271c19" d="M-28 0a28 26 0 0 1 56 0Z" />
|
||||
<Path fill="url(#oven-fire-3)" d="M-18 0q3-16 12-10 6-12 12-2 9-6 12 12Z" />
|
||||
<Path fill="#fff9c4" d="M-10 0q5-10 10-6 5-6 10 6Z" />
|
||||
</G>
|
||||
{/* Sol Malzemeler */}
|
||||
<G id="left-ingredients" transform="translate(108 185)">
|
||||
<Ellipse
|
||||
cx={-16}
|
||||
cy={6}
|
||||
fill="#d32f2f"
|
||||
stroke="#8e0000"
|
||||
strokeWidth={0.8}
|
||||
rx={14}
|
||||
ry={10}
|
||||
transform="rotate(-25 -16 6)"
|
||||
/>
|
||||
<Ellipse
|
||||
cx={-16}
|
||||
cy={6}
|
||||
fill="#e53935"
|
||||
rx={10}
|
||||
ry={6.5}
|
||||
transform="rotate(-25 -16 6)"
|
||||
/>
|
||||
<Ellipse
|
||||
cx={2}
|
||||
cy={7}
|
||||
fill="#c62828"
|
||||
stroke="#8e0000"
|
||||
strokeWidth={0.8}
|
||||
rx={13}
|
||||
ry={9}
|
||||
transform="rotate(-10 2 7)"
|
||||
/>
|
||||
<Ellipse cx={2} cy={7} fill="#e53935" rx={9} ry={5.5} transform="rotate(-10 2 7)" />
|
||||
<Circle cx={2} cy={7} r={1.5} fill="#fdd835" />
|
||||
<Circle cx={16} cy={-2} r={14} fill="#e53935" stroke="#b71c1c" strokeWidth={0.8} />
|
||||
<Ellipse cx={12} cy={-6} fill="#ff8a80" opacity={0.6} rx={4} ry={2} />
|
||||
<Path
|
||||
stroke="#2e7d32"
|
||||
strokeLinecap="round"
|
||||
strokeWidth={1.5}
|
||||
d="M16-16v3m0 0-5 2m5-2 5 2m-5-2-3-2m3 2 3-2"
|
||||
/>
|
||||
</G>
|
||||
{/* Sağ Malzemeler */}
|
||||
<G id="right-ingredients" transform="translate(290 182)">
|
||||
<Circle cx={-14} cy={2} r={15} fill="#e53935" stroke="#b71c1c" strokeWidth={0.8} />
|
||||
<Ellipse cx={-18} cy={-3} fill="#ff8a80" opacity={0.6} rx={4} ry={2} />
|
||||
<Path
|
||||
stroke="#2e7d32"
|
||||
strokeLinecap="round"
|
||||
strokeWidth={1.5}
|
||||
d="M-14-13v3m0 0-4 2m4-2 4 2"
|
||||
/>
|
||||
<Path fill="#2e7d32" stroke="#1b5e20" strokeWidth={0.8} d="M-2 0C8-16 26-12 24 2 22 14 6 10-2 0Z" />
|
||||
<Path fill="none" stroke="#4caf50" strokeWidth={0.7} d="M-2 0q14-4 26 2" />
|
||||
<Path fill="#388e3c" stroke="#1b5e20" strokeWidth={0.8} d="M8 6c10-6 24 4 18 16-8 6-18-4-18-16Z" />
|
||||
<Path fill="none" stroke="#81c784" strokeWidth={0.7} d="M8 6q12 6 18 16" />
|
||||
</G>
|
||||
{/* Alt Pizza Dilimi */}
|
||||
<G id="footer-pizza-slice" transform="rotate(-10 3278.565 44.969)">
|
||||
<Path fill="#deac68" stroke="#8d6e63" strokeWidth={1.2} d="m0 0 46-18c4 8 6 32 0 40Z" />
|
||||
<Path fill="#fbc02d" d="m2 0 40-15c3 7 5 25 0 33Z" />
|
||||
<Circle cx={28} cy={-5} r={5} fill="#c62828" stroke="#8e0000" strokeWidth={0.8} />
|
||||
<Circle cx={32} cy={8} r={4.5} fill="#c62828" stroke="#8e0000" strokeWidth={0.8} />
|
||||
<Circle cx={16} r={3.5} fill="#c62828" stroke="#8e0000" strokeWidth={0.8} />
|
||||
<Ellipse cx={23} cy={4} fill="#2e7d32" rx={2} ry={4} transform="rotate(30 23 4)" />
|
||||
<Ellipse cx={34} cy={-11} fill="#2e7d32" rx={2} ry={3} transform="rotate(-40 34 -11)" />
|
||||
</G>
|
||||
{/* Şarap Kadehi */}
|
||||
<G id="footer-wine-glass" transform="translate(345 562)">
|
||||
<Path fill="none" stroke="#71717a" strokeWidth={1.2} d="M-14-24C-15-8-11 4 0 6 11 4 15-8 14-24Z" />
|
||||
<Path fill="url(#wine-grad-3)" d="M-13-12C-11 0-7 4 0 5.5 7 4 11 0 13-12Z" />
|
||||
<Ellipse cy={-12} fill="#991b1b" rx={13} ry={2} />
|
||||
<Path stroke="#71717a" strokeWidth={1.4} d="M0 6v22" />
|
||||
<Ellipse cy={28} fill="#e4e4e7" stroke="#71717a" strokeWidth={1.2} rx={11} ry={2} />
|
||||
<Path fill="none" stroke="#fff" strokeLinecap="round" d="M-10-20c-2 8 1 18 6 23" opacity={0.6} />
|
||||
</G>
|
||||
{/* QR Köşe Aksanları */}
|
||||
<G id="qr-corner-accents">
|
||||
<Path fill="none" stroke="#2e7d32" strokeLinecap="round" strokeWidth={3} d="M78 227v-22h22" />
|
||||
<Path fill="none" stroke="#b92b27" strokeLinecap="round" strokeWidth={3} d="M322 227v-22h-22M78 423v22h22" />
|
||||
<Path fill="none" stroke="#2e7d32" strokeLinecap="round" strokeWidth={3} d="M322 423v22h-22" />
|
||||
</G>
|
||||
</G>
|
||||
|
||||
{/* QR Placeholder / Gerçek QR */}
|
||||
<G id="qr-placeholder" transform="translate(90 215)">
|
||||
<Rect width={220} height={220} fill="#fff" stroke="#e4e4e7" strokeWidth={2} rx={20} />
|
||||
{qrBase64 ? (
|
||||
<Image
|
||||
href={`data:image/png;base64,${qrBase64}`}
|
||||
x={10}
|
||||
y={10}
|
||||
width={200}
|
||||
height={200}
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
<Rect
|
||||
width={196}
|
||||
height={196}
|
||||
x={12}
|
||||
y={12}
|
||||
fill="#fafafa"
|
||||
stroke="#e4e4e7"
|
||||
strokeDasharray="4 3"
|
||||
rx={14}
|
||||
/>
|
||||
<Text
|
||||
x={110}
|
||||
y={115}
|
||||
fill="#71717a"
|
||||
fontFamily="Inter, Arial, sans-serif"
|
||||
fontSize={12}
|
||||
fontWeight={700}
|
||||
textAnchor="middle"
|
||||
>
|
||||
{"QR KOD ALANI"}
|
||||
</Text>
|
||||
</>
|
||||
)}
|
||||
</G>
|
||||
|
||||
{/* Metinler */}
|
||||
<G id="layer-texts">
|
||||
<Text
|
||||
x={200}
|
||||
y={50}
|
||||
fill="#18181b"
|
||||
fontFamily="Inter, Arial, sans-serif"
|
||||
fontSize={11}
|
||||
fontWeight={800}
|
||||
letterSpacing={1.2}
|
||||
textAnchor="middle"
|
||||
>
|
||||
{"MASA NO: 01"}
|
||||
</Text>
|
||||
<Text
|
||||
x={200}
|
||||
y={96}
|
||||
fill="#3e2723"
|
||||
fontFamily="'Playfair Display', Georgia, serif"
|
||||
fontSize={24}
|
||||
textAnchor="middle"
|
||||
>
|
||||
{"PIZZERIA"}
|
||||
</Text>
|
||||
<Text
|
||||
x={200}
|
||||
y={132}
|
||||
fill="#18181b"
|
||||
fontFamily="'Playfair Display', Georgia, serif"
|
||||
fontSize={31}
|
||||
fontWeight={700}
|
||||
letterSpacing={1}
|
||||
textAnchor="middle"
|
||||
>
|
||||
{"BELLA NAPOLI"}
|
||||
</Text>
|
||||
<Text
|
||||
x={200}
|
||||
y={222}
|
||||
fill="#2e7d32"
|
||||
fontFamily="Inter, Arial, sans-serif"
|
||||
fontSize={11.5}
|
||||
fontWeight={800}
|
||||
letterSpacing={1.8}
|
||||
textAnchor="middle"
|
||||
>
|
||||
{"AUTENTICA CUCINA ITALIANA"}
|
||||
</Text>
|
||||
<Text
|
||||
x={200}
|
||||
y={474}
|
||||
fill="#b92b27"
|
||||
fontFamily="Inter, Arial, sans-serif"
|
||||
fontSize={13}
|
||||
fontWeight={700}
|
||||
letterSpacing={1.5}
|
||||
textAnchor="middle"
|
||||
>
|
||||
{"MENÜYÜ GÖRMEK"}
|
||||
</Text>
|
||||
<Text
|
||||
x={200}
|
||||
y={494}
|
||||
fill="#b92b27"
|
||||
fontFamily="Inter, Arial, sans-serif"
|
||||
fontSize={13}
|
||||
fontWeight={700}
|
||||
letterSpacing={1.5}
|
||||
textAnchor="middle"
|
||||
>
|
||||
{"İÇİN OKUTUN"}
|
||||
</Text>
|
||||
<Text x={200} y={546} textAnchor="middle">
|
||||
<TSpan fill="#3e2723" fontFamily="Inter, Arial, sans-serif" fontSize={10} fontWeight={700}>
|
||||
{"WiFi: "}
|
||||
</TSpan>
|
||||
<TSpan fill="#5d4037" fontFamily="Inter, Arial, sans-serif" fontSize={10}>
|
||||
{"GourmetBistro"}
|
||||
</TSpan>
|
||||
<TSpan fill="#a1a1aa" fontFamily="Inter, Arial, sans-serif" fontSize={10}>
|
||||
{" | "}
|
||||
</TSpan>
|
||||
<TSpan fill="#3e2723" fontFamily="Inter, Arial, sans-serif" fontSize={10} fontWeight={700}>
|
||||
{"Şifre: "}
|
||||
</TSpan>
|
||||
<TSpan fill="#5d4037" fontFamily="Inter, Arial, sans-serif" fontSize={10}>
|
||||
{"lezzetli01"}
|
||||
</TSpan>
|
||||
</Text>
|
||||
<Text x={200} y={570} textAnchor="middle">
|
||||
<TSpan fill="#b92b27" fontFamily="Inter, Arial, sans-serif" fontSize={10} fontWeight={700}>
|
||||
{"BİZİ TAKİP EDİN"}
|
||||
</TSpan>
|
||||
<TSpan fill="#a1a1aa" fontFamily="Inter, Arial, sans-serif" fontSize={10}>
|
||||
{" | "}
|
||||
</TSpan>
|
||||
<TSpan fill="#5d4037" fontFamily="Inter, Arial, sans-serif" fontSize={10}>
|
||||
{"@gourmetbistro"}
|
||||
</TSpan>
|
||||
</Text>
|
||||
</G>
|
||||
</Svg>
|
||||
);
|
||||
|
||||
export default QrTemplate3;
|
||||
@@ -0,0 +1,213 @@
|
||||
import * as React from "react";
|
||||
import Svg, {
|
||||
SvgProps,
|
||||
Path,
|
||||
Rect,
|
||||
Circle,
|
||||
G,
|
||||
Text,
|
||||
Image,
|
||||
} from "react-native-svg";
|
||||
|
||||
interface QrTemplateLumiereProps extends SvgProps {
|
||||
qrBase64?: string;
|
||||
}
|
||||
|
||||
const QrTemplateLumiere = ({ qrBase64, ...props }: QrTemplateLumiereProps) => (
|
||||
<Svg
|
||||
viewBox="0 0 600 600"
|
||||
width={300}
|
||||
height={300}
|
||||
{...props}
|
||||
>
|
||||
<Path fill="#0b0d13" d="M0 0h600v600H0z" />
|
||||
<Rect
|
||||
width={560}
|
||||
height={560}
|
||||
x={20}
|
||||
y={20}
|
||||
fill="#132034"
|
||||
stroke="#d4af37"
|
||||
strokeWidth={1.5}
|
||||
rx={24}
|
||||
/>
|
||||
<Path
|
||||
fill="none"
|
||||
stroke="#d4af37"
|
||||
strokeOpacity={0.85}
|
||||
strokeWidth={1.5}
|
||||
d="M56 40h488a16 16 0 0 1 16 16v488a16 16 0 0 1-16 16H56a16 16 0 0 1-16-16V56a16 16 0 0 1 16-16Z"
|
||||
/>
|
||||
<Rect
|
||||
width={504}
|
||||
height={504}
|
||||
x={48}
|
||||
y={48}
|
||||
fill="none"
|
||||
stroke="#c5a059"
|
||||
strokeOpacity={0.45}
|
||||
strokeWidth={0.8}
|
||||
rx={8}
|
||||
/>
|
||||
{/* Köşe süslemeleri */}
|
||||
<Path
|
||||
fill="none"
|
||||
stroke="#d4af37"
|
||||
strokeWidth={1.5}
|
||||
d="M40 68a18 18 0 0 0 28-28"
|
||||
/>
|
||||
<Circle cx={52} cy={52} r={2} fill="#d4af37" />
|
||||
<Path
|
||||
fill="none"
|
||||
stroke="#d4af37"
|
||||
strokeWidth={1.5}
|
||||
d="M560 68a18 18 0 0 1-28-28"
|
||||
/>
|
||||
<Circle cx={548} cy={52} r={2} fill="#d4af37" />
|
||||
<Path
|
||||
fill="none"
|
||||
stroke="#d4af37"
|
||||
strokeWidth={1.5}
|
||||
d="M40 532a18 18 0 0 1 28 28"
|
||||
/>
|
||||
<Circle cx={52} cy={548} r={2} fill="#d4af37" />
|
||||
<Path
|
||||
fill="none"
|
||||
stroke="#d4af37"
|
||||
strokeWidth={1.5}
|
||||
d="M560 532a18 18 0 0 0-28 28"
|
||||
/>
|
||||
<Circle cx={548} cy={548} r={2} fill="#d4af37" />
|
||||
{/* Merkez amblem */}
|
||||
<G transform="translate(300 85)">
|
||||
<Path
|
||||
fill="none"
|
||||
stroke="#d4af37"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M0-26C-8-16-8-6 0 0c8-6 8-16 0-26Z"
|
||||
/>
|
||||
<Path fill="#d4af37" d="M0-18C-4-12-4-4 0 0c4-4 4-12 0-18" />
|
||||
<Path
|
||||
fill="none"
|
||||
stroke="#d4af37"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M-3 3c-13-2-25 7-21 21C-12 24-3 12-3 3Zm6 0c13-2 25 7 21 21C12 24 3 12 3 3Z"
|
||||
/>
|
||||
<Circle cy={10} r={1.8} fill="#d4af37" />
|
||||
</G>
|
||||
{/* Yatay çizgi */}
|
||||
<Path
|
||||
stroke="#d4af37"
|
||||
strokeOpacity={0.4}
|
||||
strokeWidth={0.8}
|
||||
d="M120 198h360"
|
||||
/>
|
||||
{/* QR Placeholder / Gerçek QR */}
|
||||
<G transform="translate(190 215)">
|
||||
<Rect
|
||||
width={220}
|
||||
height={220}
|
||||
fill="#fff"
|
||||
stroke="#d4af37"
|
||||
strokeWidth={2}
|
||||
rx={20}
|
||||
/>
|
||||
{qrBase64 ? (
|
||||
<Image
|
||||
href={`data:image/png;base64,${qrBase64}`}
|
||||
x={10}
|
||||
y={10}
|
||||
width={200}
|
||||
height={200}
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
<Rect
|
||||
width={196}
|
||||
height={196}
|
||||
x={12}
|
||||
y={12}
|
||||
fill="#fafafa"
|
||||
stroke="#d4af37"
|
||||
strokeDasharray="4 3"
|
||||
strokeOpacity={0.4}
|
||||
rx={14}
|
||||
/>
|
||||
<Text
|
||||
x={110}
|
||||
y={115}
|
||||
fill="#71717a"
|
||||
fontFamily="Inter, Arial, sans-serif"
|
||||
fontSize={12}
|
||||
fontWeight={600}
|
||||
textAnchor="middle"
|
||||
>
|
||||
{"QR KOD ALANI"}
|
||||
</Text>
|
||||
</>
|
||||
)}
|
||||
</G>
|
||||
{/* Metinler */}
|
||||
<G textAnchor="middle">
|
||||
<Text
|
||||
x={300}
|
||||
y={152}
|
||||
fill="#d4af37"
|
||||
fontFamily="'Playfair Display', 'Cinzel', 'Didot', Georgia, serif"
|
||||
fontSize={38}
|
||||
fontWeight={500}
|
||||
letterSpacing={6}
|
||||
>
|
||||
{"LUMI\xC8RE"}
|
||||
</Text>
|
||||
<Text
|
||||
x={300}
|
||||
y={180}
|
||||
fill="#e5c378"
|
||||
fontFamily="Inter, Arial, sans-serif"
|
||||
fontSize={11}
|
||||
fontWeight={600}
|
||||
letterSpacing={4}
|
||||
>
|
||||
{"RESTAURANT & BAR | MODERN CUISINE"}
|
||||
</Text>
|
||||
<Text
|
||||
x={300}
|
||||
y={472}
|
||||
fill="#d4af37"
|
||||
fontFamily="Inter, Arial, sans-serif"
|
||||
fontSize={13}
|
||||
fontWeight={600}
|
||||
letterSpacing={3}
|
||||
>
|
||||
{"SCAN WITH CAMERA"}
|
||||
</Text>
|
||||
<Text
|
||||
x={300}
|
||||
y={508}
|
||||
fill="#e2ddd2"
|
||||
fontFamily="Inter, Arial, sans-serif"
|
||||
fontSize={10.5}
|
||||
fontWeight={400}
|
||||
letterSpacing={1.2}
|
||||
>
|
||||
{"Reservations: LumiereDining.com | 240-555-0199"}
|
||||
</Text>
|
||||
<Text
|
||||
x={300}
|
||||
y={528}
|
||||
fill="#c5a059"
|
||||
fontFamily="Inter, Arial, sans-serif"
|
||||
fontSize={9.5}
|
||||
fontWeight={500}
|
||||
letterSpacing={1.8}
|
||||
>
|
||||
{"TABLE 01 \u2022 WIFI: Lumiere_Guest"}
|
||||
</Text>
|
||||
</G>
|
||||
</Svg>
|
||||
);
|
||||
|
||||
export default QrTemplateLumiere;
|
||||
@@ -0,0 +1,23 @@
|
||||
import type React from "react";
|
||||
import type { SvgProps } from "react-native-svg";
|
||||
import QrTemplateLumiere from "./QrTemplateLumiere";
|
||||
import QrTemplate3 from "./QrTemplate3";
|
||||
|
||||
export interface QrTemplateProps extends SvgProps {
|
||||
qrBase64?: string;
|
||||
}
|
||||
|
||||
// Yeni component eklemek için buraya import edip COMPONENT_TEMPLATES'e kaydet
|
||||
export const COMPONENT_TEMPLATES: Record<
|
||||
string,
|
||||
React.FC<QrTemplateProps>
|
||||
> = {
|
||||
"qr-lumiere": QrTemplateLumiere,
|
||||
"qr-3": QrTemplate3,
|
||||
};
|
||||
|
||||
// Her template'in aspect ratio'su (width/height)
|
||||
export const TEMPLATE_ASPECT_RATIOS: Record<string, number> = {
|
||||
"qr-lumiere": 1, // 600x600 = 1:1
|
||||
"qr-3": 400 / 650, // 400x650 = standart kart
|
||||
};
|
||||
Reference in New Issue
Block a user