feat(mobile): render exact SVG vector templates in mobile QR stand screen
This commit is contained in:
@@ -35,6 +35,7 @@
|
|||||||
"react-native-purchases": "^8.2.0",
|
"react-native-purchases": "^8.2.0",
|
||||||
"react-native-safe-area-context": "4.12.0",
|
"react-native-safe-area-context": "4.12.0",
|
||||||
"react-native-screens": "~4.4.0",
|
"react-native-screens": "~4.4.0",
|
||||||
|
"react-native-svg": "^15.15.5",
|
||||||
"react-native-url-polyfill": "^2.0.0",
|
"react-native-url-polyfill": "^2.0.0",
|
||||||
"react-native-view-shot": "^5.1.1"
|
"react-native-view-shot": "^5.1.1"
|
||||||
},
|
},
|
||||||
|
|||||||
+128
-302
@@ -15,9 +15,11 @@ import * as Clipboard from "expo-clipboard";
|
|||||||
import ViewShot from "react-native-view-shot";
|
import ViewShot from "react-native-view-shot";
|
||||||
import * as Sharing from "expo-sharing";
|
import * as Sharing from "expo-sharing";
|
||||||
import * as Print from "expo-print";
|
import * as Print from "expo-print";
|
||||||
|
import { SvgXml } from "react-native-svg";
|
||||||
import { QR_STAND_PRESETS, type QrStandPreset } from "@menulio/shared";
|
import { QR_STAND_PRESETS, type QrStandPreset } from "@menulio/shared";
|
||||||
import { api } from "@/lib/api";
|
import { api } from "@/lib/api";
|
||||||
import { getActiveRestaurant, type ActiveRestaurant } from "@/lib/active-restaurant";
|
import { getActiveRestaurant, type ActiveRestaurant } from "@/lib/active-restaurant";
|
||||||
|
import { MOBILE_SVG_TEMPLATES } from "@/lib/svg-templates";
|
||||||
|
|
||||||
interface QrResponse {
|
interface QrResponse {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -28,13 +30,12 @@ interface QrResponse {
|
|||||||
|
|
||||||
export default function QrScreen() {
|
export default function QrScreen() {
|
||||||
const [active, setActive] = useState<ActiveRestaurant | null>(null);
|
const [active, setActive] = useState<ActiveRestaurant | null>(null);
|
||||||
const [restaurantName, setRestaurantName] = useState<string>("THE URBAN BURGER");
|
const [restaurantName, setRestaurantName] = useState<string>("Gourmet Bistro");
|
||||||
const [qr, setQr] = useState<QrResponse | null>(null);
|
const [qr, setQr] = useState<QrResponse | null>(null);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
const [copied, setCopied] = useState(false);
|
const [copied, setCopied] = useState(false);
|
||||||
const [selectedKey, setSelectedKey] = useState<string>("qr-4");
|
const [selectedKey, setSelectedKey] = useState<string>("qr-4");
|
||||||
const [categories, setCategories] = useState<string[]>([]);
|
|
||||||
const [exportingPng, setExportingPng] = useState(false);
|
const [exportingPng, setExportingPng] = useState(false);
|
||||||
const [exportingPdf, setExportingPdf] = useState(false);
|
const [exportingPdf, setExportingPdf] = useState(false);
|
||||||
|
|
||||||
@@ -42,6 +43,7 @@ export default function QrScreen() {
|
|||||||
const viewShotRef = useRef<any>(null);
|
const viewShotRef = useRef<any>(null);
|
||||||
|
|
||||||
const activePreset: QrStandPreset = (QR_STAND_PRESETS[selectedKey] || Object.values(QR_STAND_PRESETS)[0])!;
|
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] || "";
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
(async () => {
|
(async () => {
|
||||||
@@ -62,18 +64,6 @@ export default function QrScreen() {
|
|||||||
} catch {
|
} catch {
|
||||||
// fallback
|
// fallback
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch categories for preview
|
|
||||||
if (activeRest.menuId) {
|
|
||||||
try {
|
|
||||||
const menuRes = await api.get<{ categories: { name: string }[] }>(`/menus/${activeRest.menuId}`);
|
|
||||||
if (menuRes?.categories?.length) {
|
|
||||||
setCategories(menuRes.categories.map((c) => c.name));
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
// ignore fallback
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setError(err instanceof Error ? err.message : "QR oluşturulamadı");
|
setError(err instanceof Error ? err.message : "QR oluşturulamadı");
|
||||||
} finally {
|
} finally {
|
||||||
@@ -82,9 +72,6 @@ export default function QrScreen() {
|
|||||||
})();
|
})();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const displayCategories =
|
|
||||||
categories.length > 0 ? categories.slice(0, 4) : (activePreset.categories || []);
|
|
||||||
|
|
||||||
async function handleCopy() {
|
async function handleCopy() {
|
||||||
if (!qr?.targetUrl) return;
|
if (!qr?.targetUrl) return;
|
||||||
await Clipboard.setStringAsync(qr.targetUrl);
|
await Clipboard.setStringAsync(qr.targetUrl);
|
||||||
@@ -120,21 +107,19 @@ export default function QrScreen() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function handleExportPdf() {
|
async function handleExportPdf() {
|
||||||
if (!qr || !active) return;
|
if (!qr || !active || !currentSvgRaw) return;
|
||||||
setExportingPdf(true);
|
setExportingPdf(true);
|
||||||
try {
|
try {
|
||||||
const restTitle = restaurantName.toUpperCase();
|
|
||||||
const qrImageUri = `data:image/png;base64,${qr.pngBase64}`;
|
const qrImageUri = `data:image/png;base64,${qr.pngBase64}`;
|
||||||
const catsHtml = displayCategories
|
|
||||||
.map(
|
// Inject QR code directly into the SVG for print-ready A5 PDF
|
||||||
(c, i) =>
|
const svgWithQr = currentSvgRaw.replace(
|
||||||
`<div style="padding: 5px 0;">${c}</div>${
|
/<g id="qr-placeholder"[^>]*>[\s\S]*?<\/g>/,
|
||||||
i < displayCategories.length - 1
|
`<g id="qr-placeholder" transform="translate(90, 215)">
|
||||||
? '<div style="height: 1px; background: ' + activePreset.dividerColor + '; margin: 2px 0;"></div>'
|
<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>`
|
||||||
)
|
);
|
||||||
.join("");
|
|
||||||
|
|
||||||
const htmlContent = `
|
const htmlContent = `
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
@@ -143,69 +128,30 @@ export default function QrScreen() {
|
|||||||
<meta charset="utf-8"/>
|
<meta charset="utf-8"/>
|
||||||
<style>
|
<style>
|
||||||
@page { size: A5 portrait; margin: 0; }
|
@page { size: A5 portrait; margin: 0; }
|
||||||
|
* { box-sizing: border-box; }
|
||||||
body {
|
body {
|
||||||
margin: 0; padding: 0; width: 148mm; height: 210mm;
|
margin: 0; padding: 0; width: 148mm; height: 210mm;
|
||||||
box-sizing: border-box;
|
|
||||||
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
|
||||||
background: #1c3e2b;
|
|
||||||
display: flex; align-items: center; justify-content: center;
|
display: flex; align-items: center; justify-content: center;
|
||||||
|
background: #000000;
|
||||||
-webkit-print-color-adjust: exact;
|
-webkit-print-color-adjust: exact;
|
||||||
}
|
}
|
||||||
.plaque {
|
.svg-wrap {
|
||||||
width: 125mm; height: 185mm;
|
width: 140mm;
|
||||||
background: ${activePreset.plaqueColor};
|
height: 200mm;
|
||||||
border-radius: 32px;
|
display: flex;
|
||||||
padding: 24px; box-sizing: border-box;
|
align-items: center;
|
||||||
display: flex; flex-direction: column; align-items: center; text-align: center;
|
justify-content: center;
|
||||||
color: ${activePreset.headerText};
|
|
||||||
position: relative;
|
|
||||||
box-shadow: 0 20px 50px rgba(0,0,0,0.5);
|
|
||||||
}
|
}
|
||||||
.inner-border {
|
svg {
|
||||||
position: absolute; top: 10px; left: 10px; right: 10px; bottom: 10px;
|
width: 100%;
|
||||||
border: 1px solid ${activePreset.borderAccent};
|
height: 100%;
|
||||||
border-radius: 24px; pointer-events: none;
|
object-fit: contain;
|
||||||
}
|
}
|
||||||
.badge {
|
|
||||||
border: 2px solid ${activePreset.logoBorder};
|
|
||||||
background: ${activePreset.logoBg};
|
|
||||||
color: ${activePreset.accentText};
|
|
||||||
padding: 6px 18px; border-radius: 12px;
|
|
||||||
font-weight: 900; font-size: 16px; letter-spacing: 2px;
|
|
||||||
margin-bottom: 14px; display: inline-flex; align-items: center; gap: 6px;
|
|
||||||
}
|
|
||||||
.title {
|
|
||||||
font-size: 22px; font-weight: 900; letter-spacing: 1px;
|
|
||||||
line-height: 1.1; margin-bottom: 18px; color: #ffffff;
|
|
||||||
}
|
|
||||||
.qr-box {
|
|
||||||
background: ${activePreset.qrBg};
|
|
||||||
padding: 16px; border-radius: 24px;
|
|
||||||
display: inline-block; margin-bottom: 14px;
|
|
||||||
box-shadow: 0 10px 25px rgba(0,0,0,0.2);
|
|
||||||
}
|
|
||||||
.qr-img { width: 160px; height: 160px; display: block; border-radius: 8px; }
|
|
||||||
.sub { font-size: 12px; opacity: 0.9; margin-bottom: 16px; color: ${activePreset.subText}; font-weight: 600; }
|
|
||||||
.footer-wrap { width: 100%; position: relative; }
|
|
||||||
.icon-left { position: absolute; left: 10px; bottom: 15px; color: ${activePreset.accentText}; font-size: 28px; }
|
|
||||||
.icon-right { position: absolute; right: 10px; bottom: 15px; color: ${activePreset.accentText}; font-size: 28px; }
|
|
||||||
.cats { font-size: 14px; font-weight: 900; letter-spacing: 2px; width: 75%; margin: 0 auto; color: #ffffff; }
|
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="plaque">
|
<div class="svg-wrap">
|
||||||
<div class="inner-border"></div>
|
${svgWithQr}
|
||||||
<div class="badge">🔥 ${restTitle}</div>
|
|
||||||
<div class="title">DIGITAL MENU<br><span style="letter-spacing:2px;">SCAN TO ORDER</span></div>
|
|
||||||
<div class="qr-box">
|
|
||||||
<img class="qr-img" src="${qrImageUri}" />
|
|
||||||
</div>
|
|
||||||
<div class="sub">Use your phone's camera</div>
|
|
||||||
<div class="footer-wrap">
|
|
||||||
<div class="icon-left">🍔</div>
|
|
||||||
<div class="icon-right">🥤</div>
|
|
||||||
<div class="cats">${catsHtml}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@@ -255,13 +201,13 @@ export default function QrScreen() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ScrollView contentContainerStyle={{ flexGrow: 1, backgroundColor: "#FAF8F5", paddingVertical: 20 }}>
|
<ScrollView contentContainerStyle={{ flexGrow: 1, backgroundColor: "#0C0C0E", paddingVertical: 20 }}>
|
||||||
<View style={{ width: "100%", maxWidth: 440, alignSelf: "center", paddingHorizontal: 20 }}>
|
<View style={{ width: "100%", maxWidth: 440, alignSelf: "center", paddingHorizontal: 20 }}>
|
||||||
{/* Header Badge */}
|
{/* Header Badge */}
|
||||||
<View
|
<View
|
||||||
style={{
|
style={{
|
||||||
alignSelf: "center",
|
alignSelf: "center",
|
||||||
backgroundColor: "#F3EDE2",
|
backgroundColor: "rgba(212, 175, 55, 0.15)",
|
||||||
paddingHorizontal: 14,
|
paddingHorizontal: 14,
|
||||||
paddingVertical: 6,
|
paddingVertical: 6,
|
||||||
borderRadius: 99,
|
borderRadius: 99,
|
||||||
@@ -269,22 +215,24 @@ export default function QrScreen() {
|
|||||||
flexDirection: "row",
|
flexDirection: "row",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
gap: 6,
|
gap: 6,
|
||||||
|
borderWidth: 1,
|
||||||
|
borderColor: "rgba(212, 175, 55, 0.3)",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Ionicons name="qr-code-outline" size={15} color="#926E27" />
|
<Ionicons name="qr-code-outline" size={15} color="#D4AF37" />
|
||||||
<Text style={{ color: "#926E27", fontSize: 12, fontWeight: "700" }}>
|
<Text style={{ color: "#D4AF37", fontSize: 12, fontWeight: "700" }}>
|
||||||
MASA QR STANT JENERATÖRÜ
|
MASA QR STANT JENERATÖRÜ
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<Text style={{ fontSize: 24, fontWeight: "800", color: "#1C1917", marginBottom: 4, textAlign: "center" }}>
|
<Text style={{ fontSize: 24, fontWeight: "800", color: "#FFFFFF", marginBottom: 4, textAlign: "center" }}>
|
||||||
Masa Stant Tasarımları
|
Masa Stant Tasarımları
|
||||||
</Text>
|
</Text>
|
||||||
<Text style={{ fontSize: 13, color: "#78716C", textAlign: "center", marginBottom: 20, lineHeight: 18 }}>
|
<Text style={{ fontSize: 13, color: "#A1A1AA", textAlign: "center", marginBottom: 20, lineHeight: 18 }}>
|
||||||
Şablon seçin; QR kodunuz otomatik yerleşsin. Baskıya hazır A5 PDF veya PNG olarak indirin.
|
Şablon seçin; QR kodunuz otomatik yerleşsin. Baskıya hazır A5 PDF veya PNG olarak indirin.
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
{/* Horizontal Theme Selector (rendered only if multiple themes exist) */}
|
{/* Horizontal Theme Selector */}
|
||||||
{Object.values(QR_STAND_PRESETS).length > 1 && (
|
{Object.values(QR_STAND_PRESETS).length > 1 && (
|
||||||
<ScrollView
|
<ScrollView
|
||||||
horizontal
|
horizontal
|
||||||
@@ -298,9 +246,9 @@ export default function QrScreen() {
|
|||||||
key={preset.key}
|
key={preset.key}
|
||||||
onPress={() => setSelectedKey(preset.key)}
|
onPress={() => setSelectedKey(preset.key)}
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: isSelected ? "#1C1917" : "#FFFFFF",
|
backgroundColor: isSelected ? "#D4AF37" : "#1A1A1E",
|
||||||
borderWidth: 1.5,
|
borderWidth: 1.5,
|
||||||
borderColor: isSelected ? "#1C1917" : "#E7E5E4",
|
borderColor: isSelected ? "#D4AF37" : "#2B2B32",
|
||||||
borderRadius: 14,
|
borderRadius: 14,
|
||||||
paddingHorizontal: 16,
|
paddingHorizontal: 16,
|
||||||
paddingVertical: 10,
|
paddingVertical: 10,
|
||||||
@@ -314,7 +262,7 @@ export default function QrScreen() {
|
|||||||
width: 14,
|
width: 14,
|
||||||
height: 14,
|
height: 14,
|
||||||
borderRadius: 7,
|
borderRadius: 7,
|
||||||
backgroundColor: preset.plaqueColor,
|
backgroundColor: preset.plaqueColor || "#1A1A1E",
|
||||||
borderWidth: 1,
|
borderWidth: 1,
|
||||||
borderColor: "rgba(255,255,255,0.4)",
|
borderColor: "rgba(255,255,255,0.4)",
|
||||||
}}
|
}}
|
||||||
@@ -323,7 +271,7 @@ export default function QrScreen() {
|
|||||||
style={{
|
style={{
|
||||||
fontSize: 13,
|
fontSize: 13,
|
||||||
fontWeight: "700",
|
fontWeight: "700",
|
||||||
color: isSelected ? "#FFFFFF" : "#1C1917",
|
color: isSelected ? "#0C0C0E" : "#FFFFFF",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{preset.name}
|
{preset.name}
|
||||||
@@ -339,173 +287,61 @@ export default function QrScreen() {
|
|||||||
style={{
|
style={{
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
justifyContent: "center",
|
justifyContent: "center",
|
||||||
padding: 18,
|
padding: 16,
|
||||||
borderRadius: 28,
|
borderRadius: 28,
|
||||||
backgroundColor: "#1c3e2b", // Ambient forest green tabletop environment backdrop
|
backgroundColor: "#18181B",
|
||||||
marginBottom: 20,
|
marginBottom: 20,
|
||||||
shadowColor: "#000",
|
shadowColor: "#000",
|
||||||
shadowOffset: { width: 0, height: 10 },
|
shadowOffset: { width: 0, height: 10 },
|
||||||
shadowOpacity: 0.35,
|
shadowOpacity: 0.5,
|
||||||
shadowRadius: 20,
|
shadowRadius: 20,
|
||||||
elevation: 8,
|
elevation: 8,
|
||||||
|
borderWidth: 1,
|
||||||
|
borderColor: "rgba(255,255,255,0.08)",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ViewShot ref={viewShotRef} options={{ format: "png", quality: 1.0 }}>
|
<ViewShot ref={viewShotRef} options={{ format: "png", quality: 1.0 }}>
|
||||||
<View
|
<View
|
||||||
style={{
|
style={{
|
||||||
width: 300,
|
width: 300,
|
||||||
minHeight: 520,
|
height: 487.5,
|
||||||
borderRadius: 36,
|
borderRadius: 24,
|
||||||
padding: 22,
|
overflow: "hidden",
|
||||||
alignItems: "center",
|
|
||||||
backgroundColor: activePreset.plaqueColor,
|
|
||||||
borderWidth: 2,
|
|
||||||
borderColor: "rgba(0,0,0,0.25)",
|
|
||||||
position: "relative",
|
position: "relative",
|
||||||
shadowColor: "#000",
|
backgroundColor: "#0C0C0E",
|
||||||
shadowOffset: { width: 0, height: 15 },
|
|
||||||
shadowOpacity: 0.4,
|
|
||||||
shadowRadius: 30,
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{/* Inner Border Accent Line */}
|
{/* Exact Vector SVG Artwork */}
|
||||||
|
{currentSvgRaw ? (
|
||||||
|
<SvgXml xml={currentSvgRaw} width={300} height={487.5} />
|
||||||
|
) : null}
|
||||||
|
|
||||||
|
{/* Centered QR Code Box Overlay */}
|
||||||
<View
|
<View
|
||||||
style={{
|
style={{
|
||||||
position: "absolute",
|
position: "absolute",
|
||||||
top: 10,
|
top: 161.25, // (215 / 650) * 487.5
|
||||||
left: 10,
|
left: 67.5, // (90 / 400) * 300
|
||||||
right: 10,
|
width: 165, // (220 / 400) * 300
|
||||||
bottom: 10,
|
height: 165,
|
||||||
borderRadius: 28,
|
borderRadius: 15,
|
||||||
borderWidth: 1,
|
backgroundColor: "#FFFFFF",
|
||||||
borderColor: activePreset.borderAccent,
|
padding: 8,
|
||||||
pointerEvents: "none",
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{/* Top Restaurant Badge with Flame Icon */}
|
|
||||||
<View
|
|
||||||
style={{
|
|
||||||
borderWidth: 2,
|
|
||||||
borderColor: activePreset.logoBorder,
|
|
||||||
backgroundColor: activePreset.logoBg,
|
|
||||||
paddingHorizontal: 16,
|
|
||||||
paddingVertical: 6,
|
|
||||||
borderRadius: 12,
|
|
||||||
marginBottom: 16,
|
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
flexDirection: "row",
|
justifyContent: "center",
|
||||||
gap: 6,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Text
|
|
||||||
style={{
|
|
||||||
color: activePreset.accentText,
|
|
||||||
fontSize: 16,
|
|
||||||
fontWeight: "900",
|
|
||||||
letterSpacing: 1.5,
|
|
||||||
textAlign: "center",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{restaurantName.toUpperCase()}
|
|
||||||
</Text>
|
|
||||||
<Ionicons name="flame" size={16} color={activePreset.accentText} />
|
|
||||||
</View>
|
|
||||||
|
|
||||||
{/* Title Callout */}
|
|
||||||
<Text
|
|
||||||
style={{
|
|
||||||
color: activePreset.headerText,
|
|
||||||
fontSize: 22,
|
|
||||||
fontWeight: "900",
|
|
||||||
textAlign: "center",
|
|
||||||
lineHeight: 25,
|
|
||||||
marginBottom: 18,
|
|
||||||
letterSpacing: 0.5,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
DIGITAL MENU{"\n"}
|
|
||||||
<Text style={{ letterSpacing: 1.5 }}>SCAN TO ORDER</Text>
|
|
||||||
</Text>
|
|
||||||
|
|
||||||
{/* Auto Placed QR Code Container */}
|
|
||||||
<View
|
|
||||||
style={{
|
|
||||||
backgroundColor: activePreset.qrBg,
|
|
||||||
padding: 14,
|
|
||||||
borderRadius: 24,
|
|
||||||
marginBottom: 14,
|
|
||||||
shadowColor: "#000",
|
shadowColor: "#000",
|
||||||
shadowOffset: { width: 0, height: 6 },
|
shadowOffset: { width: 0, height: 4 },
|
||||||
shadowOpacity: 0.2,
|
shadowOpacity: 0.25,
|
||||||
shadowRadius: 12,
|
shadowRadius: 8,
|
||||||
elevation: 5,
|
elevation: 4,
|
||||||
borderWidth: 1,
|
|
||||||
borderColor: "rgba(0,0,0,0.1)",
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Image
|
<Image
|
||||||
source={{ uri: `data:image/png;base64,${qr.pngBase64}` }}
|
source={{ uri: `data:image/png;base64,${qr.pngBase64}` }}
|
||||||
style={{ width: 175, height: 175, borderRadius: 10 }}
|
style={{ width: "100%", height: "100%", borderRadius: 8 }}
|
||||||
resizeMode="contain"
|
resizeMode="contain"
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{/* Instructional Subtext */}
|
|
||||||
<Text
|
|
||||||
style={{
|
|
||||||
color: activePreset.subText,
|
|
||||||
fontSize: 12,
|
|
||||||
fontWeight: "600",
|
|
||||||
marginBottom: 16,
|
|
||||||
textAlign: "center",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Use your phone's camera
|
|
||||||
</Text>
|
|
||||||
|
|
||||||
{/* Category Quick Preview List Flanked by Icons */}
|
|
||||||
<View style={{ width: "100%", position: "relative", alignItems: "center" }}>
|
|
||||||
{/* Floating Left Line Icon (Burger) */}
|
|
||||||
<View style={{ position: "absolute", left: 0, bottom: 8, opacity: 0.9 }}>
|
|
||||||
<Ionicons name="fast-food-outline" size={26} color={activePreset.accentText} />
|
|
||||||
</View>
|
|
||||||
|
|
||||||
{/* Floating Right Line Icon (Drink) */}
|
|
||||||
<View style={{ position: "absolute", right: 0, bottom: 8, opacity: 0.9 }}>
|
|
||||||
<Ionicons name="wine-outline" size={26} color={activePreset.accentText} />
|
|
||||||
</View>
|
|
||||||
|
|
||||||
{/* Center Category Stack */}
|
|
||||||
<View style={{ width: "68%", alignItems: "center", gap: 2 }}>
|
|
||||||
{displayCategories.map((cat, idx) => (
|
|
||||||
<View key={idx} style={{ width: "100%", alignItems: "center" }}>
|
|
||||||
<Text
|
|
||||||
style={{
|
|
||||||
color: activePreset.headerText,
|
|
||||||
fontSize: 13,
|
|
||||||
fontWeight: "900",
|
|
||||||
letterSpacing: 2,
|
|
||||||
textTransform: "uppercase",
|
|
||||||
paddingVertical: 2,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{cat}
|
|
||||||
</Text>
|
|
||||||
{idx < displayCategories.length - 1 && (
|
|
||||||
<View
|
|
||||||
style={{
|
|
||||||
width: "100%",
|
|
||||||
height: 1,
|
|
||||||
backgroundColor: activePreset.dividerColor,
|
|
||||||
marginVertical: 2,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</View>
|
|
||||||
))}
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
</View>
|
</View>
|
||||||
</ViewShot>
|
</ViewShot>
|
||||||
</View>
|
</View>
|
||||||
@@ -517,7 +353,7 @@ export default function QrScreen() {
|
|||||||
onPress={handleExportPdf}
|
onPress={handleExportPdf}
|
||||||
disabled={exportingPdf}
|
disabled={exportingPdf}
|
||||||
style={({ pressed }) => ({
|
style={({ pressed }) => ({
|
||||||
backgroundColor: "#C8A96B",
|
backgroundColor: "#D4AF37",
|
||||||
borderRadius: 14,
|
borderRadius: 14,
|
||||||
padding: 16,
|
padding: 16,
|
||||||
flexDirection: "row",
|
flexDirection: "row",
|
||||||
@@ -525,33 +361,35 @@ export default function QrScreen() {
|
|||||||
justifyContent: "center",
|
justifyContent: "center",
|
||||||
gap: 8,
|
gap: 8,
|
||||||
opacity: pressed || exportingPdf ? 0.85 : 1,
|
opacity: pressed || exportingPdf ? 0.85 : 1,
|
||||||
shadowColor: "#C8A96B",
|
shadowColor: "#D4AF37",
|
||||||
shadowOffset: { width: 0, height: 4 },
|
shadowOffset: { width: 0, height: 4 },
|
||||||
shadowOpacity: 0.2,
|
shadowOpacity: 0.2,
|
||||||
shadowRadius: 10,
|
shadowRadius: 8,
|
||||||
elevation: 4,
|
elevation: 3,
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
{exportingPdf ? (
|
{exportingPdf ? (
|
||||||
<ActivityIndicator color="#FFFFFF" />
|
<ActivityIndicator size="small" color="#0C0C0E" />
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<Ionicons name="document-text-outline" size={18} color="#FFFFFF" />
|
<Ionicons name="document-text-outline" size={20} color="#0C0C0E" />
|
||||||
<Text style={{ color: "#FFFFFF", fontSize: 15, fontWeight: "800" }}>
|
<Text style={{ color: "#0C0C0E", fontSize: 15, fontWeight: "800" }}>
|
||||||
A5 Baskı PDF Dosyası İndir / Paylaş
|
PDF Olarak İndir (A5 Matbaa Baskı)
|
||||||
</Text>
|
</Text>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</Pressable>
|
</Pressable>
|
||||||
|
|
||||||
{/* PNG Export Button */}
|
{/* PNG Download Button */}
|
||||||
<Pressable
|
<Pressable
|
||||||
onPress={handleExportPng}
|
onPress={handleExportPng}
|
||||||
disabled={exportingPng}
|
disabled={exportingPng}
|
||||||
style={({ pressed }) => ({
|
style={({ pressed }) => ({
|
||||||
backgroundColor: "#1C1917",
|
backgroundColor: "#1A1A1E",
|
||||||
|
borderWidth: 1.5,
|
||||||
|
borderColor: "#2B2B32",
|
||||||
borderRadius: 14,
|
borderRadius: 14,
|
||||||
padding: 15,
|
padding: 16,
|
||||||
flexDirection: "row",
|
flexDirection: "row",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
justifyContent: "center",
|
justifyContent: "center",
|
||||||
@@ -560,76 +398,64 @@ export default function QrScreen() {
|
|||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
{exportingPng ? (
|
{exportingPng ? (
|
||||||
<ActivityIndicator color="#FFFFFF" />
|
<ActivityIndicator size="small" color="#FFFFFF" />
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<Ionicons name="image-outline" size={18} color="#FFFFFF" />
|
<Ionicons name="image-outline" size={20} color="#FFFFFF" />
|
||||||
<Text style={{ color: "#FFFFFF", fontSize: 15, fontWeight: "700" }}>
|
<Text style={{ color: "#FFFFFF", fontSize: 15, fontWeight: "700" }}>
|
||||||
PNG Görsel Olarak Kaydet / Paylaş
|
PNG Olarak Kaydet / Paylaş
|
||||||
</Text>
|
</Text>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</Pressable>
|
</Pressable>
|
||||||
|
|
||||||
{/* Secondary Quick Share & Copy */}
|
{/* Direct Link Share Button */}
|
||||||
<View style={{ flexDirection: "row", gap: 10, marginTop: 4 }}>
|
|
||||||
<Pressable
|
|
||||||
onPress={handleShareQuick}
|
|
||||||
style={({ pressed }) => ({
|
|
||||||
flex: 1,
|
|
||||||
backgroundColor: "#FFFFFF",
|
|
||||||
borderWidth: 1,
|
|
||||||
borderColor: "#E7E5E4",
|
|
||||||
borderRadius: 12,
|
|
||||||
padding: 12,
|
|
||||||
flexDirection: "row",
|
|
||||||
alignItems: "center",
|
|
||||||
justifyContent: "center",
|
|
||||||
gap: 6,
|
|
||||||
opacity: pressed ? 0.85 : 1,
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
<Ionicons name="share-social-outline" size={16} color="#1C1917" />
|
|
||||||
<Text style={{ color: "#1C1917", fontSize: 13, fontWeight: "600" }}>Linki Paylaş</Text>
|
|
||||||
</Pressable>
|
|
||||||
|
|
||||||
<Pressable
|
|
||||||
onPress={handleCopy}
|
|
||||||
style={({ pressed }) => ({
|
|
||||||
flex: 1,
|
|
||||||
backgroundColor: "#FFFFFF",
|
|
||||||
borderWidth: 1,
|
|
||||||
borderColor: "#E7E5E4",
|
|
||||||
borderRadius: 12,
|
|
||||||
padding: 12,
|
|
||||||
flexDirection: "row",
|
|
||||||
alignItems: "center",
|
|
||||||
justifyContent: "center",
|
|
||||||
gap: 6,
|
|
||||||
opacity: pressed ? 0.85 : 1,
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
<Ionicons
|
|
||||||
name={copied ? "checkmark-circle-outline" : "copy-outline"}
|
|
||||||
size={16}
|
|
||||||
color={copied ? "#10B981" : "#1C1917"}
|
|
||||||
/>
|
|
||||||
<Text style={{ color: copied ? "#10B981" : "#1C1917", fontSize: 13, fontWeight: "600" }}>
|
|
||||||
{copied ? "Kopyalandı!" : "Linki Kopyala"}
|
|
||||||
</Text>
|
|
||||||
</Pressable>
|
|
||||||
</View>
|
|
||||||
|
|
||||||
{/* Back button */}
|
|
||||||
<Pressable
|
<Pressable
|
||||||
onPress={() => router.back()}
|
onPress={handleShareQuick}
|
||||||
style={{ padding: 12, alignItems: "center", flexDirection: "row", justifyContent: "center", gap: 6 }}
|
style={({ pressed }) => ({
|
||||||
|
backgroundColor: "#1A1A1E",
|
||||||
|
borderWidth: 1,
|
||||||
|
borderColor: "#2B2B32",
|
||||||
|
borderRadius: 14,
|
||||||
|
padding: 14,
|
||||||
|
flexDirection: "row",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "center",
|
||||||
|
gap: 8,
|
||||||
|
opacity: pressed ? 0.85 : 1,
|
||||||
|
})}
|
||||||
>
|
>
|
||||||
<Ionicons name="arrow-back" size={16} color="#78716C" />
|
<Ionicons name="share-outline" size={18} color="#D4AF37" />
|
||||||
<Text style={{ color: "#78716C", fontSize: 14, fontWeight: "600" }}>
|
<Text style={{ color: "#D4AF37", fontSize: 14, fontWeight: "700" }}>
|
||||||
Menü Editörüne Dön
|
Menü Linkini Paylaş
|
||||||
</Text>
|
</Text>
|
||||||
</Pressable>
|
</Pressable>
|
||||||
|
|
||||||
|
{/* Quick Copy Link Box */}
|
||||||
|
<Pressable
|
||||||
|
onPress={handleCopy}
|
||||||
|
style={{
|
||||||
|
backgroundColor: "#141417",
|
||||||
|
borderWidth: 1,
|
||||||
|
borderColor: "#242429",
|
||||||
|
borderRadius: 14,
|
||||||
|
padding: 14,
|
||||||
|
flexDirection: "row",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
marginTop: 4,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Text style={{ color: "#A1A1AA", fontSize: 12, flex: 1 }} numberOfLines={1}>
|
||||||
|
{qr.targetUrl}
|
||||||
|
</Text>
|
||||||
|
<Ionicons
|
||||||
|
name={copied ? "checkmark-circle" : "copy-outline"}
|
||||||
|
size={18}
|
||||||
|
color={copied ? "#10B981" : "#D4AF37"}
|
||||||
|
style={{ marginLeft: 8 }}
|
||||||
|
/>
|
||||||
|
</Pressable>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
Generated
+96
@@ -119,6 +119,9 @@ importers:
|
|||||||
react-native-screens:
|
react-native-screens:
|
||||||
specifier: ~4.4.0
|
specifier: ~4.4.0
|
||||||
version: 4.4.0(react-native@0.76.9(@babel/core@7.29.7(supports-color@8.1.1))(@babel/preset-env@7.29.7(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@18.3.31)(react@18.3.1)(supports-color@8.1.1))(react@18.3.1)
|
version: 4.4.0(react-native@0.76.9(@babel/core@7.29.7(supports-color@8.1.1))(@babel/preset-env@7.29.7(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@18.3.31)(react@18.3.1)(supports-color@8.1.1))(react@18.3.1)
|
||||||
|
react-native-svg:
|
||||||
|
specifier: ^15.15.5
|
||||||
|
version: 15.15.5(react-native@0.76.9(@babel/core@7.29.7(supports-color@8.1.1))(@babel/preset-env@7.29.7(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@18.3.31)(react@18.3.1)(supports-color@8.1.1))(react@18.3.1)
|
||||||
react-native-url-polyfill:
|
react-native-url-polyfill:
|
||||||
specifier: ^2.0.0
|
specifier: ^2.0.0
|
||||||
version: 2.0.0(react-native@0.76.9(@babel/core@7.29.7(supports-color@8.1.1))(@babel/preset-env@7.29.7(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@18.3.31)(react@18.3.1)(supports-color@8.1.1))
|
version: 2.0.0(react-native@0.76.9(@babel/core@7.29.7(supports-color@8.1.1))(@babel/preset-env@7.29.7(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@18.3.31)(react@18.3.1)(supports-color@8.1.1))
|
||||||
@@ -2323,6 +2326,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
|
resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
boolbase@1.0.0:
|
||||||
|
resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==}
|
||||||
|
|
||||||
bplist-creator@0.0.7:
|
bplist-creator@0.0.7:
|
||||||
resolution: {integrity: sha512-xp/tcaV3T5PCiaY04mXga7o/TE+t95gqeLmADeBI1CvZtdWTbgBt3uLpvh4UWtenKeBhCV6oVxGk38yZr2uYEA==}
|
resolution: {integrity: sha512-xp/tcaV3T5PCiaY04mXga7o/TE+t95gqeLmADeBI1CvZtdWTbgBt3uLpvh4UWtenKeBhCV6oVxGk38yZr2uYEA==}
|
||||||
|
|
||||||
@@ -2601,6 +2607,17 @@ packages:
|
|||||||
css-line-break@2.1.0:
|
css-line-break@2.1.0:
|
||||||
resolution: {integrity: sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==}
|
resolution: {integrity: sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==}
|
||||||
|
|
||||||
|
css-select@5.2.2:
|
||||||
|
resolution: {integrity: sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==}
|
||||||
|
|
||||||
|
css-tree@1.1.3:
|
||||||
|
resolution: {integrity: sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==}
|
||||||
|
engines: {node: '>=8.0.0'}
|
||||||
|
|
||||||
|
css-what@6.2.2:
|
||||||
|
resolution: {integrity: sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==}
|
||||||
|
engines: {node: '>= 6'}
|
||||||
|
|
||||||
cssesc@3.0.0:
|
cssesc@3.0.0:
|
||||||
resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==}
|
resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
@@ -2703,9 +2720,22 @@ packages:
|
|||||||
dlv@1.1.3:
|
dlv@1.1.3:
|
||||||
resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==}
|
resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==}
|
||||||
|
|
||||||
|
dom-serializer@2.0.0:
|
||||||
|
resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==}
|
||||||
|
|
||||||
|
domelementtype@2.3.0:
|
||||||
|
resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==}
|
||||||
|
|
||||||
|
domhandler@5.0.3:
|
||||||
|
resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==}
|
||||||
|
engines: {node: '>= 4'}
|
||||||
|
|
||||||
dompurify@3.4.14:
|
dompurify@3.4.14:
|
||||||
resolution: {integrity: sha512-dVoH9z+MY+C9IilgGCk3YfFqjLi3fChm2OiKJMzh6axrJ5qwxqWaZamgmHrpv22CN/KdbZJuGEGgfQoL00LTdg==}
|
resolution: {integrity: sha512-dVoH9z+MY+C9IilgGCk3YfFqjLi3fChm2OiKJMzh6axrJ5qwxqWaZamgmHrpv22CN/KdbZJuGEGgfQoL00LTdg==}
|
||||||
|
|
||||||
|
domutils@3.2.2:
|
||||||
|
resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==}
|
||||||
|
|
||||||
dotenv-expand@11.0.7:
|
dotenv-expand@11.0.7:
|
||||||
resolution: {integrity: sha512-zIHwmZPRshsCdpMDyVsqGmgyP0yT8GAgXUnkdAoJisxvf33k7yO6OuoKmcTGuXPWSsm8Oh88nZicRLA9Y0rUeA==}
|
resolution: {integrity: sha512-zIHwmZPRshsCdpMDyVsqGmgyP0yT8GAgXUnkdAoJisxvf33k7yO6OuoKmcTGuXPWSsm8Oh88nZicRLA9Y0rUeA==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
@@ -2748,6 +2778,10 @@ packages:
|
|||||||
end-of-stream@1.4.5:
|
end-of-stream@1.4.5:
|
||||||
resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==}
|
resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==}
|
||||||
|
|
||||||
|
entities@4.5.0:
|
||||||
|
resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
|
||||||
|
engines: {node: '>=0.12'}
|
||||||
|
|
||||||
env-editor@0.4.2:
|
env-editor@0.4.2:
|
||||||
resolution: {integrity: sha512-ObFo8v4rQJAE59M69QzwloxPZtd33TpYEIjtKD1rrFDcM1Gd7IkDxEBU+HriziN6HSHQnBJi8Dmy+JWkav5HKA==}
|
resolution: {integrity: sha512-ObFo8v4rQJAE59M69QzwloxPZtd33TpYEIjtKD1rrFDcM1Gd7IkDxEBU+HriziN6HSHQnBJi8Dmy+JWkav5HKA==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
@@ -3646,6 +3680,9 @@ packages:
|
|||||||
md5@2.3.0:
|
md5@2.3.0:
|
||||||
resolution: {integrity: sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==}
|
resolution: {integrity: sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==}
|
||||||
|
|
||||||
|
mdn-data@2.0.14:
|
||||||
|
resolution: {integrity: sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==}
|
||||||
|
|
||||||
memoize-one@5.2.1:
|
memoize-one@5.2.1:
|
||||||
resolution: {integrity: sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==}
|
resolution: {integrity: sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==}
|
||||||
|
|
||||||
@@ -3896,6 +3933,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==}
|
resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
nth-check@2.1.1:
|
||||||
|
resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==}
|
||||||
|
|
||||||
nullthrows@1.1.1:
|
nullthrows@1.1.1:
|
||||||
resolution: {integrity: sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==}
|
resolution: {integrity: sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==}
|
||||||
|
|
||||||
@@ -4292,6 +4332,12 @@ packages:
|
|||||||
react: '*'
|
react: '*'
|
||||||
react-native: '*'
|
react-native: '*'
|
||||||
|
|
||||||
|
react-native-svg@15.15.5:
|
||||||
|
resolution: {integrity: sha512-L4go5jA+GWutdJ/JucuN20cjAbMg1HmMtAP+wZ+3JLCf6Jd0bhXQHxciRP/AQm/FlrIEZwkMcHNZP+FXAiic0w==}
|
||||||
|
peerDependencies:
|
||||||
|
react: '*'
|
||||||
|
react-native: '*'
|
||||||
|
|
||||||
react-native-url-polyfill@2.0.0:
|
react-native-url-polyfill@2.0.0:
|
||||||
resolution: {integrity: sha512-My330Do7/DvKnEvwQc0WdcBnFPploYKp9CYlefDXzIdEaA+PAhDYllkvGeEroEzvc4Kzzj2O4yVdz8v6fjRvhA==}
|
resolution: {integrity: sha512-My330Do7/DvKnEvwQc0WdcBnFPploYKp9CYlefDXzIdEaA+PAhDYllkvGeEroEzvc4Kzzj2O4yVdz8v6fjRvhA==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@@ -7512,6 +7558,8 @@ snapshots:
|
|||||||
|
|
||||||
binary-extensions@2.3.0: {}
|
binary-extensions@2.3.0: {}
|
||||||
|
|
||||||
|
boolbase@1.0.0: {}
|
||||||
|
|
||||||
bplist-creator@0.0.7:
|
bplist-creator@0.0.7:
|
||||||
dependencies:
|
dependencies:
|
||||||
stream-buffers: 2.2.0
|
stream-buffers: 2.2.0
|
||||||
@@ -7832,6 +7880,21 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
utrie: 1.0.2
|
utrie: 1.0.2
|
||||||
|
|
||||||
|
css-select@5.2.2:
|
||||||
|
dependencies:
|
||||||
|
boolbase: 1.0.0
|
||||||
|
css-what: 6.2.2
|
||||||
|
domhandler: 5.0.3
|
||||||
|
domutils: 3.2.2
|
||||||
|
nth-check: 2.1.1
|
||||||
|
|
||||||
|
css-tree@1.1.3:
|
||||||
|
dependencies:
|
||||||
|
mdn-data: 2.0.14
|
||||||
|
source-map: 0.6.1
|
||||||
|
|
||||||
|
css-what@6.2.2: {}
|
||||||
|
|
||||||
cssesc@3.0.0: {}
|
cssesc@3.0.0: {}
|
||||||
|
|
||||||
csstype@3.2.3: {}
|
csstype@3.2.3: {}
|
||||||
@@ -7907,11 +7970,29 @@ snapshots:
|
|||||||
|
|
||||||
dlv@1.1.3: {}
|
dlv@1.1.3: {}
|
||||||
|
|
||||||
|
dom-serializer@2.0.0:
|
||||||
|
dependencies:
|
||||||
|
domelementtype: 2.3.0
|
||||||
|
domhandler: 5.0.3
|
||||||
|
entities: 4.5.0
|
||||||
|
|
||||||
|
domelementtype@2.3.0: {}
|
||||||
|
|
||||||
|
domhandler@5.0.3:
|
||||||
|
dependencies:
|
||||||
|
domelementtype: 2.3.0
|
||||||
|
|
||||||
dompurify@3.4.14:
|
dompurify@3.4.14:
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@types/trusted-types': 2.0.7
|
'@types/trusted-types': 2.0.7
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
domutils@3.2.2:
|
||||||
|
dependencies:
|
||||||
|
dom-serializer: 2.0.0
|
||||||
|
domelementtype: 2.3.0
|
||||||
|
domhandler: 5.0.3
|
||||||
|
|
||||||
dotenv-expand@11.0.7:
|
dotenv-expand@11.0.7:
|
||||||
dependencies:
|
dependencies:
|
||||||
dotenv: 16.4.7
|
dotenv: 16.4.7
|
||||||
@@ -7944,6 +8025,8 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
once: 1.4.0
|
once: 1.4.0
|
||||||
|
|
||||||
|
entities@4.5.0: {}
|
||||||
|
|
||||||
env-editor@0.4.2: {}
|
env-editor@0.4.2: {}
|
||||||
|
|
||||||
error-ex@1.3.4:
|
error-ex@1.3.4:
|
||||||
@@ -8971,6 +9054,8 @@ snapshots:
|
|||||||
crypt: 0.0.2
|
crypt: 0.0.2
|
||||||
is-buffer: 1.1.6
|
is-buffer: 1.1.6
|
||||||
|
|
||||||
|
mdn-data@2.0.14: {}
|
||||||
|
|
||||||
memoize-one@5.2.1: {}
|
memoize-one@5.2.1: {}
|
||||||
|
|
||||||
merge-options@3.0.4:
|
merge-options@3.0.4:
|
||||||
@@ -9304,6 +9389,10 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
path-key: 3.1.1
|
path-key: 3.1.1
|
||||||
|
|
||||||
|
nth-check@2.1.1:
|
||||||
|
dependencies:
|
||||||
|
boolbase: 1.0.0
|
||||||
|
|
||||||
nullthrows@1.1.1: {}
|
nullthrows@1.1.1: {}
|
||||||
|
|
||||||
ob1@0.81.5:
|
ob1@0.81.5:
|
||||||
@@ -9679,6 +9768,13 @@ snapshots:
|
|||||||
react-native: 0.76.9(@babel/core@7.29.7(supports-color@8.1.1))(@babel/preset-env@7.29.7(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@18.3.31)(react@18.3.1)(supports-color@8.1.1)
|
react-native: 0.76.9(@babel/core@7.29.7(supports-color@8.1.1))(@babel/preset-env@7.29.7(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@18.3.31)(react@18.3.1)(supports-color@8.1.1)
|
||||||
warn-once: 0.1.1
|
warn-once: 0.1.1
|
||||||
|
|
||||||
|
react-native-svg@15.15.5(react-native@0.76.9(@babel/core@7.29.7(supports-color@8.1.1))(@babel/preset-env@7.29.7(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@18.3.31)(react@18.3.1)(supports-color@8.1.1))(react@18.3.1):
|
||||||
|
dependencies:
|
||||||
|
css-select: 5.2.2
|
||||||
|
css-tree: 1.1.3
|
||||||
|
react: 18.3.1
|
||||||
|
react-native: 0.76.9(@babel/core@7.29.7(supports-color@8.1.1))(@babel/preset-env@7.29.7(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@18.3.31)(react@18.3.1)(supports-color@8.1.1)
|
||||||
|
|
||||||
react-native-url-polyfill@2.0.0(react-native@0.76.9(@babel/core@7.29.7(supports-color@8.1.1))(@babel/preset-env@7.29.7(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@18.3.31)(react@18.3.1)(supports-color@8.1.1)):
|
react-native-url-polyfill@2.0.0(react-native@0.76.9(@babel/core@7.29.7(supports-color@8.1.1))(@babel/preset-env@7.29.7(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@18.3.31)(react@18.3.1)(supports-color@8.1.1)):
|
||||||
dependencies:
|
dependencies:
|
||||||
react-native: 0.76.9(@babel/core@7.29.7(supports-color@8.1.1))(@babel/preset-env@7.29.7(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@18.3.31)(react@18.3.1)(supports-color@8.1.1)
|
react-native: 0.76.9(@babel/core@7.29.7(supports-color@8.1.1))(@babel/preset-env@7.29.7(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@18.3.31)(react@18.3.1)(supports-color@8.1.1)
|
||||||
|
|||||||
@@ -1,88 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 400 650" width="400" height="650">
|
|
||||||
<defs>
|
|
||||||
<!-- Gold Gradient for luxury accents & borders -->
|
|
||||||
<linearGradient id="gold-accent" x1="0%" y1="0%" x2="100%" y2="100%">
|
|
||||||
<stop offset="0%" stop-color="#F3D382" />
|
|
||||||
<stop offset="50%" stop-color="#D4AF37" />
|
|
||||||
<stop offset="100%" stop-color="#AA7C11" />
|
|
||||||
</linearGradient>
|
|
||||||
|
|
||||||
<!-- Card Background Subtle Dark Gradient -->
|
|
||||||
<linearGradient id="card-surface" x1="0%" y1="0%" x2="0%" y2="100%">
|
|
||||||
<stop offset="0%" stop-color="#1A1A1E" />
|
|
||||||
<stop offset="100%" stop-color="#121215" />
|
|
||||||
</linearGradient>
|
|
||||||
</defs>
|
|
||||||
|
|
||||||
<!-- 1. Backgrounds & Contrast Panels -->
|
|
||||||
<g id="layer-background">
|
|
||||||
<!-- Distinct Outer Canvas Background -->
|
|
||||||
<rect id="bg-canvas" width="400" height="650" fill="#0C0C0E" />
|
|
||||||
|
|
||||||
<!-- Main Contrasting Menu Card Panel -->
|
|
||||||
<rect id="main-card" x="20" y="20" width="360" height="610" rx="24" fill="url(#card-surface)" stroke="url(#gold-accent)" stroke-width="1.5" />
|
|
||||||
|
|
||||||
<!-- Inner Decorative Framing Line -->
|
|
||||||
<rect id="inner-border" x="30" y="30" width="340" height="590" rx="18" fill="none" stroke="#2B2B32" stroke-width="1" stroke-dasharray="5 3" />
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<!-- 2. Decorative Icons, Crests & Accents -->
|
|
||||||
<g id="layer-icons">
|
|
||||||
<!-- Header Chef & Cutlery Crest -->
|
|
||||||
<g transform="translate(170, 52)" stroke="url(#gold-accent)" stroke-width="1.5" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
|
||||||
<!-- Fork -->
|
|
||||||
<path d="M-20 6 v12 c0 4 3 6 7 6 v14 M-23 6 v8 M-17 6 v8" />
|
|
||||||
<!-- Chef Hat -->
|
|
||||||
<path d="M12 36 h36 c3 0 5-2 5-5 v-4 c0-2-1-3-3-4 2-3 2-7 0-10 -1-2-3-3-5-3 0-5-4-9-9-9 -4 0-8 3-9 7 -2-1-5-1-7 1 -3 3-3 7-1 10 -2 1-3 2-3 4 v4 c0 3 2 5 6 5 z" />
|
|
||||||
<path d="M16 30 h28" stroke-width="1" />
|
|
||||||
<!-- Knife -->
|
|
||||||
<path d="M78 6 v38 M78 6 c6 0 10 6 10 14 v8 c0 4-4 6-10 6" />
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<!-- Center Star Divider -->
|
|
||||||
<g id="divider-accent">
|
|
||||||
<path d="M130 162 h55 M215 162 h55" stroke="url(#gold-accent)" stroke-width="1" opacity="0.6" />
|
|
||||||
<polygon points="200,157 202.5,161.5 207,162 203.5,165 204.5,169.5 200,167 195.5,169.5 196.5,165 193,162 197.5,161.5" fill="url(#gold-accent)" />
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<!-- WiFi Icon -->
|
|
||||||
<g transform="translate(68, 592)" stroke="#A1A1AA" stroke-width="1.5" fill="none" stroke-linecap="round">
|
|
||||||
<path d="M0 0 a14 14 0 0 1 18 0" />
|
|
||||||
<path d="M3 4 a9 9 0 0 1 12 0" />
|
|
||||||
<circle cx="9" cy="8" r="1" fill="#A1A1AA" />
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<!-- Instagram / Social Icon -->
|
|
||||||
<g transform="translate(225, 592)" stroke="#A1A1AA" stroke-width="1.5" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
|
||||||
<rect x="0" y="0" width="13" height="13" rx="3.5" />
|
|
||||||
<circle cx="6.5" cy="6.5" r="3" />
|
|
||||||
<circle cx="10" cy="3" r="0.5" fill="#A1A1AA" />
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<!-- 3. Clean Placeholder Box for QR Code -->
|
|
||||||
<g id="qr-placeholder" transform="translate(90, 215)">
|
|
||||||
<rect width="220" height="220" rx="20" fill="#FFFFFF" stroke="#E4E4E7" stroke-width="2" />
|
|
||||||
<rect x="12" y="12" width="196" height="196" rx="14" fill="#FAFAFA" stroke="#E4E4E7" stroke-dasharray="4 3" />
|
|
||||||
<text x="110" y="115" font-family="Inter, sans-serif" font-size="12" font-weight="600" fill="#71717A" text-anchor="middle">QR KOD ALANI</text>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<!-- 4. Native Editable Texts -->
|
|
||||||
<g id="layer-texts" font-family="Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif" text-anchor="middle">
|
|
||||||
<!-- Header Titles -->
|
|
||||||
<text x="200" y="130" font-size="22" font-weight="800" fill="#FFFFFF" letter-spacing="2">GOURMET BISTRO</text>
|
|
||||||
<text x="200" y="150" font-size="11" font-weight="500" fill="#D4AF37" letter-spacing="2.5">TEMASSIZ DİJİTAL MENÜ</text>
|
|
||||||
|
|
||||||
<!-- Scan Call to Action -->
|
|
||||||
<text x="200" y="475" font-size="13" font-weight="700" fill="#FFFFFF" letter-spacing="1.2">MENÜYÜ GÖRMEK İÇİN OKUTUN</text>
|
|
||||||
<text x="200" y="495" font-size="11" font-weight="400" fill="#8E8E93">Kameranız ile QR kodu taratın</text>
|
|
||||||
|
|
||||||
<!-- Table Badge Container & Label -->
|
|
||||||
<rect x="140" y="520" width="120" height="28" rx="14" fill="#242429" stroke="#383840" stroke-width="1" />
|
|
||||||
<text x="200" y="539" font-size="12" font-weight="600" fill="#E4E4E7" letter-spacing="0.5">Masa No: 01</text>
|
|
||||||
|
|
||||||
<!-- Footer Network & Social Meta -->
|
|
||||||
<text x="128" y="602" font-size="10" font-weight="400" fill="#A1A1AA" text-anchor="start">Bistro_Guest</text>
|
|
||||||
<text x="246" y="602" font-size="10" font-weight="400" fill="#A1A1AA" text-anchor="start">@gourmetbistro</text>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 4.5 KiB |
Reference in New Issue
Block a user