feat(mobile): add QrTemplate4 Crimson Petal, support full Turkish characters on all system fonts and templates

This commit is contained in:
AyrisAI
2026-08-21 00:41:16 +03:00
parent 4dbb68a73a
commit d2846ed628
4 changed files with 561 additions and 69 deletions
@@ -0,0 +1,279 @@
import * as React from "react";
import Svg, {
Defs,
G,
Path,
Circle,
Use,
Rect,
Text,
Image,
} from "react-native-svg";
import type { QrTemplateProps } from "./index";
const QrTemplate4 = ({
qrBase64,
restaurantName = "THE CRIMSON PETAL",
subtitle = "FINE DINING EXPERIENCE",
tableNo = "MASA NO: 01",
ctaText = "MENÜYÜ GÖRMEK İÇİN OKUTUN",
wifiText = "WiFi: CrimsonGuest | Şifre: petal2026",
socialText = "@thecrimsonpetal",
fontFamily = "Georgia, 'Times New Roman', serif",
showTableNo = true,
showSubtitle = true,
showCta = true,
showWifi = true,
showSocial = true,
...props
}: QrTemplateProps) => (
<Svg width={300} height={435} viewBox="0 0 400 580" {...props}>
<Defs>
<G id="b4">
<Path
fill="none"
stroke="#c59b4b"
strokeWidth={1.3}
d="M0-22c22-8 36 6 38 22-2 16-16 30-38 22Z"
/>
<Path
fill="none"
stroke="#c59b4b"
strokeWidth={0.9}
d="M0-12c14-6 24 2 26 12-2 10-12 18-26 12Z"
/>
<Path stroke="#c59b4b" strokeWidth={0.8} d="M0 0h26" />
<Path
fill="none"
stroke="#c59b4b"
strokeWidth={1.2}
d="M0-8c16-28 34-32 46-18C36-12 20-4 0-8ZM0 8c16 28 34 32 46 18C36 12 20 4 0 8Z"
/>
<Circle cx={48} cy={-28} r={1.5} fill="#c59b4b" />
<Circle cx={48} cy={28} r={1.5} fill="#c59b4b" />
<Path
fill="none"
stroke="#c59b4b"
strokeWidth={1.1}
d="M0-35c24-21 52-15 56-1-14 6-32 12-56 1Zm0 70c24 21 52 15 56 1-14-6-32-12-56-1Z"
/>
<Path
fill="none"
stroke="#c59b4b"
d="M28-44c14-20 36-10 30 2-10 6-20 4-30-2Zm0 88c14 20 36 10 30-2-10-6-20-4-30 2Z"
/>
<Circle cx={60} cy={-44} r={1.2} fill="#c59b4b" />
<Circle cx={60} cy={44} r={1.2} fill="#c59b4b" />
<Circle cx={34} cy={-58} r={1.2} fill="#c59b4b" />
<Circle cx={34} cy={58} r={1.2} fill="#c59b4b" />
<Path
fill="none"
stroke="#c59b4b"
strokeLinecap="round"
d="M0-60c22-15 36-35 32-55-6 15-16 30-32 55M0 60c22 15 36 35 32 55-6-15-16-30-32-55"
/>
<Path
fill="none"
stroke="#c59b4b"
strokeWidth={0.9}
d="M14-78c8-6 12 2 6 6-4 0-6-4-6-6Zm0 156c8 6 12-2 6-6-4 0-6 4-6 6Z"
/>
<Circle cx={32} cy={-116} r={1.5} fill="#c59b4b" />
<Circle cx={32} cy={116} r={1.5} fill="#c59b4b" />
</G>
<G id="a4">
<Path
fill="none"
stroke="#c59b4b"
strokeWidth={1.3}
d="M18 64c0-28 18-46 46-46"
/>
<Path
fill="none"
stroke="#c59b4b"
strokeWidth={0.8}
d="M24 52c0-16 12-28 28-28"
/>
<Path
fill="none"
stroke="#c59b4b"
strokeLinecap="round"
strokeWidth={0.9}
d="M28 32c4-6 10-4 8 2-2 4-8 2-6-4s10-6 14-2"
/>
<Circle cx={28} cy={28} r={1.8} fill="#c59b4b" />
<Circle cx={48} cy={18} r={1.4} fill="#c59b4b" />
<Circle cx={18} cy={48} r={1.4} fill="#c59b4b" />
</G>
</Defs>
<Path fill="#fdfdfd" d="M0 0h400v580H0z" />
<Path
fill="none"
stroke="#c59b4b"
strokeWidth={1.2}
d="M20 14h360v552H20z"
/>
<Use href="#a4" />
<Use href="#a4" transform="matrix(-1 0 0 1 400 0)" />
<Use href="#a4" transform="matrix(1 0 0 -1 0 580)" />
<Use href="#a4" transform="rotate(180 200 290)" />
<Path
fill="none"
stroke="#c59b4b"
strokeLinecap="round"
strokeWidth={1.2}
d="M132 62c8-4 16 2 24-2 4-2 2-6-2-4m114 6c-8-4-16 2-24-2-4-2-2-6 2-4"
/>
<Use href="#b4" transform="translate(0 310)" />
<Use href="#b4" transform="matrix(-1 0 0 1 400 310)" />
<Path
fill="none"
stroke="#c59b4b"
strokeLinecap="square"
strokeWidth={2.2}
d="M114 278v-58h54m118 58v-58h-54M114 342v58h54m118-58v58h-54"
/>
{/* QR Placeholder / Live QR */}
<G transform="translate(125 230)">
<Rect
width={150}
height={150}
fill="#fff"
stroke="#eae5d9"
strokeWidth={1.5}
rx={12}
/>
{qrBase64 ? (
<Image
href={`data:image/png;base64,${qrBase64}`}
x={8}
y={8}
width={134}
height={134}
/>
) : (
<>
<Rect
width={134}
height={134}
x={8}
y={8}
fill="#fafaf8"
stroke="#c59b4b"
strokeDasharray="4 3"
strokeOpacity={0.35}
rx={8}
/>
<Text
x={75}
y={79}
fill="#c59b4b"
fontFamily="Inter, Arial, sans-serif"
fontSize={10}
fontWeight={700}
letterSpacing={1}
textAnchor="middle"
>
{"QR KOD ALANI"}
</Text>
</>
)}
</G>
{/* Metinler */}
<G fill="#111" textAnchor="middle">
{/* Masa No */}
{showTableNo && tableNo ? (
<Text
x={200}
y={56}
fill="#c59b4b"
fontFamily={fontFamily}
fontSize={11}
fontWeight={700}
letterSpacing={1.5}
>
{tableNo}
</Text>
) : null}
{/* Restoran Başlığı */}
{restaurantName ? (
<Text
x={200}
y={116}
fill="#111111"
fontFamily={fontFamily}
fontSize={26}
fontWeight={700}
letterSpacing={2}
>
{restaurantName}
</Text>
) : null}
{/* Slogan */}
{showSubtitle && subtitle ? (
<Text
x={200}
y={154}
fill="#71717a"
fontFamily={fontFamily}
fontSize={11}
fontWeight={600}
letterSpacing={1.8}
>
{subtitle}
</Text>
) : null}
{/* CTA Metni */}
{showCta && ctaText ? (
<Text
x={200}
y={440}
fill="#111111"
fontFamily={fontFamily}
fontSize={15}
fontWeight={700}
letterSpacing={1}
>
{ctaText}
</Text>
) : null}
{/* WiFi Bilgisi */}
{showWifi && wifiText ? (
<Text
x={200}
y={488}
fill="#71717a"
fontFamily="System, -apple-system, Roboto, sans-serif"
fontSize={10.5}
fontWeight={600}
letterSpacing={0.5}
>
{wifiText}
</Text>
) : null}
{/* Sosyal Medya */}
{showSocial && socialText ? (
<Text
x={200}
y={514}
fill="#c59b4b"
fontFamily="System, -apple-system, Roboto, sans-serif"
fontSize={10.5}
fontWeight={600}
letterSpacing={0.5}
>
{socialText}
</Text>
) : null}
</G>
</Svg>
);
export default QrTemplate4;
@@ -3,6 +3,7 @@ import type { SvgProps } from "react-native-svg";
import QrTemplate1 from "./QrTemplateLumiere"; import QrTemplate1 from "./QrTemplateLumiere";
import QrTemplate2 from "./QrTemplate2"; import QrTemplate2 from "./QrTemplate2";
import QrTemplate3 from "./QrTemplate3"; import QrTemplate3 from "./QrTemplate3";
import QrTemplate4 from "./QrTemplate4";
export interface QrCustomTextConfig { export interface QrCustomTextConfig {
restaurantName?: string; restaurantName?: string;
@@ -24,12 +25,33 @@ export interface QrTemplateProps extends SvgProps, QrCustomTextConfig {
qrBase64?: string; qrBase64?: string;
} }
// Türkçe karakterleri (İ, ı, Ş, ş, Ğ, ğ, Ü, ü, Ö, ö, Ç, ç) eksiksiz destekleyen sistem fontları
export const FONT_OPTIONS = [ export const FONT_OPTIONS = [
{ id: "sans", label: "Modern Sans", fontFamily: "System, -apple-system, Arial, sans-serif" }, {
{ id: "serif", label: "Zarif Serif", fontFamily: "Georgia, 'Times New Roman', serif" }, id: "sans",
{ id: "didot", label: "Lüks Didot", fontFamily: "Didot, 'Playfair Display', Georgia, serif" }, label: "Modern Sans (Sistem)",
{ id: "palatino", label: "Klasik Kitap", fontFamily: "Palatino, 'Palatino Linotype', serif" }, fontFamily: "System, -apple-system, Roboto, 'Helvetica Neue', Arial, sans-serif",
{ id: "trebuchet", label: "Dinamik", fontFamily: "'Trebuchet MS', Helvetica, sans-serif" }, },
{
id: "serif",
label: "Zarif Serif (Georgia)",
fontFamily: "Georgia, 'Times New Roman', serif",
},
{
id: "times",
label: "Klasik (Times New Roman)",
fontFamily: "'Times New Roman', Times, Georgia, serif",
},
{
id: "palatino",
label: "Lüks Palatino",
fontFamily: "Palatino, 'Palatino Linotype', Georgia, serif",
},
{
id: "trebuchet",
label: "Dinamik (Trebuchet)",
fontFamily: "'Trebuchet MS', 'Segoe UI', Arial, sans-serif",
},
]; ];
// Yeni component eklemek için buraya import edip COMPONENT_TEMPLATES'e kaydet // Yeni component eklemek için buraya import edip COMPONENT_TEMPLATES'e kaydet
@@ -40,6 +62,7 @@ export const COMPONENT_TEMPLATES: Record<
"qr-1": QrTemplate1, "qr-1": QrTemplate1,
"qr-2": QrTemplate2, "qr-2": QrTemplate2,
"qr-3": QrTemplate3, "qr-3": QrTemplate3,
"qr-4": QrTemplate4,
}; };
// Her template'in aspect ratio'su (width/height) // Her template'in aspect ratio'su (width/height)
@@ -47,6 +70,5 @@ export const TEMPLATE_ASPECT_RATIOS: Record<string, number> = {
"qr-1": 1, // 600x600 = 1:1 "qr-1": 1, // 600x600 = 1:1
"qr-2": 400 / 650, // 400x650 = standart kart "qr-2": 400 / 650, // 400x650 = standart kart
"qr-3": 400 / 650, // 400x650 = standart kart "qr-3": 400 / 650, // 400x650 = standart kart
"qr-4": 400 / 580, // 400x580 = zarif altın çerçeve
}; };
+237
View File
@@ -0,0 +1,237 @@
import Svg, {
SvgProps,
Defs,
G,
Path,
Circle,
Use,
Rect,
Text,
} from "react-native-svg"
import { memo } from "react"
const SvgComponent = (props: SvgProps) => (
<Svg width={400} height={580} {...props}>
<Defs>
<G id="b">
<Path
fill="none"
stroke="#c59b4b"
strokeWidth={1.3}
d="M0-22c22-8 36 6 38 22-2 16-16 30-38 22Z"
/>
<Path
fill="none"
stroke="#c59b4b"
strokeWidth={0.9}
d="M0-12c14-6 24 2 26 12-2 10-12 18-26 12Z"
/>
<Path stroke="#c59b4b" strokeWidth={0.8} d="M0 0h26" />
<Path
fill="none"
stroke="#c59b4b"
strokeWidth={1.2}
d="M0-8c16-28 34-32 46-18C36-12 20-4 0-8ZM0 8c16 28 34 32 46 18C36 12 20 4 0 8Z"
/>
<Circle cx={48} cy={-28} r={1.5} fill="#c59b4b" />
<Circle cx={48} cy={28} r={1.5} fill="#c59b4b" />
<Path
fill="none"
stroke="#c59b4b"
strokeWidth={1.1}
d="M0-35c24-21 52-15 56-1-14 6-32 12-56 1Zm0 70c24 21 52 15 56 1-14-6-32-12-56-1Z"
/>
<Path
fill="none"
stroke="#c59b4b"
d="M28-44c14-20 36-10 30 2-10 6-20 4-30-2Zm0 88c14 20 36 10 30-2-10-6-20-4-30 2Z"
/>
<Circle cx={60} cy={-44} r={1.2} fill="#c59b4b" />
<Circle cx={60} cy={44} r={1.2} fill="#c59b4b" />
<Circle cx={34} cy={-58} r={1.2} fill="#c59b4b" />
<Circle cx={34} cy={58} r={1.2} fill="#c59b4b" />
<Path
fill="none"
stroke="#c59b4b"
strokeLinecap="round"
d="M0-60c22-15 36-35 32-55-6 15-16 30-32 55M0 60c22 15 36 35 32 55-6-15-16-30-32-55"
/>
<Path
fill="none"
stroke="#c59b4b"
strokeWidth={0.9}
d="M14-78c8-6 12 2 6 6-4 0-6-4-6-6Zm0 156c8 6 12-2 6-6-4 0-6 4-6 6Z"
/>
<Circle cx={32} cy={-116} r={1.5} fill="#c59b4b" />
<Circle cx={32} cy={116} r={1.5} fill="#c59b4b" />
</G>
<G id="a">
<Path
fill="none"
stroke="#c59b4b"
strokeWidth={1.3}
d="M18 64c0-28 18-46 46-46"
/>
<Path
fill="none"
stroke="#c59b4b"
strokeWidth={0.8}
d="M24 52c0-16 12-28 28-28"
/>
<Path
fill="none"
stroke="#c59b4b"
strokeLinecap="round"
strokeWidth={0.9}
d="M28 32c4-6 10-4 8 2-2 4-8 2-6-4s10-6 14-2"
/>
<Circle cx={28} cy={28} r={1.8} fill="#c59b4b" />
<Circle cx={48} cy={18} r={1.4} fill="#c59b4b" />
<Circle cx={18} cy={48} r={1.4} fill="#c59b4b" />
</G>
</Defs>
<Path fill="#fdfdfd" d="M0 0h400v580H0z" />
<Path
fill="none"
stroke="#c59b4b"
strokeWidth={1.2}
d="M20 14h360v552H20z"
/>
<Use href="#a" />
<Use href="#a" transform="matrix(-1 0 0 1 400 0)" />
<Use href="#a" transform="matrix(1 0 0 -1 0 580)" />
<Use href="#a" transform="rotate(180 200 290)" />
<Path
fill="none"
stroke="#c59b4b"
strokeLinecap="round"
strokeWidth={1.2}
d="M132 62c8-4 16 2 24-2 4-2 2-6-2-4m114 6c-8-4-16 2-24-2-4-2-2-6 2-4"
/>
<Use href="#b" transform="translate(0 310)" />
<Use href="#b" transform="matrix(-1 0 0 1 400 310)" />
<Path
fill="none"
stroke="#c59b4b"
strokeLinecap="square"
strokeWidth={2.2}
d="M114 278v-58h54m118 58v-58h-54M114 342v58h54m118-58v58h-54"
/>
<G transform="translate(125 230)">
<Rect
width={150}
height={150}
fill="#fff"
stroke="#eae5d9"
strokeWidth={1.5}
rx={12}
/>
<Rect
width={134}
height={134}
x={8}
y={8}
fill="#fafaf8"
stroke="#c59b4b"
strokeDasharray="4 3"
strokeOpacity={0.35}
rx={8}
/>
<Text
x={75}
y={79}
fill="#c59b4b"
fontFamily="Inter, Arial, sans-serif"
fontSize={10}
fontWeight={700}
letterSpacing={1}
textAnchor="middle"
>
{"QR KOD ALANI"}
</Text>
</G>
<G fill="#111" textAnchor="middle">
<Text
x={200}
y={66}
fontFamily="'Playfair Display', 'Cinzel', 'Times New Roman', serif"
fontSize={17}
fontWeight={700}
letterSpacing={3}
>
{"THE"}
</Text>
<Text
x={200}
y={112}
fontFamily="'Playfair Display', 'Cinzel', 'Times New Roman', serif"
fontSize={34}
fontWeight={700}
letterSpacing={1.5}
>
{"CRIMSON"}
</Text>
<Text
x={200}
y={152}
fontFamily="'Playfair Display', 'Cinzel', 'Times New Roman', serif"
fontSize={34}
fontWeight={700}
letterSpacing={1.5}
>
{"PETAL"}
</Text>
<Text
x={200}
y={184}
fontFamily="Inter, Arial, sans-serif"
fontSize={11}
fontWeight={700}
letterSpacing={1.8}
>
{"FINE DINING EXPERIENCE"}
</Text>
<Text
x={200}
y={460}
fontFamily="Inter, Arial, sans-serif"
fontSize={22}
fontWeight={800}
letterSpacing={0.5}
>
{"SCAN FOR"}
</Text>
<Text
x={200}
y={488}
fontFamily="Inter, Arial, sans-serif"
fontSize={22}
fontWeight={800}
letterSpacing={0.5}
>
{"OUR MENU"}
</Text>
<Text
x={200}
y={526}
fontFamily="Inter, Arial, sans-serif"
fontSize={11.5}
fontWeight={600}
letterSpacing={0.3}
>
{"contactless ordering"}
</Text>
<Text
x={200}
y={544}
fontFamily="Inter, Arial, sans-serif"
fontSize={11.5}
fontWeight={600}
letterSpacing={0.3}
>
{"scan with camera"}
</Text>
</G>
</Svg>
)
const Memo = memo(SvgComponent)
export default Memo
+16 -62
View File
@@ -22,66 +22,9 @@ export interface QrStandPreset {
export const QR_STAND_PRESETS: Record<string, QrStandPreset> = { export const QR_STAND_PRESETS: Record<string, QrStandPreset> = {
"qr-1": { "qr-1": {
key: "qr-1", key: "qr-1",
name: "Gourmet Bistro Stand (QR-1)", name: "Lumière Restaurant (QR-1)",
categoryName: "Özel Masa Kartı", categoryName: "Özel Masa Kartı",
svgTemplateUrl: "/qr-1.svg", svgTemplateUrl: "/qr-1.svg",
aspectRatio: "400/650",
bgGradient: "radial-gradient(circle at 50% 50%, #1A1A1E 0%, #0C0C0E 100%)",
plaqueColor: "#1A1A1E",
borderAccent: "#D4AF37",
accentText: "#D4AF37",
headerText: "#FFFFFF",
subText: "#8E8E93",
dividerColor: "rgba(212, 175, 55, 0.3)",
},
"qr-4": {
key: "qr-4",
name: "Gourmet Gold Luxury Stand (QR-4)",
categoryName: "Özel Masa Kartı",
svgTemplateUrl: "/qr-4.svg",
aspectRatio: "400/650",
bgGradient: "radial-gradient(circle at 50% 50%, #451A03 0%, #1c0a02 100%)",
plaqueColor: "#000000",
borderAccent: "#D4AF37",
accentText: "#FFD700",
headerText: "#FFFFFF",
subText: "#A1A1AA",
dividerColor: "rgba(212, 175, 55, 0.3)",
},
"qr-5": {
key: "qr-5",
name: "L'Atelier Fine Dining Stand (QR-5)",
categoryName: "Özel Masa Kartı",
svgTemplateUrl: "/qr-5.svg",
aspectRatio: "400/650",
bgGradient: "radial-gradient(circle at 50% 50%, #171F2C 0%, #0B0E14 100%)",
plaqueColor: "#121721",
borderAccent: "#D4AF37",
accentText: "#E5C378",
headerText: "#F9F6F0",
subText: "#94A3B8",
dividerColor: "rgba(212, 175, 55, 0.3)",
},
"qr-6": {
key: "qr-6",
name: "Vintage Parşömen Stand (QR-6)",
categoryName: "Özel Masa Kartı",
svgTemplateUrl: "/qr-6.svg",
aspectRatio: "400/650",
bgGradient: "radial-gradient(circle at 50% 50%, #252C36 0%, #1A1F28 100%)",
plaqueColor: "#252C36",
borderAccent: "#D4AF37",
accentText: "#D4AF37",
headerText: "#1A1A1A",
subText: "#6B5A3E",
dividerColor: "rgba(212, 175, 55, 0.3)",
},
"qr-lumiere": {
key: "qr-lumiere",
name: "Lumière Restaurant (QR-L)",
categoryName: "Özel Masa Kartı",
svgTemplateUrl: "/qr-lumiere.svg",
aspectRatio: "1/1", aspectRatio: "1/1",
bgGradient: "radial-gradient(circle at 50% 50%, #132034 0%, #0b0d13 100%)", bgGradient: "radial-gradient(circle at 50% 50%, #132034 0%, #0b0d13 100%)",
plaqueColor: "#132034", plaqueColor: "#132034",
@@ -93,7 +36,7 @@ export const QR_STAND_PRESETS: Record<string, QrStandPreset> = {
}, },
"qr-2": { "qr-2": {
key: "qr-2", key: "qr-2",
name: "The Daily Grind (QR-2)", name: "The Daily Grind Cafe (QR-2)",
categoryName: "Özel Masa Kartı", categoryName: "Özel Masa Kartı",
svgTemplateUrl: "/qr-2.svg", svgTemplateUrl: "/qr-2.svg",
aspectRatio: "400/650", aspectRatio: "400/650",
@@ -119,7 +62,18 @@ export const QR_STAND_PRESETS: Record<string, QrStandPreset> = {
subText: "#5d4037", subText: "#5d4037",
dividerColor: "rgba(185, 43, 39, 0.3)", dividerColor: "rgba(185, 43, 39, 0.3)",
}, },
"qr-4": {
key: "qr-4",
name: "The Crimson Petal (QR-4)",
categoryName: "Özel Masa Kartı",
svgTemplateUrl: "/qr-4.svg",
aspectRatio: "400/580",
bgGradient: "radial-gradient(circle at 50% 50%, #fafafa 0%, #f0f0f0 100%)",
plaqueColor: "#fdfdfd",
borderAccent: "#c59b4b",
accentText: "#c59b4b",
headerText: "#111111",
subText: "#71717a",
dividerColor: "rgba(197, 155, 75, 0.3)",
},
}; };