feat(mobile): register QrTemplate2 Daily Grind and filter UI to active component templates

This commit is contained in:
AyrisAI
2026-08-21 00:30:14 +03:00
parent 8481a34e61
commit 34dd24eee2
4 changed files with 315 additions and 5 deletions
+14 -5
View File
@@ -33,21 +33,29 @@ export default function QrScreen() {
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 availablePresets = Object.values(QR_STAND_PRESETS).filter(
(p) => !!COMPONENT_TEMPLATES[p.key]
);
const [selectedKey, setSelectedKey] = useState<string>(
availablePresets[0]?.key || "qr-lumiere"
);
const [exportingPng, setExportingPng] = useState(false); const [exportingPng, setExportingPng] = useState(false);
const [exportingPdf, setExportingPdf] = useState(false); const [exportingPdf, setExportingPdf] = useState(false);
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
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] || availablePresets[0] || Object.values(QR_STAND_PRESETS)[0]!;
// Component-based template sistemi // Component-based template sistemi
const TemplateComponent = COMPONENT_TEMPLATES[selectedKey] ?? Object.values(COMPONENT_TEMPLATES)[0]; const TemplateComponent =
COMPONENT_TEMPLATES[selectedKey] ?? Object.values(COMPONENT_TEMPLATES)[0];
const aspectRatio = TEMPLATE_ASPECT_RATIOS[selectedKey] ?? 1; const aspectRatio = TEMPLATE_ASPECT_RATIOS[selectedKey] ?? 1;
const displayWidth = 300; const displayWidth = 300;
const displayHeight = Math.round(displayWidth / aspectRatio); const displayHeight = Math.round(displayWidth / aspectRatio);
useEffect(() => { useEffect(() => {
(async () => { (async () => {
const activeRest = await getActiveRestaurant(); const activeRest = await getActiveRestaurant();
@@ -230,14 +238,15 @@ export default function QrScreen() {
</Text> </Text>
{/* Horizontal Theme Selector */} {/* Horizontal Theme Selector */}
{Object.values(QR_STAND_PRESETS).length > 1 && ( {availablePresets.length > 1 && (
<ScrollView <ScrollView
horizontal horizontal
showsHorizontalScrollIndicator={false} showsHorizontalScrollIndicator={false}
contentContainerStyle={{ gap: 10, paddingHorizontal: 4, marginBottom: 20 }} contentContainerStyle={{ gap: 10, paddingHorizontal: 4, marginBottom: 20 }}
> >
{Object.values(QR_STAND_PRESETS).map((preset) => { {availablePresets.map((preset) => {
const isSelected = preset.key === selectedKey; const isSelected = preset.key === selectedKey;
return ( return (
<Pressable <Pressable
key={preset.key} key={preset.key}
@@ -0,0 +1,282 @@
import * as React from "react";
import Svg, {
SvgProps,
Path,
Rect,
Circle,
G,
Ellipse,
Text,
Image,
} from "react-native-svg";
interface QrTemplate2Props extends SvgProps {
qrBase64?: string;
}
const QrTemplate2 = ({ qrBase64, ...props }: QrTemplate2Props) => (
<Svg
viewBox="0 0 400 650"
width={300}
height={487.5}
{...props}
>
{/* Arka Plan */}
<Path fill="#252c36" d="M0 0h400v650H0z" />
{/* Ana Kart */}
<Rect
width={360}
height={610}
x={20}
y={20}
fill="#f6efe2"
stroke="#3a2312"
strokeWidth={1.5}
rx={20}
/>
<Rect
width={344}
height={594}
x={28}
y={28}
fill="none"
stroke="#8c6239"
strokeOpacity={0.4}
rx={14}
/>
<Rect
width={336}
height={586}
x={32}
y={32}
fill="none"
stroke="#8c6239"
strokeOpacity={0.25}
strokeWidth={0.5}
rx={10}
/>
{/* Sol Üst Botanik Dal */}
<G fill="none" stroke="#3a2312" strokeLinecap="round" strokeWidth={1.05}>
<Path d="M36 36c11.25 11.25 18.75 26.25 22.5 48.75" />
<Path
fill="#eadcc7"
d="M43.5 45c-3.75-7.5 7.5-10.5 9-3 0 7.5-9 3-9 3Zm6 11.25C45 51 57 46.5 60 53.25c0 6.75-10.5 3-10.5 3Zm4.5 13.5C60 64.5 69.75 72 64.5 78c-6 4.5-10.5-8.25-10.5-8.25Z"
/>
</G>
{/* Sağ Üst Kahve İkonu */}
<G transform="translate(340 52)">
<Circle r={32} fill="#eadcc7" stroke="#3a2312" strokeWidth={1.2} />
<Circle
r={26}
fill="#f6efe2"
stroke="#8c6239"
strokeDasharray="2 2"
strokeWidth={0.8}
/>
<Circle r={21} fill="#7a4b26" stroke="#3a2312" strokeWidth={1.4} />
<Path fill="#f6efe2" d="M0 10C-8 4-4-6 0-12 4-6 8 4 0 10" />
<Path fill="#7a4b26" d="M0 4C-5 0-2-5 0-8 2-5 5 0 0 4" />
<Circle cy={-14} r={1.8} fill="#f6efe2" />
</G>
{/* Sol Orta Kahve Bardağı */}
<G transform="translate(48 250)">
<Circle r={26} fill="#eadcc7" stroke="#3a2312" />
<Circle r={17} fill="#6a3b18" stroke="#3a2312" strokeWidth={1.2} />
<Path fill="#f6efe2" d="M-5-3c-3-4 5-7 5-1 0-6 8-3 5 1L0 7z" />
<Path fill="none" stroke="#3a2312" strokeWidth={1.5} d="M17-4c6 0 6 8 0 8" />
</G>
{/* Sağ Dal */}
<G fill="none" stroke="#3a2312" strokeWidth={0.91}>
<Path d="M352 230c-7 21-10.5 49-3.5 77" />
<Path
fill="#eadcc7"
d="M349.2 247.5c8.4-7 15.4 2.1 5.6 7.7-5.6 0-5.6-7.7-5.6-7.7Zm-2.8 24.5c-9.8-8.4-15.4 1.4-7 7 7 0 7-7 7-7Zm2.1 19.6c9.1-7 13.3 2.8 4.9 8.4-4.9 0-4.9-8.4-4.9-8.4Z"
/>
</G>
{/* Dekoratif Çizgi */}
<Path
fill="none"
stroke="#8c6239"
strokeLinecap="round"
strokeWidth={1.2}
d="M142 66c6-2 12 2 18 0m80 0c6 2 12-2 18 0"
/>
{/* Sol Alt Dal */}
<G fill="none" stroke="#3a2312" strokeWidth={0.91}>
<Path d="M42 601.5c13-26 9.75-52 0-71.5" />
<Path
fill="#eadcc7"
d="M47.2 549.5c10.4-9.75 17.55 0 6.5 7.8Zm2.6 26c11.7-9.75 18.2 1.3 5.2 9.1Z"
/>
</G>
{/* Kahve Çekirdekleri (arka plan süsü) */}
<G stroke="#3a2312" strokeWidth={1.2} transform="rotate(-20 1741.206 -8.75) scale(.8)">
<Ellipse fill="#cbb296" rx={9} ry={6.5} />
<Path fill="none" d="M-7 0q7 3 7 0t7 0" />
</G>
<G stroke="#3a2312" strokeWidth={1.2} transform="rotate(35 -862.106 494.982) scale(.8)">
<Ellipse fill="#cbb296" rx={9} ry={6.5} />
<Path fill="none" d="M-7 0q7 3 7 0t7 0" />
</G>
<G stroke="#3a2312" strokeWidth={1.2} transform="rotate(-45 602.558 -225.588) scale(.7)">
<Ellipse fill="#cbb296" rx={9} ry={6.5} />
<Path fill="none" d="M-7 0q7 3 7 0t7 0" />
</G>
{/* Sağ Alt Fincan */}
<G transform="translate(325 590)">
<Ellipse cy={18} fill="#eadcc7" stroke="#3a2312" strokeWidth={1.2} rx={28} ry={7} />
<Path fill="#f6efe2" stroke="#3a2312" strokeWidth={1.4} d="M-18 2c0 14 36 14 36 0Z" />
<Ellipse cy={2} fill="#6a3b18" stroke="#3a2312" strokeWidth={1.2} rx={18} ry={4} />
<Path fill="none" stroke="#3a2312" strokeWidth={1.3} d="M16 4c8 0 8 8 0 10" />
<Path fill="none" stroke="#8c6239" strokeLinecap="round" d="M-6-4c-2-5 2-8 0-13M2-5c-2-5 2-8 0-14" />
</G>
{/* QR Köşe Çerçeveleri */}
<Path
fill="none"
stroke="#3a2312"
strokeLinecap="round"
strokeWidth={2}
d="M80 205c0-10 0-10 10-10m230 10c0-10 0-10-10-10M80 445c0 10 0 10 10 10m230-10c0 10 0 10-10 10"
/>
<Path stroke="#8c6239" strokeWidth={0.8} d="M145 608h30m50 0h30" />
{/* QR Placeholder / Gerçek QR */}
<G transform="translate(90 215)">
<Rect width={220} height={220} fill="#fbf8f2" stroke="#d8c7b0" strokeWidth={1.5} rx={20} />
{qrBase64 ? (
<Image
href={`data:image/png;base64,${qrBase64}`}
x={10}
y={10}
width={200}
height={200}
/>
) : (
<>
<Rect
width={200}
height={200}
x={10}
y={10}
fill="#f5efe6"
stroke="#d8c7b0"
strokeDasharray="4 3"
rx={14}
/>
<Circle cx={110} cy={98} r={18} fill="#fbf8f2" stroke="#8c6239" />
<G fill="#3a2312" transform="matrix(.65 0 0 .65 110 98)">
<Ellipse cx={-5} cy={2} rx={7} ry={5} transform="rotate(-30)" />
<Ellipse cx={5} rx={7} ry={5} transform="rotate(40)" />
</G>
<Text
x={110}
y={132}
fill="#8c6239"
fontFamily="Inter, Arial, sans-serif"
fontSize={11}
fontWeight={700}
letterSpacing={1}
textAnchor="middle"
>
{"QR KOD ALANI"}
</Text>
</>
)}
</G>
{/* Metinler */}
<G textAnchor="middle">
<Text
x={200}
y={70}
fill="#3a2312"
fontFamily="'Playfair Display', Georgia, serif"
fontSize={14}
fontStyle="italic"
fontWeight={700}
letterSpacing={4}
>
{"THE"}
</Text>
<Text
x={200}
y={114}
fill="#2c1a0e"
fontFamily="'Playfair Display', Georgia, serif"
fontSize={34}
fontWeight={800}
letterSpacing={0.5}
>
{"The Daily Grind"}
</Text>
<Text
x={200}
y={146}
fill="#3a2312"
fontFamily="Inter, Arial, sans-serif"
fontSize={13}
fontWeight={800}
letterSpacing={3}
>
{"COFFEE & KITCHEN"}
</Text>
<Text
x={200}
y={488}
fill="#2c1a0e"
fontFamily="Inter, Arial, sans-serif"
fontSize={19}
fontWeight={900}
letterSpacing={1.5}
>
{"SCAN TO VIEW MENU"}
</Text>
<Text
x={200}
y={520}
fill="#3a2312"
fontFamily="Inter, Arial, sans-serif"
fontSize={10.5}
fontWeight={700}
letterSpacing={0.5}
>
{"DAILY GRIND CAFE | WIFI: DAILY_GRIND_GUEST"}
</Text>
<Text
x={200}
y={542}
fill="#8c6239"
fontFamily="Inter, Arial, sans-serif"
fontSize={9}
fontWeight={600}
letterSpacing={1}
>
{"\u015E\u0130FRE: coffee2026 \u2022 MASA NO: 04"}
</Text>
<Text
x={200}
y={611}
fill="#3a2312"
fontFamily="'Playfair Display', Georgia, serif"
fontSize={11}
fontStyle="italic"
fontWeight={700}
letterSpacing={2}
>
{"EST. 2018"}
</Text>
</G>
</Svg>
);
export default QrTemplate2;
@@ -1,6 +1,7 @@
import type React from "react"; import type React from "react";
import type { SvgProps } from "react-native-svg"; import type { SvgProps } from "react-native-svg";
import QrTemplateLumiere from "./QrTemplateLumiere"; import QrTemplateLumiere from "./QrTemplateLumiere";
import QrTemplate2 from "./QrTemplate2";
import QrTemplate3 from "./QrTemplate3"; import QrTemplate3 from "./QrTemplate3";
export interface QrTemplateProps extends SvgProps { export interface QrTemplateProps extends SvgProps {
@@ -13,11 +14,14 @@ export const COMPONENT_TEMPLATES: Record<
React.FC<QrTemplateProps> React.FC<QrTemplateProps>
> = { > = {
"qr-lumiere": QrTemplateLumiere, "qr-lumiere": QrTemplateLumiere,
"qr-2": QrTemplate2,
"qr-3": QrTemplate3, "qr-3": QrTemplate3,
}; };
// Her template'in aspect ratio'su (width/height) // Her template'in aspect ratio'su (width/height)
export const TEMPLATE_ASPECT_RATIOS: Record<string, number> = { export const TEMPLATE_ASPECT_RATIOS: Record<string, number> = {
"qr-lumiere": 1, // 600x600 = 1:1 "qr-lumiere": 1, // 600x600 = 1:1
"qr-2": 400 / 650, // 400x650 = standart kart
"qr-3": 400 / 650, // 400x650 = standart kart "qr-3": 400 / 650, // 400x650 = standart kart
}; };
+15
View File
@@ -91,6 +91,20 @@ export const QR_STAND_PRESETS: Record<string, QrStandPreset> = {
subText: "#e5c378", subText: "#e5c378",
dividerColor: "rgba(212, 175, 55, 0.3)", dividerColor: "rgba(212, 175, 55, 0.3)",
}, },
"qr-2": {
key: "qr-2",
name: "The Daily Grind (QR-2)",
categoryName: "Özel Masa Kartı",
svgTemplateUrl: "/qr-2.svg",
aspectRatio: "400/650",
bgGradient: "radial-gradient(circle at 50% 50%, #252c36 0%, #171c22 100%)",
plaqueColor: "#f6efe2",
borderAccent: "#3a2312",
accentText: "#8c6239",
headerText: "#2c1a0e",
subText: "#3a2312",
dividerColor: "rgba(140, 98, 57, 0.3)",
},
"qr-3": { "qr-3": {
key: "qr-3", key: "qr-3",
name: "Pizzeria Bella Napoli (QR-3)", name: "Pizzeria Bella Napoli (QR-3)",
@@ -108,3 +122,4 @@ export const QR_STAND_PRESETS: Record<string, QrStandPreset> = {
}; };