feat(mobile): register QrTemplate2 Daily Grind and filter UI to active component templates
This commit is contained in:
@@ -33,21 +33,29 @@ export default function QrScreen() {
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
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 [exportingPdf, setExportingPdf] = useState(false);
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
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
|
||||
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 displayWidth = 300;
|
||||
const displayHeight = Math.round(displayWidth / aspectRatio);
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
const activeRest = await getActiveRestaurant();
|
||||
@@ -230,14 +238,15 @@ export default function QrScreen() {
|
||||
</Text>
|
||||
|
||||
{/* Horizontal Theme Selector */}
|
||||
{Object.values(QR_STAND_PRESETS).length > 1 && (
|
||||
{availablePresets.length > 1 && (
|
||||
<ScrollView
|
||||
horizontal
|
||||
showsHorizontalScrollIndicator={false}
|
||||
contentContainerStyle={{ gap: 10, paddingHorizontal: 4, marginBottom: 20 }}
|
||||
>
|
||||
{Object.values(QR_STAND_PRESETS).map((preset) => {
|
||||
{availablePresets.map((preset) => {
|
||||
const isSelected = preset.key === selectedKey;
|
||||
|
||||
return (
|
||||
<Pressable
|
||||
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 { SvgProps } from "react-native-svg";
|
||||
import QrTemplateLumiere from "./QrTemplateLumiere";
|
||||
import QrTemplate2 from "./QrTemplate2";
|
||||
import QrTemplate3 from "./QrTemplate3";
|
||||
|
||||
export interface QrTemplateProps extends SvgProps {
|
||||
@@ -13,11 +14,14 @@ export const COMPONENT_TEMPLATES: Record<
|
||||
React.FC<QrTemplateProps>
|
||||
> = {
|
||||
"qr-lumiere": QrTemplateLumiere,
|
||||
"qr-2": QrTemplate2,
|
||||
"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-2": 400 / 650, // 400x650 = standart kart
|
||||
"qr-3": 400 / 650, // 400x650 = standart kart
|
||||
};
|
||||
|
||||
|
||||
@@ -91,6 +91,20 @@ export const QR_STAND_PRESETS: Record<string, QrStandPreset> = {
|
||||
subText: "#e5c378",
|
||||
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": {
|
||||
key: "qr-3",
|
||||
name: "Pizzeria Bella Napoli (QR-3)",
|
||||
@@ -108,3 +122,4 @@ export const QR_STAND_PRESETS: Record<string, QrStandPreset> = {
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user