Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e7ba6c7807 | ||
|
|
c1569c0879 | ||
|
|
ae2386eacf | ||
|
|
d00b45ce72 | ||
|
|
102a9a744b | ||
|
|
b0645c0ac3 | ||
|
|
2c3599839b | ||
|
|
2530ab4005 | ||
|
|
b72846bd91 | ||
|
|
d2846ed628 | ||
|
|
4dbb68a73a | ||
|
|
932c174b5f | ||
|
|
34dd24eee2 | ||
|
|
8481a34e61 | ||
|
|
ccd8c90267 | ||
|
|
3629f98855 | ||
|
|
dd282f3987 | ||
|
|
4236ddb5ee | ||
|
|
c7e4832441 | ||
|
|
fa50e73198 | ||
|
|
808097377d | ||
|
|
6cde8dfb87 | ||
|
|
01c9f5f195 | ||
|
|
f768b92873 | ||
|
|
eb0b5d1886 | ||
|
|
38fd0dd030 | ||
|
|
18396f3b99 | ||
|
|
edb7af9288 | ||
|
|
a0cb04b2c6 | ||
|
|
4ce6505a4d |
@@ -5,6 +5,10 @@ build/
|
||||
.expo/
|
||||
.turbo/
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
*.tsbuildinfo
|
||||
.env
|
||||
.env.local
|
||||
@@ -15,3 +19,7 @@ supabase/.temp/
|
||||
coverage/
|
||||
*.ipa
|
||||
*.apk
|
||||
|
||||
# Lockfile protections (monorepo strictly uses pnpm-lock.yaml)
|
||||
package-lock.json
|
||||
yarn.lock
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
auto-install-peers=true
|
||||
strict-peer-dependencies=false
|
||||
enable-pre-post-scripts=true
|
||||
only-built-dependencies=core-js,@swc/core,esbuild,sharp,@esbuild/darwin-arm64,@next/swc-darwin-arm64
|
||||
@@ -0,0 +1,203 @@
|
||||
[
|
||||
{
|
||||
"key": "qr-1",
|
||||
"name": "Lumière Restaurant (QR-1)",
|
||||
"categoryName": "Özel Masa Kartı",
|
||||
"svgTemplateUrl": "/qr-1.svg",
|
||||
"aspectRatio": "1/1",
|
||||
"bgType": "gradient",
|
||||
"bgGradient": "radial-gradient(circle at 50% 50%, #132034 0%, #0b0d13 100%)",
|
||||
"plaqueColor": "#132034",
|
||||
"borderAccent": "#d4af37",
|
||||
"accentText": "#d4af37",
|
||||
"headerText": "#FFFFFF",
|
||||
"subText": "#e5c378",
|
||||
"dividerColor": "rgba(212, 175, 55, 0.3)",
|
||||
"isActive": true,
|
||||
"qrConfig": {
|
||||
"xPercent": 50,
|
||||
"yPercent": 38,
|
||||
"size": 115,
|
||||
"borderRadius": 14,
|
||||
"borderColor": "#D4AF37",
|
||||
"bgBoxColor": "#FFFFFF"
|
||||
},
|
||||
"layers": [
|
||||
{
|
||||
"id": "rName",
|
||||
"text": "{{RESTAURANT_NAME}}",
|
||||
"yPercent": 16,
|
||||
"fontSize": 18,
|
||||
"fontFamily": "Playfair Display, Georgia, serif",
|
||||
"color": "#D4AF37",
|
||||
"fontWeight": "800",
|
||||
"letterSpacing": 2
|
||||
},
|
||||
{
|
||||
"id": "sub",
|
||||
"text": "RESTAURANT & BAR",
|
||||
"yPercent": 26,
|
||||
"fontSize": 10,
|
||||
"fontFamily": "Plus Jakarta Sans, sans-serif",
|
||||
"color": "#E5C378",
|
||||
"fontWeight": "600",
|
||||
"letterSpacing": 1.5
|
||||
},
|
||||
{
|
||||
"id": "cta",
|
||||
"text": "MENÜYÜ GÖRMEK İÇİN OKUTUN",
|
||||
"yPercent": 80,
|
||||
"fontSize": 11,
|
||||
"fontFamily": "Playfair Display, Georgia, serif",
|
||||
"color": "#FFFFFF",
|
||||
"fontWeight": "700",
|
||||
"letterSpacing": 1
|
||||
},
|
||||
{
|
||||
"id": "table",
|
||||
"text": "MASA NO: 01 • WIFI: Bistro",
|
||||
"yPercent": 90,
|
||||
"fontSize": 9,
|
||||
"fontFamily": "Plus Jakarta Sans, sans-serif",
|
||||
"color": "#A1A1AA",
|
||||
"fontWeight": "600"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "qr-2",
|
||||
"name": "The Daily Grind Cafe (QR-2)",
|
||||
"categoryName": "Özel Masa Kartı",
|
||||
"svgTemplateUrl": "/qr-2.svg",
|
||||
"aspectRatio": "400/650",
|
||||
"bgType": "gradient",
|
||||
"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)",
|
||||
"isActive": true,
|
||||
"qrConfig": {
|
||||
"xPercent": 50,
|
||||
"yPercent": 36,
|
||||
"size": 125,
|
||||
"borderRadius": 14,
|
||||
"borderColor": "#3a2312",
|
||||
"bgBoxColor": "#FFFFFF"
|
||||
},
|
||||
"layers": [
|
||||
{
|
||||
"id": "rName",
|
||||
"text": "{{RESTAURANT_NAME}}",
|
||||
"yPercent": 14,
|
||||
"fontSize": 18,
|
||||
"fontFamily": "Playfair Display, Georgia, serif",
|
||||
"color": "#8c6239",
|
||||
"fontWeight": "800",
|
||||
"letterSpacing": 2
|
||||
},
|
||||
{
|
||||
"id": "cta",
|
||||
"text": "MENÜYÜ GÖRMEK İÇİN OKUTUN",
|
||||
"yPercent": 82,
|
||||
"fontSize": 11,
|
||||
"fontFamily": "Playfair Display, Georgia, serif",
|
||||
"color": "#2c1a0e",
|
||||
"fontWeight": "700",
|
||||
"letterSpacing": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "qr-3",
|
||||
"name": "Pizzeria Bella Napoli (QR-3)",
|
||||
"categoryName": "Özel Masa Kartı",
|
||||
"svgTemplateUrl": "/qr-3.svg",
|
||||
"aspectRatio": "400/650",
|
||||
"bgType": "gradient",
|
||||
"bgGradient": "radial-gradient(circle at 50% 50%, #faf4e6 0%, #f7eed8 100%)",
|
||||
"plaqueColor": "#fdfbf5",
|
||||
"borderAccent": "#b92b27",
|
||||
"accentText": "#b92b27",
|
||||
"headerText": "#18181b",
|
||||
"subText": "#5d4037",
|
||||
"dividerColor": "rgba(185, 43, 39, 0.3)",
|
||||
"isActive": true,
|
||||
"qrConfig": {
|
||||
"xPercent": 50,
|
||||
"yPercent": 36,
|
||||
"size": 125,
|
||||
"borderRadius": 14,
|
||||
"borderColor": "#b92b27",
|
||||
"bgBoxColor": "#FFFFFF"
|
||||
},
|
||||
"layers": [
|
||||
{
|
||||
"id": "rName",
|
||||
"text": "{{RESTAURANT_NAME}}",
|
||||
"yPercent": 14,
|
||||
"fontSize": 18,
|
||||
"fontFamily": "Playfair Display, Georgia, serif",
|
||||
"color": "#b92b27",
|
||||
"fontWeight": "800",
|
||||
"letterSpacing": 2
|
||||
},
|
||||
{
|
||||
"id": "cta",
|
||||
"text": "MENÜYÜ GÖRMEK İÇİN OKUTUN",
|
||||
"yPercent": 82,
|
||||
"fontSize": 11,
|
||||
"fontFamily": "Playfair Display, Georgia, serif",
|
||||
"color": "#18181b",
|
||||
"fontWeight": "700",
|
||||
"letterSpacing": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "qr-8",
|
||||
"name": "Emerald & Gold Special (QR-8)",
|
||||
"categoryName": "Özel Kare Masa Kartı",
|
||||
"svgTemplateUrl": "/qr-8.svg",
|
||||
"aspectRatio": "1/1",
|
||||
"bgType": "gradient",
|
||||
"bgGradient": "radial-gradient(circle at 50% 50%, #0c4443 0%, #062423 100%)",
|
||||
"plaqueColor": "#f9f5eb",
|
||||
"borderAccent": "#d1a752",
|
||||
"accentText": "#d1a752",
|
||||
"headerText": "#ffffff",
|
||||
"subText": "#d1a752",
|
||||
"dividerColor": "rgba(209, 167, 82, 0.4)",
|
||||
"isActive": true,
|
||||
"qrConfig": {
|
||||
"xPercent": 50,
|
||||
"yPercent": 38,
|
||||
"size": 115,
|
||||
"borderRadius": 14,
|
||||
"borderColor": "#d1a752",
|
||||
"bgBoxColor": "#FFFFFF"
|
||||
},
|
||||
"layers": [
|
||||
{
|
||||
"id": "rName",
|
||||
"text": "{{RESTAURANT_NAME}}",
|
||||
"yPercent": 16,
|
||||
"fontSize": 18,
|
||||
"fontFamily": "Playfair Display, Georgia, serif",
|
||||
"color": "#d1a752",
|
||||
"fontWeight": "800"
|
||||
},
|
||||
{
|
||||
"id": "cta",
|
||||
"text": "MENÜYÜ GÖRMEK İÇİN OKUTUN",
|
||||
"yPercent": 80,
|
||||
"fontSize": 11,
|
||||
"fontFamily": "Playfair Display, Georgia, serif",
|
||||
"color": "#ffffff",
|
||||
"fontWeight": "700"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -9,6 +9,7 @@ import { menuItemsRoutes } from "./routes/menu-items.js";
|
||||
import { menusRoutes } from "./routes/menus.js";
|
||||
import { meRoutes } from "./routes/me.js";
|
||||
import { qrRoutes } from "./routes/qr.js";
|
||||
import { qrTemplatesRoutes } from "./routes/qr-templates.js";
|
||||
import { restaurantsRoutes } from "./routes/restaurants.js";
|
||||
import { subscriptionRoutes } from "./routes/subscription.js";
|
||||
|
||||
@@ -42,6 +43,7 @@ await app.register(menusRoutes);
|
||||
await app.register(menuCategoriesRoutes);
|
||||
await app.register(menuItemsRoutes);
|
||||
await app.register(qrRoutes);
|
||||
await app.register(qrTemplatesRoutes);
|
||||
await app.register(aiImportsRoutes);
|
||||
await app.register(domainsRoutes);
|
||||
await app.register(analyticsRoutes);
|
||||
|
||||
@@ -0,0 +1,156 @@
|
||||
import type { FastifyInstance } from "fastify";
|
||||
import fs from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { type QrStandPreset } from "@menulio/shared";
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const DATA_FILE = path.resolve(__dirname, "../data/qr-templates.json");
|
||||
|
||||
export interface DynamicQrTemplate extends QrStandPreset {
|
||||
id: string;
|
||||
isActive: boolean;
|
||||
bgType?: "gradient" | "image" | "svg";
|
||||
bgImageUrl?: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
defaultQrConfig?: {
|
||||
xPercent: number;
|
||||
yPercent: number;
|
||||
size: number;
|
||||
borderRadius: number;
|
||||
borderColor: string;
|
||||
bgBoxColor: string;
|
||||
};
|
||||
defaultLayers?: Array<{
|
||||
id: string;
|
||||
text: string;
|
||||
xPercent?: number;
|
||||
yPercent: number;
|
||||
fontSize: number;
|
||||
fontFamily?: string;
|
||||
color: string;
|
||||
fontWeight?: string;
|
||||
}>;
|
||||
}
|
||||
|
||||
async function loadTemplatesFromDisk(): Promise<DynamicQrTemplate[]> {
|
||||
try {
|
||||
const content = await fs.readFile(DATA_FILE, "utf-8");
|
||||
const parsed = JSON.parse(content);
|
||||
return parsed.map((p: any) => ({
|
||||
...p,
|
||||
id: p.key,
|
||||
isActive: p.isActive ?? true,
|
||||
createdAt: p.createdAt || new Date().toISOString(),
|
||||
updatedAt: p.updatedAt || new Date().toISOString(),
|
||||
defaultQrConfig: p.defaultQrConfig || p.qrConfig,
|
||||
defaultLayers: p.defaultLayers || p.layers,
|
||||
}));
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
async function saveTemplatesToDisk(templates: DynamicQrTemplate[]): Promise<void> {
|
||||
try {
|
||||
await fs.writeFile(DATA_FILE, JSON.stringify(templates, null, 2), "utf-8");
|
||||
} catch (err) {
|
||||
console.error("Failed to write qr-templates to disk:", err);
|
||||
}
|
||||
}
|
||||
|
||||
export async function qrTemplatesRoutes(app: FastifyInstance) {
|
||||
// 1. Tüm Aktif Şablonları Listele (Mobil ve Web İçin)
|
||||
app.get("/qr-templates", async () => {
|
||||
const list = await loadTemplatesFromDisk();
|
||||
return {
|
||||
templates: list.filter((t) => t.isActive),
|
||||
total: list.length,
|
||||
};
|
||||
});
|
||||
|
||||
// 2. Admin Şablon Listesi (Aktif + Taslak hepsi)
|
||||
app.get("/admin/qr-templates", async () => {
|
||||
const list = await loadTemplatesFromDisk();
|
||||
return {
|
||||
templates: list,
|
||||
total: list.length,
|
||||
};
|
||||
});
|
||||
|
||||
// 3. Yeni Şablon Ekle veya Mevcut Olanı Güncelle
|
||||
app.post<{ Body: Partial<DynamicQrTemplate> }>("/admin/qr-templates", async (req, reply) => {
|
||||
const body = req.body;
|
||||
if (!body.name) {
|
||||
return reply.status(400).send({ error: "Şablon adı zorunludur." });
|
||||
}
|
||||
|
||||
const key = body.key || `qr-${Date.now()}`;
|
||||
const list = await loadTemplatesFromDisk();
|
||||
const existingIndex = list.findIndex((t) => t.id === key || t.key === key);
|
||||
|
||||
const now = new Date().toISOString();
|
||||
const newTemplate: DynamicQrTemplate = {
|
||||
key,
|
||||
id: key,
|
||||
name: body.name,
|
||||
categoryName: body.categoryName || "Özel Şablon",
|
||||
svgTemplateUrl: body.svgTemplateUrl || `/templates/${key}.svg`,
|
||||
aspectRatio: body.aspectRatio || "1/1",
|
||||
bgType: body.bgType || "gradient",
|
||||
bgImageUrl: body.bgImageUrl,
|
||||
bgGradient: body.bgGradient || "radial-gradient(circle at 50% 50%, #132034 0%, #0b0d13 100%)",
|
||||
plaqueColor: body.plaqueColor || "#132034",
|
||||
borderAccent: body.borderAccent || "#D4AF37",
|
||||
accentText: body.accentText || "#D4AF37",
|
||||
headerText: body.headerText || "#FFFFFF",
|
||||
subText: body.subText || "#E5C378",
|
||||
dividerColor: body.dividerColor || "rgba(212, 175, 55, 0.3)",
|
||||
isActive: body.isActive !== undefined ? body.isActive : true,
|
||||
createdAt: existingIndex >= 0 ? list[existingIndex].createdAt : now,
|
||||
updatedAt: now,
|
||||
defaultQrConfig: body.defaultQrConfig || (body as any).qrConfig || {
|
||||
xPercent: 50,
|
||||
yPercent: 38,
|
||||
size: 115,
|
||||
borderRadius: 14,
|
||||
borderColor: body.borderAccent || "#D4AF37",
|
||||
bgBoxColor: "#FFFFFF",
|
||||
},
|
||||
defaultLayers: body.defaultLayers || (body as any).layers || [
|
||||
{
|
||||
id: "rName",
|
||||
text: "{{RESTAURANT_NAME}}",
|
||||
yPercent: 16,
|
||||
fontSize: 18,
|
||||
color: body.accentText || "#D4AF37",
|
||||
fontWeight: "800",
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
if (existingIndex >= 0) {
|
||||
list[existingIndex] = newTemplate;
|
||||
} else {
|
||||
list.unshift(newTemplate);
|
||||
}
|
||||
|
||||
await saveTemplatesToDisk(list);
|
||||
return { success: true, template: newTemplate };
|
||||
});
|
||||
|
||||
// 4. Şablon Sil
|
||||
app.delete<{ Params: { id: string } }>("/admin/qr-templates/:id", async (req, reply) => {
|
||||
const { id } = req.params;
|
||||
const list = await loadTemplatesFromDisk();
|
||||
const filtered = list.filter((t) => t.id !== id && t.key !== id);
|
||||
|
||||
if (filtered.length === list.length) {
|
||||
return reply.status(404).send({ error: "Şablon bulunamadı." });
|
||||
}
|
||||
|
||||
await saveTemplatesToDisk(filtered);
|
||||
return { success: true, deletedId: id };
|
||||
});
|
||||
}
|
||||
@@ -7,7 +7,8 @@
|
||||
"orientation": "portrait",
|
||||
"icon": "./assets/icon.png",
|
||||
"userInterfaceStyle": "automatic",
|
||||
"newArchEnabled": false,
|
||||
"newArchEnabled": true,
|
||||
"platforms": ["ios", "android"],
|
||||
"splash": {
|
||||
"image": "./assets/splash.png",
|
||||
"resizeMode": "contain",
|
||||
|
||||
|
Before Width: | Height: | Size: 1.6 MiB After Width: | Height: | Size: 4.5 KiB |
|
Before Width: | Height: | Size: 357 KiB After Width: | Height: | Size: 4.5 KiB |
@@ -0,0 +1,71 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 400 650" width="400" height="650">
|
||||
<defs>
|
||||
<!-- Define a linear gradient for gold effect -->
|
||||
<linearGradient id="goldGradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" stop-color="#D4AF37"/>
|
||||
<stop offset="100%" stop-color="#FFD700"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<!-- Background Canvas -->
|
||||
<rect id="bg-canvas" fill="#451A03" width="400" height="650"/>
|
||||
<!-- Main Menu Card Panel -->
|
||||
<rect id="main-card" x="20" y="20" width="360" height="610" rx="24" fill="#000000" stroke="url(#goldGradient)" stroke-width="2"/>
|
||||
<!-- Layer for Icons/Graphics -->
|
||||
<g id="layer-icons">
|
||||
<!-- Top decorative line -->
|
||||
<line x1="80" y1="165" x2="320" y2="165" stroke="url(#goldGradient)" stroke-width="1" fill="#D97706"/>
|
||||
<!-- Bottom decorative line -->
|
||||
<line x1="80" y1="535" x2="320" y2="535" stroke="url(#goldGradient)" stroke-width="1" fill="#B45309"/>
|
||||
<!-- Subtle corner accents (L-shapes) -->
|
||||
<path d="M40 40 L40 70 M40 40 L70 40" stroke="url(#goldGradient)" stroke-width="1.5" stroke-linecap="round" fill="#D97706"/>
|
||||
<path d="M360 40 L360 70 M360 40 L330 40" stroke="url(#goldGradient)" stroke-width="1.5" stroke-linecap="round" fill="#B45309"/>
|
||||
<path d="M40 610 L40 580 M40 610 L70 610" stroke="url(#goldGradient)" stroke-width="1.5" stroke-linecap="round" fill="#D97706"/>
|
||||
<path d="M360 610 L360 580 M360 610 L330 610" stroke="url(#goldGradient)" stroke-width="1.5" stroke-linecap="round" fill="#B45309"/>
|
||||
<!-- Minimalist Fork and Knife Line Art -->
|
||||
<g transform="translate(0, 15)">
|
||||
<!-- Fork -->
|
||||
<path d="M185 550 L185 560 M188 550 L188 560 M191 550 L191 560 M188 560 L188 565" stroke="url(#goldGradient)" stroke-width="1.5" stroke-linecap="round" fill="#D97706"/>
|
||||
<!-- Knife -->
|
||||
<path d="M205 550 L205 560 L208 560 L208 552 C208 550 206.5 549 205 549 Z M206.5 560 L206.5 565" stroke="url(#goldGradient)" stroke-width="1.5" stroke-linecap="round" fill="#B45309"/>
|
||||
</g>
|
||||
</g>
|
||||
<!-- QR Placeholder -->
|
||||
<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>
|
||||
<!-- Layer for Texts -->
|
||||
<g id="layer-texts" fill="#c10101">
|
||||
<!-- Table Info badge -->
|
||||
<rect x="140" y="40" width="120" height="30" rx="15" fill="url(#goldGradient)"/>
|
||||
<text x="200" y="60" font-family="Inter, sans-serif" font-size="14" font-weight="600" fill="#1A1A1D" text-anchor="middle">
|
||||
MASA NO: 01
|
||||
</text>
|
||||
<!-- Restaurant Name -->
|
||||
<text x="200" y="120" font-family="Inter, sans-serif" font-size="26" font-weight="700" fill="url(#goldGradient)" text-anchor="middle">
|
||||
Gourmet Bistro
|
||||
</text>
|
||||
<!-- Tagline / Subtitle -->
|
||||
<text x="200" y="145" font-family="Inter, sans-serif" font-size="12" font-weight="400" fill="#E4E4E7" text-anchor="middle">
|
||||
Temassız Dijital Menü
|
||||
</text>
|
||||
<!-- "MENÜYÜ GÖRMEK İÇİN OKUTUN" -->
|
||||
<text x="200" y="480" font-family="Inter, sans-serif" font-size="16" font-weight="700" fill="url(#goldGradient)" text-anchor="middle">
|
||||
MENÜYÜ GÖRMEK İÇİN OKUTUN
|
||||
</text>
|
||||
<!-- Instruction note -->
|
||||
<text x="200" y="505" font-family="Inter, sans-serif" font-size="11" font-weight="400" fill="#A1A1AA" text-anchor="middle">
|
||||
Kameranızı QR Koda Odaklayın
|
||||
</text>
|
||||
<!-- WiFi & Social Media footer info -->
|
||||
<text x="200" y="595" font-family="Inter, sans-serif" font-size="11" font-weight="400" fill="#A1A1AA" text-anchor="middle">
|
||||
Ücretsiz Wi-Fi: GourmetBistro_Guest
|
||||
</text>
|
||||
<text x="200" y="610" font-family="Inter, sans-serif" font-size="11" font-weight="400" fill="#A1A1AA" text-anchor="middle">
|
||||
Bizi Takip Edin: @GourmetBistro
|
||||
</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.0 KiB |
@@ -0,0 +1,285 @@
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 400 650"
|
||||
width="400"
|
||||
height="650"
|
||||
>
|
||||
<g id="layer-background">
|
||||
<rect
|
||||
id="bg-canvas"
|
||||
x="0"
|
||||
y="0"
|
||||
width="400"
|
||||
height="650"
|
||||
fill="#0B0E14"
|
||||
/>
|
||||
</g>
|
||||
|
||||
<g id="layer-card">
|
||||
<!-- Ana Lüks Kart Arka Planı (Gece Yarısı Laciverti & Kömür) -->
|
||||
<rect
|
||||
id="main-card"
|
||||
x="20"
|
||||
y="20"
|
||||
width="360"
|
||||
height="610"
|
||||
rx="24"
|
||||
fill="#121721"
|
||||
stroke="#D4AF37"
|
||||
stroke-width="1.5"
|
||||
/>
|
||||
|
||||
<!-- İç Çift Altın Bordür Hattı -->
|
||||
<rect
|
||||
id="inner-border"
|
||||
x="28"
|
||||
y="28"
|
||||
width="344"
|
||||
height="594"
|
||||
rx="18"
|
||||
fill="none"
|
||||
stroke="#C5A059"
|
||||
stroke-width="0.8"
|
||||
stroke-opacity="0.4"
|
||||
/>
|
||||
<rect
|
||||
x="32"
|
||||
y="32"
|
||||
width="336"
|
||||
height="586"
|
||||
rx="14"
|
||||
fill="none"
|
||||
stroke="#C5A059"
|
||||
stroke-width="0.5"
|
||||
stroke-opacity="0.2"
|
||||
/>
|
||||
</g>
|
||||
|
||||
<g id="layer-icons">
|
||||
<!-- Üst Köşe Geometrik Art-Deco Motifleri -->
|
||||
<path
|
||||
d="M 38 48 L 48 38 M 38 38 L 52 38 M 38 38 L 38 52"
|
||||
stroke="#D4AF37"
|
||||
stroke-width="1"
|
||||
stroke-linecap="round"
|
||||
stroke-opacity="0.6"
|
||||
fill="none"
|
||||
/>
|
||||
<path
|
||||
d="M 362 48 L 352 38 M 362 38 L 348 38 M 362 38 L 362 52"
|
||||
stroke="#D4AF37"
|
||||
stroke-width="1"
|
||||
stroke-linecap="round"
|
||||
stroke-opacity="0.6"
|
||||
fill="none"
|
||||
/>
|
||||
|
||||
<!-- Üst Masa Numarası Kapsülü -->
|
||||
<rect
|
||||
x="148"
|
||||
y="44"
|
||||
width="104"
|
||||
height="22"
|
||||
rx="11"
|
||||
fill="#1A2230"
|
||||
stroke="#D4AF37"
|
||||
stroke-width="0.8"
|
||||
/>
|
||||
<circle cx="158" cy="55" r="1.5" fill="#D4AF37" />
|
||||
<circle cx="242" cy="55" r="1.5" fill="#D4AF37" />
|
||||
|
||||
<!-- Merkezi Gastronomi & Yıldız Amblemi -->
|
||||
<g transform="translate(200, 84)">
|
||||
<circle cx="0" cy="0" r="11" fill="none" stroke="#D4AF37" stroke-width="0.8" stroke-dasharray="2 2" />
|
||||
<polygon points="0,-7 2,-2 7,0 2,2 0,7 -2,2 -7,0 -2,-2" fill="#D4AF37" />
|
||||
</g>
|
||||
|
||||
<!-- Zarif Tipografik Ayırıcı -->
|
||||
<line x1="75" y1="172" x2="175" y2="172" stroke="#D4AF37" stroke-width="0.75" stroke-opacity="0.5" />
|
||||
<polygon points="200,169.5 202.5,172 200,174.5 197.5,172" fill="#D4AF37" />
|
||||
<line x1="225" y1="172" x2="325" y2="172" stroke="#D4AF37" stroke-width="0.75" stroke-opacity="0.5" />
|
||||
|
||||
<!-- QR Alanı Hedef Odak Çerçeveleri -->
|
||||
<path d="M 80 205 L 80 195 L 90 195" fill="none" stroke="#D4AF37" stroke-width="2" stroke-linecap="round" />
|
||||
<path d="M 320 205 L 320 195 L 310 195" fill="none" stroke="#D4AF37" stroke-width="2" stroke-linecap="round" />
|
||||
<path d="M 80 445 L 80 455 L 90 455" fill="none" stroke="#D4AF37" stroke-width="2" stroke-linecap="round" />
|
||||
<path d="M 320 445 L 320 455 L 310 455" fill="none" stroke="#D4AF37" stroke-width="2" stroke-linecap="round" />
|
||||
|
||||
<!-- Alt Bilgi Kartı Paneli -->
|
||||
<rect
|
||||
x="36"
|
||||
y="520"
|
||||
width="328"
|
||||
height="84"
|
||||
rx="14"
|
||||
fill="#171F2C"
|
||||
stroke="#D4AF37"
|
||||
stroke-width="0.8"
|
||||
stroke-opacity="0.4"
|
||||
/>
|
||||
<line x1="200" y1="530" x2="200" y2="594" stroke="#D4AF37" stroke-width="0.5" stroke-opacity="0.25" />
|
||||
|
||||
<!-- Minimal WiFi Vektörü -->
|
||||
<g transform="translate(62, 545)">
|
||||
<path d="M 0 4 A 12 12 0 0 1 18 4" fill="none" stroke="#D4AF37" stroke-width="1.4" stroke-linecap="round" />
|
||||
<path d="M 3.5 8 A 7 7 0 0 1 14.5 8" fill="none" stroke="#D4AF37" stroke-width="1.4" stroke-linecap="round" />
|
||||
<circle cx="9" cy="12" r="1.2" fill="#D4AF37" />
|
||||
</g>
|
||||
|
||||
<!-- Minimal Instagram / Web Vektörü -->
|
||||
<g transform="translate(224, 545)">
|
||||
<rect x="0" y="0" width="15" height="15" rx="4" fill="none" stroke="#D4AF37" stroke-width="1.4" />
|
||||
<circle cx="7.5" cy="7.5" r="3.2" fill="none" stroke="#D4AF37" stroke-width="1.1" />
|
||||
<circle cx="11.5" cy="3.5" r="0.75" fill="#D4AF37" />
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<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, Arial, sans-serif"
|
||||
font-size="12"
|
||||
font-weight="600"
|
||||
fill="#71717A"
|
||||
text-anchor="middle"
|
||||
>QR KOD ALANI</text>
|
||||
</g>
|
||||
|
||||
<g id="layer-texts">
|
||||
<!-- Masa Numarası -->
|
||||
<text
|
||||
x="200"
|
||||
y="59"
|
||||
font-family="Inter, Arial, sans-serif"
|
||||
font-size="9"
|
||||
font-weight="700"
|
||||
letter-spacing="2.5"
|
||||
fill="#E5C378"
|
||||
text-anchor="middle"
|
||||
>TABLE • 01</text>
|
||||
|
||||
<!-- Restoran Logosu & İsmi -->
|
||||
<text
|
||||
x="200"
|
||||
y="126"
|
||||
font-family="'Playfair Display', 'Cinzel', Georgia, serif"
|
||||
font-size="28"
|
||||
font-weight="700"
|
||||
letter-spacing="1.5"
|
||||
fill="#F9F6F0"
|
||||
text-anchor="middle"
|
||||
>L’ATELIER BISTRO</text>
|
||||
|
||||
<!-- Fransızca / Uluslararası Alt Başlık -->
|
||||
<text
|
||||
x="200"
|
||||
y="152"
|
||||
font-family="Inter, Arial, sans-serif"
|
||||
font-size="8.5"
|
||||
font-weight="600"
|
||||
letter-spacing="4"
|
||||
fill="#D4AF37"
|
||||
text-anchor="middle"
|
||||
>FINE DINING & COCKTAILS</text>
|
||||
|
||||
<!-- Çağrı Metni / Talimat -->
|
||||
<text
|
||||
x="200"
|
||||
y="480"
|
||||
font-family="Inter, Arial, sans-serif"
|
||||
font-size="11.5"
|
||||
font-weight="700"
|
||||
letter-spacing="2"
|
||||
fill="#F9F6F0"
|
||||
text-anchor="middle"
|
||||
>DİJİTAL MENÜ İÇİN OKUTUN</text>
|
||||
|
||||
<text
|
||||
x="200"
|
||||
y="498"
|
||||
font-family="'Playfair Display', Georgia, serif"
|
||||
font-size="10.5"
|
||||
font-style="italic"
|
||||
font-weight="400"
|
||||
letter-spacing="0.5"
|
||||
fill="#94A3B8"
|
||||
text-anchor="middle"
|
||||
>Kameranızı QR koda yaklaştırınız</text>
|
||||
|
||||
<!-- Sol Sütun: Yüksek Hızlı WiFi Bilgileri -->
|
||||
<text
|
||||
x="126"
|
||||
y="550"
|
||||
font-family="Inter, Arial, sans-serif"
|
||||
font-size="9"
|
||||
font-weight="700"
|
||||
letter-spacing="1.2"
|
||||
fill="#E5C378"
|
||||
text-anchor="middle"
|
||||
>GUEST WIFI</text>
|
||||
|
||||
<text
|
||||
x="126"
|
||||
y="567"
|
||||
font-family="Inter, Arial, sans-serif"
|
||||
font-size="8.5"
|
||||
font-weight="500"
|
||||
fill="#CBD5E1"
|
||||
text-anchor="middle"
|
||||
>Ağ: Atelier-Guest</text>
|
||||
|
||||
<text
|
||||
x="126"
|
||||
y="582"
|
||||
font-family="Inter, Arial, sans-serif"
|
||||
font-size="8.5"
|
||||
font-weight="500"
|
||||
fill="#CBD5E1"
|
||||
text-anchor="middle"
|
||||
>Şifre: bistro2026</text>
|
||||
|
||||
<!-- Sağ Sütun: Sosyal Medya & Rezervasyon -->
|
||||
<text
|
||||
x="286"
|
||||
y="550"
|
||||
font-family="Inter, Arial, sans-serif"
|
||||
font-size="8.5"
|
||||
font-weight="700"
|
||||
letter-spacing="1.2"
|
||||
fill="#E5C378"
|
||||
text-anchor="middle"
|
||||
>SOSYAL MEDYA</text>
|
||||
|
||||
<text
|
||||
x="286"
|
||||
y="575"
|
||||
font-family="Inter, Arial, sans-serif"
|
||||
font-size="10.5"
|
||||
font-weight="600"
|
||||
letter-spacing="0.5"
|
||||
fill="#F9F6F0"
|
||||
text-anchor="middle"
|
||||
>@latelier.bistro</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 7.1 KiB |
@@ -0,0 +1,275 @@
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 400 650"
|
||||
width="400"
|
||||
height="650"
|
||||
>
|
||||
<g id="layer-background">
|
||||
<!-- Koyu Doku / Kontrast Arka Plan -->
|
||||
<rect
|
||||
id="bg-canvas"
|
||||
x="0"
|
||||
y="0"
|
||||
width="400"
|
||||
height="650"
|
||||
fill="#252C36"
|
||||
/>
|
||||
</g>
|
||||
|
||||
<g id="layer-card">
|
||||
<!-- Sıcak Parşömen / Vintage Krem Ana Kart -->
|
||||
<rect
|
||||
id="main-card"
|
||||
x="20"
|
||||
y="20"
|
||||
width="360"
|
||||
height="610"
|
||||
rx="20"
|
||||
fill="#F6EFE2"
|
||||
stroke="#3A2312"
|
||||
stroke-width="1.5"
|
||||
/>
|
||||
<!-- Çift İnce Vintage Çerçeve -->
|
||||
<rect
|
||||
x="28"
|
||||
y="28"
|
||||
width="344"
|
||||
height="594"
|
||||
rx="14"
|
||||
fill="none"
|
||||
stroke="#8C6239"
|
||||
stroke-width="1"
|
||||
stroke-opacity="0.4"
|
||||
/>
|
||||
<rect
|
||||
x="32"
|
||||
y="32"
|
||||
width="336"
|
||||
height="586"
|
||||
rx="10"
|
||||
fill="none"
|
||||
stroke="#8C6239"
|
||||
stroke-width="0.5"
|
||||
stroke-opacity="0.25"
|
||||
/>
|
||||
</g>
|
||||
|
||||
<g id="layer-icons">
|
||||
<!-- Sol Üst: Botanik Kahve / Çay Dalı -->
|
||||
<g transform="translate(36, 36) scale(0.75)" stroke="#3A2312" stroke-width="1.4" fill="none" stroke-linecap="round">
|
||||
<path d="M 0 0 C 15 15 25 35 30 65" />
|
||||
<path d="M 10 12 C 5 2 20 -2 22 8 C 22 18 10 12 10 12 Z" fill="#EADCC7" />
|
||||
<path d="M 18 27 C 12 20 28 14 32 23 C 32 32 18 27 18 27 Z" fill="#EADCC7" />
|
||||
<path d="M 24 45 C 32 38 45 48 38 56 C 30 62 24 45 24 45 Z" fill="#EADCC7" />
|
||||
</g>
|
||||
|
||||
<!-- Sağ Üst: Kuşbakışı Latte Art Fincanı -->
|
||||
<g transform="translate(340, 52)">
|
||||
<!-- Tabak -->
|
||||
<circle cx="0" cy="0" r="32" fill="#EADCC7" stroke="#3A2312" stroke-width="1.2" />
|
||||
<circle cx="0" cy="0" r="26" fill="#F6EFE2" stroke="#8C6239" stroke-width="0.8" stroke-dasharray="2 2" />
|
||||
<!-- Fincan Kenarı & Kahve -->
|
||||
<circle cx="0" cy="0" r="21" fill="#7A4B26" stroke="#3A2312" stroke-width="1.4" />
|
||||
<!-- Latte Art / Rosetta Yaprakları -->
|
||||
<path d="M 0 10 C -8 4 -4 -6 0 -12 C 4 -6 8 4 0 10 Z" fill="#F6EFE2" />
|
||||
<path d="M 0 4 C -5 0 -2 -5 0 -8 C 2 -5 5 0 0 4 Z" fill="#7A4B26" />
|
||||
<circle cx="0" cy="-14" r="1.8" fill="#F6EFE2" />
|
||||
</g>
|
||||
|
||||
<!-- Sol Orta: Küçük Kahve Fincanı & Tabağı -->
|
||||
<g transform="translate(48, 250)">
|
||||
<ellipse cx="0" cy="0" rx="26" ry="26" fill="#EADCC7" stroke="#3A2312" stroke-width="1" />
|
||||
<circle cx="0" cy="0" r="17" fill="#6A3B18" stroke="#3A2312" stroke-width="1.2" />
|
||||
<!-- Basit Kalp Latte Deseni -->
|
||||
<path d="M -5 -3 C -8 -7 0 -10 0 -4 C 0 -10 8 -7 5 -3 C 2 3 0 7 0 7 C 0 7 -2 3 -5 -3 Z" fill="#F6EFE2" />
|
||||
<!-- Fincan Kulpu -->
|
||||
<path d="M 17 -4 C 23 -4 23 4 17 4" fill="none" stroke="#3A2312" stroke-width="1.5" />
|
||||
</g>
|
||||
|
||||
<!-- Sağ Orta: Dikey Yaprak İllüstrasyonu -->
|
||||
<g transform="translate(345, 230) scale(0.7)" stroke="#3A2312" stroke-width="1.3" fill="none">
|
||||
<path d="M 10 0 C 0 30 -5 70 5 110" />
|
||||
<path d="M 6 25 C 18 15 28 28 14 36 C 6 36 6 25 6 25 Z" fill="#EADCC7" />
|
||||
<path d="M 2 60 C -12 48 -20 62 -8 70 C 2 70 2 60 2 60 Z" fill="#EADCC7" />
|
||||
<path d="M 5 88 C 18 78 24 92 12 100 C 5 100 5 88 5 88 Z" fill="#EADCC7" />
|
||||
</g>
|
||||
|
||||
<!-- Üst Başlık Yan Süsleme Çizgileri -->
|
||||
<path d="M 142 66 C 148 64 154 68 160 66" fill="none" stroke="#8C6239" stroke-width="1.2" stroke-linecap="round" />
|
||||
<path d="M 240 66 C 246 68 252 64 258 66" fill="none" stroke="#8C6239" stroke-width="1.2" stroke-linecap="round" />
|
||||
|
||||
<!-- Sol Alt: Çekirdek ve Yaprak Grubu -->
|
||||
<g transform="translate(42, 530) scale(0.65)" stroke="#3A2312" stroke-width="1.4" fill="none">
|
||||
<path d="M 0 110 C 20 70 15 30 0 0" />
|
||||
<path d="M 8 30 C 24 15 35 30 18 42 Z" fill="#EADCC7" />
|
||||
<path d="M 12 70 C 30 55 40 72 20 84 Z" fill="#EADCC7" />
|
||||
</g>
|
||||
|
||||
<!-- Kahve Çekirdekleri Motifleri -->
|
||||
<!-- Sol Alt Çekirdek 1 -->
|
||||
<g transform="translate(108, 595) rotate(-20) scale(0.8)">
|
||||
<ellipse cx="0" cy="0" rx="9" ry="6.5" fill="#CBB296" stroke="#3A2312" stroke-width="1.2" />
|
||||
<path d="M -7 0 Q 0 3 0 0 Q 0 -3 7 0" fill="none" stroke="#3A2312" stroke-width="1.2" />
|
||||
</g>
|
||||
<!-- Sol Alt Çekirdek 2 -->
|
||||
<g transform="translate(128, 584) rotate(35) scale(0.8)">
|
||||
<ellipse cx="0" cy="0" rx="9" ry="6.5" fill="#CBB296" stroke="#3A2312" stroke-width="1.2" />
|
||||
<path d="M -7 0 Q 0 3 0 0 Q 0 -3 7 0" fill="none" stroke="#3A2312" stroke-width="1.2" />
|
||||
</g>
|
||||
<!-- Sağ Orta Çekirdek -->
|
||||
<g transform="translate(336, 360) rotate(-45) scale(0.7)">
|
||||
<ellipse cx="0" cy="0" rx="9" ry="6.5" fill="#CBB296" stroke="#3A2312" stroke-width="1.2" />
|
||||
<path d="M -7 0 Q 0 3 0 0 Q 0 -3 7 0" fill="none" stroke="#3A2312" stroke-width="1.2" />
|
||||
</g>
|
||||
|
||||
<!-- Sağ Alt: Sıcak Kahve Fincanı İllüstrasyonu -->
|
||||
<g transform="translate(325, 590)">
|
||||
<!-- Tabak -->
|
||||
<ellipse cx="0" cy="18" rx="28" ry="7" fill="#EADCC7" stroke="#3A2312" stroke-width="1.2" />
|
||||
<!-- Fincan Gövdesi -->
|
||||
<path d="M -18 2 C -18 16 18 16 18 2 Z" fill="#F6EFE2" stroke="#3A2312" stroke-width="1.4" />
|
||||
<ellipse cx="0" cy="2" rx="18" ry="4" fill="#6A3B18" stroke="#3A2312" stroke-width="1.2" />
|
||||
<!-- Kulp -->
|
||||
<path d="M 16 4 C 24 4 24 12 16 14" fill="none" stroke="#3A2312" stroke-width="1.3" />
|
||||
<!-- Buhar Çizgileri -->
|
||||
<path d="M -6 -4 C -8 -9 -4 -12 -6 -17" fill="none" stroke="#8C6239" stroke-width="1" stroke-linecap="round" />
|
||||
<path d="M 2 -5 C 0 -10 4 -13 2 -19" fill="none" stroke="#8C6239" stroke-width="1" stroke-linecap="round" />
|
||||
</g>
|
||||
|
||||
<!-- QR Alanı İnce Köşe Süsleri -->
|
||||
<path d="M 80 205 C 80 195 80 195 90 195" fill="none" stroke="#3A2312" stroke-width="2" stroke-linecap="round" />
|
||||
<path d="M 320 205 C 320 195 320 195 310 195" fill="none" stroke="#3A2312" stroke-width="2" stroke-linecap="round" />
|
||||
<path d="M 80 445 C 80 455 80 455 90 455" fill="none" stroke="#3A2312" stroke-width="2" stroke-linecap="round" />
|
||||
<path d="M 320 445 C 320 455 320 455 310 455" fill="none" stroke="#3A2312" stroke-width="2" stroke-linecap="round" />
|
||||
|
||||
<!-- Alt Est. Bölümü Çizgileri -->
|
||||
<line x1="145" y1="608" x2="175" y2="608" stroke="#8C6239" stroke-width="0.8" />
|
||||
<line x1="225" y1="608" x2="255" y2="608" stroke="#8C6239" stroke-width="0.8" />
|
||||
</g>
|
||||
|
||||
<g id="qr-placeholder" transform="translate(90, 215)">
|
||||
<!-- QR Kod Arka Planı (Hafif Sıcak Ton) -->
|
||||
<rect
|
||||
width="220"
|
||||
height="220"
|
||||
rx="20"
|
||||
fill="#FBF8F2"
|
||||
stroke="#D8C7B0"
|
||||
stroke-width="1.5"
|
||||
/>
|
||||
<rect
|
||||
x="10"
|
||||
y="10"
|
||||
width="200"
|
||||
height="200"
|
||||
rx="14"
|
||||
fill="#F5EFE6"
|
||||
stroke="#D8C7B0"
|
||||
stroke-dasharray="4 3"
|
||||
/>
|
||||
<!-- QR Merkezine Kahve Çekirdeği İkonu Rozeti -->
|
||||
<circle cx="110" cy="98" r="18" fill="#FBF8F2" stroke="#8C6239" stroke-width="1" />
|
||||
<g transform="translate(110, 98) scale(0.65)">
|
||||
<ellipse cx="-5" cy="2" rx="7" ry="5" fill="#3A2312" transform="rotate(-30)" />
|
||||
<ellipse cx="5" cy="0" rx="7" ry="5" fill="#3A2312" transform="rotate(40)" />
|
||||
</g>
|
||||
<text
|
||||
x="110"
|
||||
y="132"
|
||||
font-family="Inter, Arial, sans-serif"
|
||||
font-size="11"
|
||||
font-weight="700"
|
||||
letter-spacing="1"
|
||||
fill="#8C6239"
|
||||
text-anchor="middle"
|
||||
>QR KOD ALANI</text>
|
||||
</g>
|
||||
|
||||
<g id="layer-texts">
|
||||
<!-- Üst Başlık Takısı -->
|
||||
<text
|
||||
x="200"
|
||||
y="70"
|
||||
font-family="'Playfair Display', Georgia, serif"
|
||||
font-size="14"
|
||||
font-style="italic"
|
||||
font-weight="700"
|
||||
letter-spacing="4"
|
||||
fill="#3A2312"
|
||||
text-anchor="middle"
|
||||
>THE</text>
|
||||
|
||||
<!-- Ana Tipografi: El Yazısı / Retro Tabela Havası -->
|
||||
<text
|
||||
x="200"
|
||||
y="114"
|
||||
font-family="'Playfair Display', 'Brush Script MT', 'Great Vibes', Georgia, cursive, serif"
|
||||
font-size="34"
|
||||
font-weight="800"
|
||||
letter-spacing="0.5"
|
||||
fill="#2C1A0E"
|
||||
text-anchor="middle"
|
||||
>The Daily Grind</text>
|
||||
|
||||
<!-- Alt Kategori Başlığı -->
|
||||
<text
|
||||
x="200"
|
||||
y="146"
|
||||
font-family="Inter, Arial, sans-serif"
|
||||
font-size="13"
|
||||
font-weight="800"
|
||||
letter-spacing="3"
|
||||
fill="#3A2312"
|
||||
text-anchor="middle"
|
||||
>COFFEE & KITCHEN</text>
|
||||
|
||||
<!-- Büyük ve Belirgin Çağrı Metni -->
|
||||
<text
|
||||
x="200"
|
||||
y="488"
|
||||
font-family="Inter, Arial, sans-serif"
|
||||
font-size="19"
|
||||
font-weight="900"
|
||||
letter-spacing="1.5"
|
||||
fill="#2C1A0E"
|
||||
text-anchor="middle"
|
||||
>SCAN TO VIEW MENU</text>
|
||||
|
||||
<!-- Restoran & WiFi Bilgi Bandı -->
|
||||
<text
|
||||
x="200"
|
||||
y="520"
|
||||
font-family="Inter, Arial, sans-serif"
|
||||
font-size="10.5"
|
||||
font-weight="700"
|
||||
letter-spacing="0.5"
|
||||
fill="#3A2312"
|
||||
text-anchor="middle"
|
||||
>DAILY GRIND CAFE | WIFI: DAILY_GRIND_GUEST</text>
|
||||
|
||||
<!-- Şifre ve Masa Detayı -->
|
||||
<text
|
||||
x="200"
|
||||
y="542"
|
||||
font-family="Inter, Arial, sans-serif"
|
||||
font-size="9"
|
||||
font-weight="600"
|
||||
letter-spacing="1"
|
||||
fill="#8C6239"
|
||||
text-anchor="middle"
|
||||
>ŞİFRE: coffee2026 • MASA NO: 04</text>
|
||||
|
||||
<!-- Kuruluş / Est. Yılı -->
|
||||
<text
|
||||
x="200"
|
||||
y="611"
|
||||
font-family="'Playfair Display', Georgia, serif"
|
||||
font-size="11"
|
||||
font-style="italic"
|
||||
font-weight="700"
|
||||
letter-spacing="2"
|
||||
fill="#3A2312"
|
||||
text-anchor="middle"
|
||||
>EST. 2018</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 9.3 KiB |
|
After Width: | Height: | Size: 278 KiB |
|
After Width: | Height: | Size: 666 KiB |
|
After Width: | Height: | Size: 516 KiB |
@@ -15,6 +15,13 @@
|
||||
"distribution": "internal",
|
||||
"ios": {
|
||||
"image": "latest"
|
||||
},
|
||||
"env": {
|
||||
"EXPO_PUBLIC_SUPABASE_URL": "https://fwahdopsyvryvjaqande.supabase.co",
|
||||
"EXPO_PUBLIC_SUPABASE_ANON_KEY": "sb_publishable_9YsK7YU7xfzxJQsz3x5iMA_LHCqUN3b",
|
||||
"EXPO_PUBLIC_API_URL": "https://api.menul.io",
|
||||
"EXPO_PUBLIC_WEB_URL": "https://menul.io",
|
||||
"EXPO_PUBLIC_REVENUECAT_API_KEY": "appl_uQIvGUuvEthCNheZtcRfVixVhFy"
|
||||
}
|
||||
},
|
||||
"production": {
|
||||
|
||||
@@ -25,9 +25,9 @@
|
||||
"expo-haptics": "^14.0.1",
|
||||
"expo-image-picker": "~16.0.6",
|
||||
"expo-linking": "~7.0.5",
|
||||
"expo-print": "^57.0.1",
|
||||
"expo-print": "~14.0.3",
|
||||
"expo-router": "~4.0.0",
|
||||
"expo-sharing": "^57.0.13",
|
||||
"expo-sharing": "~13.0.1",
|
||||
"expo-splash-screen": "~0.29.24",
|
||||
"expo-status-bar": "~2.0.0",
|
||||
"react": "18.3.1",
|
||||
@@ -35,8 +35,9 @@
|
||||
"react-native-purchases": "^8.2.0",
|
||||
"react-native-safe-area-context": "4.12.0",
|
||||
"react-native-screens": "~4.4.0",
|
||||
"react-native-svg": "15.8.0",
|
||||
"react-native-url-polyfill": "^2.0.0",
|
||||
"react-native-view-shot": "^5.1.1"
|
||||
"react-native-view-shot": "~4.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.25.2",
|
||||
|
||||
@@ -4,7 +4,7 @@ import { StatusBar } from "expo-status-bar";
|
||||
export default function RootLayout() {
|
||||
return (
|
||||
<>
|
||||
<StatusBar style="dark" />
|
||||
<StatusBar style="light" />
|
||||
<Stack screenOptions={{ headerShown: false }} />
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,242 @@
|
||||
import React, { useRef } from "react";
|
||||
import {
|
||||
PanResponder,
|
||||
Pressable,
|
||||
Text,
|
||||
View,
|
||||
} from "react-native";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import * as Haptics from "expo-haptics";
|
||||
import { type CanvasTextLayer, getCanvasFontFamily } from "./types";
|
||||
|
||||
interface CanvasLayerItemProps {
|
||||
layer: CanvasTextLayer;
|
||||
isSelected: boolean;
|
||||
onSelect: (id: string) => void;
|
||||
onEdit: (layer: CanvasTextLayer) => void;
|
||||
onUpdatePosition: (id: string, x: number, y: number) => void;
|
||||
onDelete: (id: string) => void;
|
||||
onDragStart?: () => void;
|
||||
onDragMove?: (rawX: number, rawY: number, itemWidth: number, itemHeight: number) => { snappedX: number; snappedY: number };
|
||||
onDragEnd?: (finalX: number, finalY: number, itemWidth: number, itemHeight: number) => void;
|
||||
canvasWidth: number;
|
||||
canvasHeight: number;
|
||||
}
|
||||
|
||||
export const CanvasLayerItem: React.FC<CanvasLayerItemProps> = ({
|
||||
layer,
|
||||
isSelected,
|
||||
onSelect,
|
||||
onEdit,
|
||||
onUpdatePosition,
|
||||
onDelete,
|
||||
onDragStart,
|
||||
onDragMove,
|
||||
onDragEnd,
|
||||
canvasWidth,
|
||||
canvasHeight,
|
||||
}) => {
|
||||
const layerRef = useRef(layer);
|
||||
layerRef.current = layer;
|
||||
|
||||
const canvasWidthRef = useRef(canvasWidth);
|
||||
canvasWidthRef.current = canvasWidth;
|
||||
|
||||
const canvasHeightRef = useRef(canvasHeight);
|
||||
canvasHeightRef.current = canvasHeight;
|
||||
|
||||
const panStartRef = useRef({ x: layer.x, y: layer.y });
|
||||
const layoutSizeRef = useRef({ width: 70, height: 26 });
|
||||
const currentPosRef = useRef({ x: layer.x, y: layer.y });
|
||||
const lastTapRef = useRef<number>(0);
|
||||
const isDraggingRef = useRef(false);
|
||||
|
||||
const panResponder = useRef(
|
||||
PanResponder.create({
|
||||
onStartShouldSetPanResponder: () => true,
|
||||
onStartShouldSetPanResponderCapture: () => true,
|
||||
onMoveShouldSetPanResponder: (_evt, gestureState) =>
|
||||
Math.abs(gestureState.dx) > 1 || Math.abs(gestureState.dy) > 1,
|
||||
onMoveShouldSetPanResponderCapture: (_evt, gestureState) =>
|
||||
Math.abs(gestureState.dx) > 1 || Math.abs(gestureState.dy) > 1,
|
||||
onPanResponderTerminationRequest: () => false,
|
||||
|
||||
onPanResponderGrant: () => {
|
||||
const now = Date.now();
|
||||
const DOUBLE_TAP_DELAY = 300;
|
||||
const curLayer = layerRef.current;
|
||||
if (now - lastTapRef.current < DOUBLE_TAP_DELAY) {
|
||||
try {
|
||||
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Medium);
|
||||
} catch {}
|
||||
onEdit(curLayer);
|
||||
} else {
|
||||
onSelect(curLayer.id);
|
||||
}
|
||||
lastTapRef.current = now;
|
||||
|
||||
panStartRef.current = { x: curLayer.x, y: curLayer.y };
|
||||
currentPosRef.current = { x: curLayer.x, y: curLayer.y };
|
||||
isDraggingRef.current = false;
|
||||
onDragStart?.();
|
||||
},
|
||||
|
||||
onPanResponderMove: (_evt, gestureState) => {
|
||||
if (!isDraggingRef.current && (Math.abs(gestureState.dx) > 2 || Math.abs(gestureState.dy) > 2)) {
|
||||
isDraggingRef.current = true;
|
||||
}
|
||||
|
||||
const curLayer = layerRef.current;
|
||||
const itemW = layoutSizeRef.current.width;
|
||||
const itemH = layoutSizeRef.current.height;
|
||||
const cWidth = canvasWidthRef.current;
|
||||
const cHeight = canvasHeightRef.current;
|
||||
|
||||
const maxX = Math.max(0, cWidth - itemW);
|
||||
const maxY = Math.max(0, cHeight - itemH);
|
||||
const rawX = Math.max(0, Math.min(maxX, panStartRef.current.x + gestureState.dx));
|
||||
const rawY = Math.max(0, Math.min(maxY, panStartRef.current.y + gestureState.dy));
|
||||
|
||||
if (onDragMove) {
|
||||
const { snappedX, snappedY } = onDragMove(rawX, rawY, itemW, itemH);
|
||||
currentPosRef.current = { x: snappedX, y: snappedY };
|
||||
onUpdatePosition(curLayer.id, snappedX, snappedY);
|
||||
} else {
|
||||
const sx = Math.round(rawX);
|
||||
const sy = Math.round(rawY);
|
||||
currentPosRef.current = { x: sx, y: sy };
|
||||
onUpdatePosition(curLayer.id, sx, sy);
|
||||
}
|
||||
},
|
||||
|
||||
onPanResponderRelease: () => {
|
||||
const itemW = layoutSizeRef.current.width;
|
||||
const itemH = layoutSizeRef.current.height;
|
||||
onDragEnd?.(currentPosRef.current.x, currentPosRef.current.y, itemW, itemH);
|
||||
},
|
||||
|
||||
onPanResponderTerminate: () => {
|
||||
const itemW = layoutSizeRef.current.width;
|
||||
const itemH = layoutSizeRef.current.height;
|
||||
onDragEnd?.(currentPosRef.current.x, currentPosRef.current.y, itemW, itemH);
|
||||
},
|
||||
})
|
||||
).current;
|
||||
|
||||
// Instagram Badge arka plan stili
|
||||
const renderBackgroundStyle = () => {
|
||||
switch (layer.bgStyle) {
|
||||
case "solid":
|
||||
return {
|
||||
backgroundColor: layer.bgBoxColor || "rgba(18, 18, 21, 0.92)",
|
||||
paddingHorizontal: 12,
|
||||
paddingVertical: 5,
|
||||
borderRadius: 8,
|
||||
};
|
||||
case "soft":
|
||||
return {
|
||||
backgroundColor: "rgba(255, 255, 255, 0.18)",
|
||||
paddingHorizontal: 12,
|
||||
paddingVertical: 5,
|
||||
borderRadius: 8,
|
||||
borderWidth: 1,
|
||||
borderColor: "rgba(255, 255, 255, 0.25)",
|
||||
};
|
||||
case "outline":
|
||||
return {
|
||||
backgroundColor: "transparent",
|
||||
paddingHorizontal: 10,
|
||||
paddingVertical: 4,
|
||||
borderRadius: 8,
|
||||
borderWidth: 1.5,
|
||||
borderColor: layer.color || "#D4AF37",
|
||||
};
|
||||
case "none":
|
||||
default:
|
||||
return {
|
||||
backgroundColor: "transparent",
|
||||
paddingHorizontal: 4,
|
||||
paddingVertical: 2,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<View
|
||||
{...panResponder.panHandlers}
|
||||
accessible={true}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={`Metin: ${layer.text}`}
|
||||
accessibilityHint="Metni düzenlemek için çift dokunun, taşımak için sürükleyin"
|
||||
accessibilityState={{ selected: isSelected }}
|
||||
onLayout={(e) => {
|
||||
const { width, height } = e.nativeEvent.layout;
|
||||
layoutSizeRef.current = { width, height };
|
||||
}}
|
||||
style={{
|
||||
position: "absolute",
|
||||
left: layer.x,
|
||||
top: layer.y,
|
||||
transform: [{ rotate: `${layer.rotation || 0}deg` }],
|
||||
zIndex: isSelected ? 90 : 20,
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
borderWidth: isSelected ? 1.5 : 0,
|
||||
borderColor: isSelected ? "#D4AF37" : "transparent",
|
||||
borderStyle: isSelected ? "dashed" : "solid",
|
||||
borderRadius: 10,
|
||||
padding: 2,
|
||||
}}
|
||||
>
|
||||
<View style={renderBackgroundStyle()}>
|
||||
<Text
|
||||
style={{
|
||||
fontSize: layer.fontSize,
|
||||
color: layer.color,
|
||||
fontFamily: getCanvasFontFamily(layer.fontId),
|
||||
fontWeight: layer.fontWeight || "bold",
|
||||
textAlign: layer.textAlign || "center",
|
||||
letterSpacing: layer.letterSpacing ?? 0.5,
|
||||
textShadowColor: layer.hasShadow ? "rgba(0,0,0,0.8)" : "transparent",
|
||||
textShadowOffset: { width: 0, height: 1 },
|
||||
textShadowRadius: 3,
|
||||
}}
|
||||
>
|
||||
{layer.text}
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
{/* Seçili İken Üst Köşedeki Küçük Silme Rozeti */}
|
||||
{isSelected && (
|
||||
<Pressable
|
||||
onPress={(e) => {
|
||||
e.stopPropagation?.();
|
||||
try {
|
||||
Haptics.notificationAsync(Haptics.NotificationFeedbackType.Warning);
|
||||
} catch {}
|
||||
onDelete(layer.id);
|
||||
}}
|
||||
hitSlop={{ top: 8, bottom: 8, left: 8, right: 8 }}
|
||||
style={{
|
||||
position: "absolute",
|
||||
top: -8,
|
||||
right: -8,
|
||||
backgroundColor: "#EF4444",
|
||||
width: 18,
|
||||
height: 18,
|
||||
borderRadius: 9,
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
borderWidth: 1.5,
|
||||
borderColor: "#0C0C0E",
|
||||
}}
|
||||
>
|
||||
<Ionicons name="close" size={11} color="#FFFFFF" />
|
||||
</Pressable>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,176 @@
|
||||
import React, { useRef } from "react";
|
||||
import {
|
||||
Image,
|
||||
PanResponder,
|
||||
View,
|
||||
} from "react-native";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { type CanvasQrConfig } from "./types";
|
||||
|
||||
interface CanvasQrItemProps {
|
||||
qrConfig: CanvasQrConfig;
|
||||
qrBase64?: string;
|
||||
isSelected: boolean;
|
||||
onSelect: () => void;
|
||||
onUpdatePosition: (x: number, y: number) => void;
|
||||
onDragStart?: () => void;
|
||||
onDragMove?: (rawX: number, rawY: number, itemWidth: number, itemHeight: number) => { snappedX: number; snappedY: number };
|
||||
onDragEnd?: (finalX: number, finalY: number, itemWidth: number, itemHeight: number) => void;
|
||||
canvasWidth: number;
|
||||
canvasHeight: number;
|
||||
}
|
||||
|
||||
export const CanvasQrItem: React.FC<CanvasQrItemProps> = ({
|
||||
qrConfig,
|
||||
qrBase64,
|
||||
isSelected,
|
||||
onSelect,
|
||||
onUpdatePosition,
|
||||
onDragStart,
|
||||
onDragMove,
|
||||
onDragEnd,
|
||||
canvasWidth,
|
||||
canvasHeight,
|
||||
}) => {
|
||||
// Stale closure önlemi için ref'ler
|
||||
const qrConfigRef = useRef(qrConfig);
|
||||
qrConfigRef.current = qrConfig;
|
||||
|
||||
const canvasWidthRef = useRef(canvasWidth);
|
||||
canvasWidthRef.current = canvasWidth;
|
||||
|
||||
const canvasHeightRef = useRef(canvasHeight);
|
||||
canvasHeightRef.current = canvasHeight;
|
||||
|
||||
const panStartRef = useRef({ x: qrConfig.x, y: qrConfig.y });
|
||||
const currentPosRef = useRef({ x: qrConfig.x, y: qrConfig.y });
|
||||
|
||||
const panResponder = useRef(
|
||||
PanResponder.create({
|
||||
onStartShouldSetPanResponder: () => true,
|
||||
onStartShouldSetPanResponderCapture: () => true,
|
||||
onMoveShouldSetPanResponder: (_evt, gestureState) =>
|
||||
Math.abs(gestureState.dx) > 1 || Math.abs(gestureState.dy) > 1,
|
||||
onMoveShouldSetPanResponderCapture: (_evt, gestureState) =>
|
||||
Math.abs(gestureState.dx) > 1 || Math.abs(gestureState.dy) > 1,
|
||||
onPanResponderTerminationRequest: () => false,
|
||||
|
||||
onPanResponderGrant: () => {
|
||||
onSelect();
|
||||
const cur = qrConfigRef.current;
|
||||
panStartRef.current = { x: cur.x, y: cur.y };
|
||||
currentPosRef.current = { x: cur.x, y: cur.y };
|
||||
onDragStart?.();
|
||||
},
|
||||
|
||||
onPanResponderMove: (_evt, gestureState) => {
|
||||
const cur = qrConfigRef.current;
|
||||
const cWidth = canvasWidthRef.current;
|
||||
const cHeight = canvasHeightRef.current;
|
||||
|
||||
const maxX = Math.max(0, cWidth - cur.size);
|
||||
const maxY = Math.max(0, cHeight - cur.size);
|
||||
|
||||
const rawX = Math.max(0, Math.min(maxX, panStartRef.current.x + gestureState.dx));
|
||||
const rawY = Math.max(0, Math.min(maxY, panStartRef.current.y + gestureState.dy));
|
||||
|
||||
if (onDragMove) {
|
||||
const { snappedX, snappedY } = onDragMove(rawX, rawY, cur.size, cur.size);
|
||||
currentPosRef.current = { x: snappedX, y: snappedY };
|
||||
onUpdatePosition(snappedX, snappedY);
|
||||
} else {
|
||||
const sx = Math.round(rawX);
|
||||
const sy = Math.round(rawY);
|
||||
currentPosRef.current = { x: sx, y: sy };
|
||||
onUpdatePosition(sx, sy);
|
||||
}
|
||||
},
|
||||
|
||||
onPanResponderRelease: () => {
|
||||
const cur = qrConfigRef.current;
|
||||
onDragEnd?.(currentPosRef.current.x, currentPosRef.current.y, cur.size, cur.size);
|
||||
},
|
||||
|
||||
onPanResponderTerminate: () => {
|
||||
const cur = qrConfigRef.current;
|
||||
onDragEnd?.(currentPosRef.current.x, currentPosRef.current.y, cur.size, cur.size);
|
||||
},
|
||||
})
|
||||
).current;
|
||||
|
||||
return (
|
||||
<View
|
||||
{...panResponder.panHandlers}
|
||||
accessible={true}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel="QR Kod Alanı"
|
||||
accessibilityHint="QR kodunu seçmek, boyutunu ayarlamak veya stant üzerinde taşımak için dokunun"
|
||||
accessibilityState={{ selected: isSelected }}
|
||||
style={{
|
||||
position: "absolute",
|
||||
left: qrConfig.x,
|
||||
top: qrConfig.y,
|
||||
width: qrConfig.size,
|
||||
height: qrConfig.size,
|
||||
zIndex: isSelected ? 80 : 15,
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
backgroundColor: qrConfig.bgBoxColor || "#FFFFFF",
|
||||
borderRadius: qrConfig.borderRadius,
|
||||
borderWidth: isSelected ? 2 : qrConfig.borderWidth,
|
||||
borderColor: isSelected ? "#D4AF37" : qrConfig.borderColor,
|
||||
borderStyle: isSelected ? "dashed" : "solid",
|
||||
padding: qrConfig.padding,
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
shadowColor: "#000",
|
||||
shadowOffset: { width: 0, height: 4 },
|
||||
shadowOpacity: 0.25,
|
||||
shadowRadius: 6,
|
||||
elevation: 4,
|
||||
}}
|
||||
>
|
||||
{qrBase64 ? (
|
||||
<Image
|
||||
source={{ uri: `data:image/png;base64,${qrBase64}` }}
|
||||
style={{
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
borderRadius: Math.max(0, qrConfig.borderRadius - 4),
|
||||
}}
|
||||
resizeMode="contain"
|
||||
/>
|
||||
) : (
|
||||
<View style={{ alignItems: "center", justifyContent: "center", width: "100%", height: "100%" }}>
|
||||
<Ionicons name="qr-code" size={Math.round(qrConfig.size * 0.7)} color="#0C0C0E" />
|
||||
</View>
|
||||
)}
|
||||
|
||||
{/* Seçili İken Taşıma Rozeti */}
|
||||
{isSelected && (
|
||||
<View
|
||||
style={{
|
||||
position: "absolute",
|
||||
top: -8,
|
||||
right: -8,
|
||||
backgroundColor: "#D4AF37",
|
||||
width: 22,
|
||||
height: 22,
|
||||
borderRadius: 11,
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
borderWidth: 1.5,
|
||||
borderColor: "#0C0C0E",
|
||||
}}
|
||||
>
|
||||
<Ionicons name="move" size={12} color="#0C0C0E" />
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,610 @@
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import {
|
||||
KeyboardAvoidingView,
|
||||
Modal,
|
||||
Platform,
|
||||
Pressable,
|
||||
ScrollView,
|
||||
StyleSheet,
|
||||
Text,
|
||||
TextInput,
|
||||
View,
|
||||
} from "react-native";
|
||||
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import * as Haptics from "expo-haptics";
|
||||
import {
|
||||
type CanvasTextLayer,
|
||||
CANVAS_FONT_OPTIONS,
|
||||
CANVAS_COLOR_PALETTE,
|
||||
getCanvasFontFamily,
|
||||
} from "./types";
|
||||
|
||||
interface InstagramTextEditorModalProps {
|
||||
visible: boolean;
|
||||
layer: CanvasTextLayer | null;
|
||||
onSave: (updated: Partial<CanvasTextLayer>) => void;
|
||||
onClose: () => void;
|
||||
onDelete?: (id: string) => void;
|
||||
}
|
||||
|
||||
export const InstagramTextEditorModal: React.FC<InstagramTextEditorModalProps> = ({
|
||||
visible,
|
||||
layer,
|
||||
onSave,
|
||||
onClose,
|
||||
onDelete,
|
||||
}) => {
|
||||
const insets = useSafeAreaInsets();
|
||||
const inputRef = useRef<TextInput>(null);
|
||||
|
||||
const [text, setText] = useState("");
|
||||
const [fontId, setFontId] = useState("sans");
|
||||
const [color, setColor] = useState("#FFFFFF");
|
||||
const [fontSize, setFontSize] = useState(16);
|
||||
const [textAlign, setTextAlign] = useState<"left" | "center" | "right">("center");
|
||||
const [bgStyle, setBgStyle] = useState<"none" | "solid" | "soft" | "outline">("none");
|
||||
const [bgBoxColor, setBgBoxColor] = useState<string | undefined>(undefined);
|
||||
|
||||
// Modal açıldığında layer verilerini yükle
|
||||
useEffect(() => {
|
||||
if (layer && visible) {
|
||||
setText(layer.text || "");
|
||||
setFontId(layer.fontId || "sans");
|
||||
setColor(layer.color || "#FFFFFF");
|
||||
setFontSize(layer.fontSize || 16);
|
||||
setTextAlign(layer.textAlign || "center");
|
||||
if (layer.bgBoxColor) {
|
||||
if (layer.bgBoxColor.includes("0.2") || layer.bgBoxColor.includes("0.3")) {
|
||||
setBgStyle("soft");
|
||||
} else {
|
||||
setBgStyle("solid");
|
||||
}
|
||||
setBgBoxColor(layer.bgBoxColor);
|
||||
} else {
|
||||
setBgStyle("none");
|
||||
setBgBoxColor(undefined);
|
||||
}
|
||||
|
||||
// Auto focus text input
|
||||
setTimeout(() => {
|
||||
inputRef.current?.focus();
|
||||
}, 150);
|
||||
}
|
||||
}, [layer, visible]);
|
||||
|
||||
// Arka plan pill stilini sırayla değiştir (Instagram "A" butonu mantığı)
|
||||
const handleToggleBgStyle = () => {
|
||||
try {
|
||||
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
|
||||
} catch {}
|
||||
|
||||
if (bgStyle === "none") {
|
||||
setBgStyle("solid");
|
||||
// Solid: seçili rengi kutu yap, metin rengini zıt yap
|
||||
if (color === "#FFFFFF" || color === "#F6EFE3") {
|
||||
setBgBoxColor("rgba(18, 18, 21, 0.92)");
|
||||
} else {
|
||||
setBgBoxColor("rgba(212, 175, 55, 0.92)");
|
||||
}
|
||||
} else if (bgStyle === "solid") {
|
||||
setBgStyle("soft");
|
||||
setBgBoxColor("rgba(255, 255, 255, 0.22)");
|
||||
} else if (bgStyle === "soft") {
|
||||
setBgStyle("outline");
|
||||
setBgBoxColor(undefined);
|
||||
} else {
|
||||
setBgStyle("none");
|
||||
setBgBoxColor(undefined);
|
||||
}
|
||||
};
|
||||
|
||||
// Metin Hizalama Değiştir (Sol -> Orta -> Sağ)
|
||||
const handleToggleAlign = () => {
|
||||
try {
|
||||
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
|
||||
} catch {}
|
||||
setTextAlign((prev) => {
|
||||
if (prev === "center") return "left";
|
||||
if (prev === "left") return "right";
|
||||
return "center";
|
||||
});
|
||||
};
|
||||
|
||||
// Renk Değiştir
|
||||
const handleSelectColor = (selectedColor: string) => {
|
||||
try {
|
||||
Haptics.selectionAsync();
|
||||
} catch {}
|
||||
setColor(selectedColor);
|
||||
if (bgStyle === "solid") {
|
||||
if (selectedColor === "#FFFFFF" || selectedColor === "#F6EFE3") {
|
||||
setBgBoxColor("rgba(18, 18, 21, 0.92)");
|
||||
} else if (selectedColor === "#121215") {
|
||||
setBgBoxColor("rgba(255, 255, 255, 0.92)");
|
||||
} else {
|
||||
setBgBoxColor(selectedColor);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Font Değiştir
|
||||
const handleSelectFont = (fId: string) => {
|
||||
try {
|
||||
Haptics.selectionAsync();
|
||||
} catch {}
|
||||
setFontId(fId);
|
||||
};
|
||||
|
||||
// Bitti / Kaydet
|
||||
const handleDone = () => {
|
||||
try {
|
||||
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Medium);
|
||||
} catch {}
|
||||
|
||||
let finalBg = bgBoxColor;
|
||||
if (bgStyle === "none" || bgStyle === "outline") {
|
||||
finalBg = undefined;
|
||||
}
|
||||
|
||||
onSave({
|
||||
text: text.trim() ? text : "Metin",
|
||||
fontId,
|
||||
color,
|
||||
fontSize,
|
||||
textAlign,
|
||||
bgBoxColor: finalBg,
|
||||
bgStyle,
|
||||
hasShadow: bgStyle === "outline" || (!finalBg && color !== "#121215"),
|
||||
});
|
||||
onClose();
|
||||
};
|
||||
|
||||
const fontFamily = getCanvasFontFamily(fontId);
|
||||
|
||||
if (!visible || !layer) return null;
|
||||
|
||||
return (
|
||||
<Modal
|
||||
visible={visible}
|
||||
transparent
|
||||
animationType="fade"
|
||||
statusBarTranslucent
|
||||
onRequestClose={onClose}
|
||||
>
|
||||
<View style={styles.overlayBackdrop}>
|
||||
<KeyboardAvoidingView
|
||||
behavior={Platform.OS === "ios" ? "padding" : "height"}
|
||||
style={[styles.container, { paddingTop: insets.top + 8, paddingBottom: insets.bottom + 12 }]}
|
||||
>
|
||||
{/* ÜST BAR - INSTAGRAM STORY KONTROLLERİ */}
|
||||
<View style={styles.topBar}>
|
||||
{/* Sol: İptal / Kapat */}
|
||||
<Pressable
|
||||
onPress={onClose}
|
||||
hitSlop={{ top: 10, bottom: 10, left: 10, right: 10 }}
|
||||
style={styles.iconButton}
|
||||
>
|
||||
<Ionicons name="close" size={24} color="#FFFFFF" />
|
||||
</Pressable>
|
||||
|
||||
{/* Orta Araçlar: Hizalama & Arka Plan Rozeti */}
|
||||
<View style={styles.centerTools}>
|
||||
{/* Hizalama Butonu */}
|
||||
<Pressable
|
||||
onPress={handleToggleAlign}
|
||||
hitSlop={{ top: 8, bottom: 8, left: 8, right: 8 }}
|
||||
style={styles.toolPill}
|
||||
>
|
||||
<Ionicons
|
||||
name={
|
||||
textAlign === "left"
|
||||
? "reorder-three-outline"
|
||||
: textAlign === "right"
|
||||
? "reorder-three-outline"
|
||||
: "reorder-two-outline"
|
||||
}
|
||||
size={20}
|
||||
color="#FFFFFF"
|
||||
/>
|
||||
</Pressable>
|
||||
|
||||
{/* Instagram "A" Arka Plan Butonu */}
|
||||
<Pressable
|
||||
onPress={handleToggleBgStyle}
|
||||
hitSlop={{ top: 8, bottom: 8, left: 8, right: 8 }}
|
||||
style={[
|
||||
styles.toolPill,
|
||||
bgStyle !== "none" && { backgroundColor: "#FFFFFF" },
|
||||
]}
|
||||
>
|
||||
<Text
|
||||
style={[
|
||||
styles.bgPillText,
|
||||
bgStyle !== "none" && { color: "#0C0C0E", fontWeight: "900" },
|
||||
]}
|
||||
>
|
||||
A
|
||||
</Text>
|
||||
{bgStyle === "soft" && (
|
||||
<View style={styles.softIndicator} />
|
||||
)}
|
||||
{bgStyle === "outline" && (
|
||||
<View style={styles.outlineIndicator} />
|
||||
)}
|
||||
</Pressable>
|
||||
|
||||
{/* Katmanı Sil (Varsa) */}
|
||||
{onDelete && (
|
||||
<Pressable
|
||||
onPress={() => {
|
||||
try {
|
||||
Haptics.notificationAsync(Haptics.NotificationFeedbackType.Warning);
|
||||
} catch {}
|
||||
onDelete(layer.id);
|
||||
onClose();
|
||||
}}
|
||||
hitSlop={{ top: 8, bottom: 8, left: 8, right: 8 }}
|
||||
style={[styles.toolPill, { backgroundColor: "rgba(239, 68, 68, 0.2)" }]}
|
||||
>
|
||||
<Ionicons name="trash-outline" size={17} color="#EF4444" />
|
||||
</Pressable>
|
||||
)}
|
||||
</View>
|
||||
|
||||
{/* Sağ: BİTTİ (Done) Butonu */}
|
||||
<Pressable
|
||||
onPress={handleDone}
|
||||
hitSlop={{ top: 8, bottom: 8, left: 8, right: 8 }}
|
||||
style={styles.doneButton}
|
||||
>
|
||||
<Text style={styles.doneButtonText}>Bitti</Text>
|
||||
</Pressable>
|
||||
</View>
|
||||
|
||||
{/* ORTA BÖLÜM: SOL BOYUT SLIDER & CANLI METİN ALANI */}
|
||||
<View style={styles.editorBody}>
|
||||
{/* Sol: Dikey Boyut Stepper / Gösterge */}
|
||||
<View style={styles.sizeControlCol}>
|
||||
<Pressable
|
||||
onPress={() => {
|
||||
try { Haptics.selectionAsync(); } catch {}
|
||||
setFontSize((s) => Math.min(48, s + 2));
|
||||
}}
|
||||
hitSlop={{ top: 8, bottom: 8, left: 8, right: 8 }}
|
||||
style={styles.sizeStepBtn}
|
||||
>
|
||||
<Ionicons name="add" size={16} color="#FFFFFF" />
|
||||
</Pressable>
|
||||
|
||||
<View style={styles.sizeTrack}>
|
||||
<View
|
||||
style={[
|
||||
styles.sizeTrackFill,
|
||||
{ height: `${Math.min(100, Math.max(10, ((fontSize - 8) / 40) * 100))}%` },
|
||||
]}
|
||||
/>
|
||||
</View>
|
||||
|
||||
<Pressable
|
||||
onPress={() => {
|
||||
try { Haptics.selectionAsync(); } catch {}
|
||||
setFontSize((s) => Math.max(8, s - 2));
|
||||
}}
|
||||
hitSlop={{ top: 8, bottom: 8, left: 8, right: 8 }}
|
||||
style={styles.sizeStepBtn}
|
||||
>
|
||||
<Ionicons name="remove" size={16} color="#FFFFFF" />
|
||||
</Pressable>
|
||||
|
||||
<Text style={styles.sizeLabel}>{fontSize}pt</Text>
|
||||
</View>
|
||||
|
||||
{/* Orta: Canlı İnstagram Yazı Alanı */}
|
||||
<View style={styles.textInputArea}>
|
||||
<View
|
||||
style={[
|
||||
styles.textPillWrapper,
|
||||
bgStyle === "solid" && {
|
||||
backgroundColor: bgBoxColor || "#D4AF37",
|
||||
borderRadius: 14,
|
||||
paddingHorizontal: 16,
|
||||
paddingVertical: 8,
|
||||
},
|
||||
bgStyle === "soft" && {
|
||||
backgroundColor: "rgba(255, 255, 255, 0.22)",
|
||||
borderRadius: 14,
|
||||
paddingHorizontal: 16,
|
||||
paddingVertical: 8,
|
||||
borderWidth: 1,
|
||||
borderColor: "rgba(255, 255, 255, 0.3)",
|
||||
},
|
||||
bgStyle === "outline" && {
|
||||
borderWidth: 1.5,
|
||||
borderColor: color,
|
||||
borderRadius: 14,
|
||||
paddingHorizontal: 14,
|
||||
paddingVertical: 6,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<TextInput
|
||||
ref={inputRef}
|
||||
value={text}
|
||||
onChangeText={setText}
|
||||
multiline
|
||||
placeholder="Yazmaya başla..."
|
||||
placeholderTextColor="rgba(255,255,255,0.4)"
|
||||
textAlign={textAlign}
|
||||
autoCapitalize="sentences"
|
||||
style={[
|
||||
styles.textInput,
|
||||
{
|
||||
fontFamily,
|
||||
fontSize,
|
||||
color:
|
||||
bgStyle === "solid" && (bgBoxColor === "#FFFFFF" || bgBoxColor === "#F6EFE3")
|
||||
? "#0C0C0E"
|
||||
: color,
|
||||
textAlign,
|
||||
letterSpacing: fontId === "serif" ? 1.5 : 0.5,
|
||||
textShadowColor: bgStyle === "outline" ? color : "rgba(0,0,0,0.7)",
|
||||
textShadowOffset: { width: 0, height: 1 },
|
||||
textShadowRadius: bgStyle === "outline" ? 8 : 4,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* ALT BÖLÜM 1: RENK SEÇİCİ BALONCUKLAR */}
|
||||
<View style={styles.colorPickerBar}>
|
||||
<ScrollView
|
||||
horizontal
|
||||
showsHorizontalScrollIndicator={false}
|
||||
contentContainerStyle={styles.colorScrollContent}
|
||||
>
|
||||
{CANVAS_COLOR_PALETTE.map((c) => {
|
||||
const isSelected = color === c.color;
|
||||
return (
|
||||
<Pressable
|
||||
key={c.id}
|
||||
onPress={() => handleSelectColor(c.color)}
|
||||
style={[
|
||||
styles.colorCircleWrapper,
|
||||
isSelected && { borderColor: "#FFFFFF", transform: [{ scale: 1.15 }] },
|
||||
]}
|
||||
>
|
||||
<View
|
||||
style={[
|
||||
styles.colorCircle,
|
||||
{ backgroundColor: c.color },
|
||||
c.color === "#121215" && { borderWidth: 1, borderColor: "rgba(255,255,255,0.4)" },
|
||||
]}
|
||||
/>
|
||||
</Pressable>
|
||||
);
|
||||
})}
|
||||
</ScrollView>
|
||||
</View>
|
||||
|
||||
{/* ALT BÖLÜM 2: INSTAGRAM FONT STİLLERİ CAROUSEL */}
|
||||
<View style={styles.fontPickerBar}>
|
||||
<ScrollView
|
||||
horizontal
|
||||
showsHorizontalScrollIndicator={false}
|
||||
contentContainerStyle={styles.fontScrollContent}
|
||||
>
|
||||
{CANVAS_FONT_OPTIONS.map((f) => {
|
||||
const isSelected = fontId === f.id;
|
||||
return (
|
||||
<Pressable
|
||||
key={f.id}
|
||||
onPress={() => handleSelectFont(f.id)}
|
||||
style={[
|
||||
styles.fontPill,
|
||||
isSelected && styles.fontPillActive,
|
||||
]}
|
||||
>
|
||||
<Text
|
||||
style={[
|
||||
styles.fontPillText,
|
||||
{ fontFamily: f.fontFamily },
|
||||
isSelected && styles.fontPillTextActive,
|
||||
]}
|
||||
>
|
||||
{f.label}
|
||||
</Text>
|
||||
</Pressable>
|
||||
);
|
||||
})}
|
||||
</ScrollView>
|
||||
</View>
|
||||
</KeyboardAvoidingView>
|
||||
</View>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
overlayBackdrop: {
|
||||
flex: 1,
|
||||
backgroundColor: "rgba(10, 10, 14, 0.94)",
|
||||
},
|
||||
container: {
|
||||
flex: 1,
|
||||
justifyContent: "space-between",
|
||||
},
|
||||
topBar: {
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
paddingHorizontal: 16,
|
||||
height: 52,
|
||||
zIndex: 10,
|
||||
},
|
||||
iconButton: {
|
||||
width: 40,
|
||||
height: 40,
|
||||
borderRadius: 20,
|
||||
backgroundColor: "rgba(255, 255, 255, 0.12)",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
},
|
||||
centerTools: {
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
gap: 10,
|
||||
},
|
||||
toolPill: {
|
||||
width: 38,
|
||||
height: 38,
|
||||
borderRadius: 19,
|
||||
backgroundColor: "rgba(255, 255, 255, 0.12)",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
position: "relative",
|
||||
},
|
||||
bgPillText: {
|
||||
color: "#FFFFFF",
|
||||
fontSize: 16,
|
||||
fontWeight: "700",
|
||||
},
|
||||
softIndicator: {
|
||||
position: "absolute",
|
||||
bottom: 4,
|
||||
width: 6,
|
||||
height: 6,
|
||||
borderRadius: 3,
|
||||
backgroundColor: "#60A5FA",
|
||||
},
|
||||
outlineIndicator: {
|
||||
position: "absolute",
|
||||
bottom: 4,
|
||||
width: 6,
|
||||
height: 6,
|
||||
borderRadius: 3,
|
||||
backgroundColor: "#F59E0B",
|
||||
},
|
||||
doneButton: {
|
||||
backgroundColor: "#FFFFFF",
|
||||
paddingHorizontal: 18,
|
||||
paddingVertical: 8,
|
||||
borderRadius: 20,
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
},
|
||||
doneButtonText: {
|
||||
color: "#0C0C0E",
|
||||
fontSize: 14,
|
||||
fontWeight: "800",
|
||||
},
|
||||
editorBody: {
|
||||
flex: 1,
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
paddingHorizontal: 16,
|
||||
},
|
||||
sizeControlCol: {
|
||||
width: 36,
|
||||
alignItems: "center",
|
||||
gap: 8,
|
||||
},
|
||||
sizeStepBtn: {
|
||||
width: 30,
|
||||
height: 30,
|
||||
borderRadius: 15,
|
||||
backgroundColor: "rgba(255, 255, 255, 0.15)",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
},
|
||||
sizeTrack: {
|
||||
width: 4,
|
||||
height: 120,
|
||||
backgroundColor: "rgba(255, 255, 255, 0.18)",
|
||||
borderRadius: 2,
|
||||
justifyContent: "flex-end",
|
||||
overflow: "hidden",
|
||||
},
|
||||
sizeTrackFill: {
|
||||
width: "100%",
|
||||
backgroundColor: "#D4AF37",
|
||||
borderRadius: 2,
|
||||
},
|
||||
sizeLabel: {
|
||||
color: "rgba(255, 255, 255, 0.6)",
|
||||
fontSize: 10,
|
||||
fontWeight: "700",
|
||||
},
|
||||
textInputArea: {
|
||||
flex: 1,
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
paddingHorizontal: 12,
|
||||
},
|
||||
textPillWrapper: {
|
||||
maxWidth: "100%",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
},
|
||||
textInput: {
|
||||
minWidth: 140,
|
||||
maxWidth: "100%",
|
||||
padding: 0,
|
||||
margin: 0,
|
||||
fontWeight: "800",
|
||||
},
|
||||
colorPickerBar: {
|
||||
paddingVertical: 10,
|
||||
},
|
||||
colorScrollContent: {
|
||||
paddingHorizontal: 20,
|
||||
gap: 12,
|
||||
alignItems: "center",
|
||||
},
|
||||
colorCircleWrapper: {
|
||||
width: 32,
|
||||
height: 32,
|
||||
borderRadius: 16,
|
||||
borderWidth: 2,
|
||||
borderColor: "transparent",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
},
|
||||
colorCircle: {
|
||||
width: 24,
|
||||
height: 24,
|
||||
borderRadius: 12,
|
||||
},
|
||||
fontPickerBar: {
|
||||
paddingVertical: 8,
|
||||
},
|
||||
fontScrollContent: {
|
||||
paddingHorizontal: 16,
|
||||
gap: 8,
|
||||
alignItems: "center",
|
||||
},
|
||||
fontPill: {
|
||||
paddingHorizontal: 16,
|
||||
paddingVertical: 8,
|
||||
borderRadius: 18,
|
||||
backgroundColor: "rgba(255, 255, 255, 0.08)",
|
||||
borderWidth: 1,
|
||||
borderColor: "rgba(255, 255, 255, 0.12)",
|
||||
},
|
||||
fontPillActive: {
|
||||
backgroundColor: "#FFFFFF",
|
||||
borderColor: "#FFFFFF",
|
||||
},
|
||||
fontPillText: {
|
||||
color: "rgba(255, 255, 255, 0.8)",
|
||||
fontSize: 12,
|
||||
fontWeight: "700",
|
||||
letterSpacing: 0.5,
|
||||
},
|
||||
fontPillTextActive: {
|
||||
color: "#0C0C0E",
|
||||
fontWeight: "900",
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,890 @@
|
||||
import React, { useRef, useState } from "react";
|
||||
import {
|
||||
Pressable,
|
||||
ScrollView,
|
||||
Text,
|
||||
View,
|
||||
} from "react-native";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import * as Haptics from "expo-haptics";
|
||||
import ViewShot from "react-native-view-shot";
|
||||
import { CanvasLayerItem } from "./CanvasLayerItem";
|
||||
import { CanvasQrItem } from "./CanvasQrItem";
|
||||
import { InstagramTextEditorModal } from "./InstagramTextEditorModal";
|
||||
import {
|
||||
type CanvasTextLayer,
|
||||
type CanvasQrConfig,
|
||||
QR_SIZE_PRESETS,
|
||||
QR_RADIUS_PRESETS,
|
||||
getDefaultLayersForTemplate,
|
||||
getDefaultQrConfigForTemplate,
|
||||
} from "./types";
|
||||
|
||||
interface QrCanvasStudioProps {
|
||||
templateKey: string;
|
||||
restaurantName: string;
|
||||
qrBase64?: string;
|
||||
width: number;
|
||||
height: number;
|
||||
renderBackground: () => React.ReactNode;
|
||||
layers: CanvasTextLayer[];
|
||||
onLayersChange: React.Dispatch<React.SetStateAction<CanvasTextLayer[]>>;
|
||||
qrConfig: CanvasQrConfig;
|
||||
onQrConfigChange: React.Dispatch<React.SetStateAction<CanvasQrConfig>>;
|
||||
viewShotRef?: React.RefObject<any>;
|
||||
selectedLayerId: string | null;
|
||||
onSelectLayer: (id: string | null) => void;
|
||||
isQrSelected: boolean;
|
||||
onSelectQr: (selected: boolean) => void;
|
||||
onDragStateChange?: (isDragging: boolean) => void;
|
||||
}
|
||||
|
||||
export const QrCanvasStudio: React.FC<QrCanvasStudioProps> = ({
|
||||
templateKey,
|
||||
restaurantName,
|
||||
qrBase64,
|
||||
width,
|
||||
height,
|
||||
renderBackground,
|
||||
layers,
|
||||
onLayersChange,
|
||||
qrConfig,
|
||||
onQrConfigChange,
|
||||
viewShotRef,
|
||||
selectedLayerId,
|
||||
onSelectLayer,
|
||||
isQrSelected,
|
||||
onSelectQr,
|
||||
onDragStateChange,
|
||||
}) => {
|
||||
const selectedLayer = layers.find((l) => l.id === selectedLayerId) || null;
|
||||
|
||||
// Instagram Metin Düzenleyici Modal Durumu
|
||||
const [editingLayer, setEditingLayer] = useState<CanvasTextLayer | null>(null);
|
||||
const [isTextModalOpen, setIsTextModalOpen] = useState(false);
|
||||
|
||||
// Instagram/Figma Kılavuz Çizgileri
|
||||
const [guideLines, setGuideLines] = useState<{
|
||||
verticalCenter: boolean;
|
||||
horizontalCenter: boolean;
|
||||
}>({ verticalCenter: false, horizontalCenter: false });
|
||||
|
||||
const lastVRef = useRef(false);
|
||||
const lastHRef = useRef(false);
|
||||
|
||||
// Sürükleme Başlangıcı
|
||||
const handleDragStart = () => {
|
||||
onDragStateChange?.(true);
|
||||
};
|
||||
|
||||
// Manyetik Yapışma (Snap) & Kılavuz Çizgileri
|
||||
const handleSnap = (rawX: number, rawY: number, itemWidth: number, itemHeight: number) => {
|
||||
const SNAP_THRESHOLD = 6;
|
||||
let snappedX = Math.round(rawX);
|
||||
let snappedY = Math.round(rawY);
|
||||
let vCenter = false;
|
||||
let hCenter = false;
|
||||
|
||||
// 1. Dikey Kılavuz (Yatayda Tam Ortalandığında)
|
||||
const itemCenterX = rawX + itemWidth / 2;
|
||||
const canvasCenterX = width / 2;
|
||||
if (Math.abs(itemCenterX - canvasCenterX) <= SNAP_THRESHOLD) {
|
||||
snappedX = Math.round(canvasCenterX - itemWidth / 2);
|
||||
vCenter = true;
|
||||
}
|
||||
|
||||
// 2. Yatay Kılavuz (Dikeyde Tam Ortalandığında)
|
||||
const itemCenterY = rawY + itemHeight / 2;
|
||||
const canvasCenterY = height / 2;
|
||||
if (Math.abs(itemCenterY - canvasCenterY) <= SNAP_THRESHOLD) {
|
||||
snappedY = Math.round(canvasCenterY - itemHeight / 2);
|
||||
hCenter = true;
|
||||
}
|
||||
|
||||
// Dokunsal Titreşim
|
||||
if ((vCenter && !lastVRef.current) || (hCenter && !lastHRef.current)) {
|
||||
try {
|
||||
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
|
||||
} catch {}
|
||||
}
|
||||
lastVRef.current = vCenter;
|
||||
lastHRef.current = hCenter;
|
||||
|
||||
setGuideLines({ verticalCenter: vCenter, horizontalCenter: hCenter });
|
||||
return { snappedX, snappedY };
|
||||
};
|
||||
|
||||
// Sürükleme Bitişi
|
||||
const handleDragEnd = () => {
|
||||
setGuideLines({ verticalCenter: false, horizontalCenter: false });
|
||||
lastVRef.current = false;
|
||||
lastHRef.current = false;
|
||||
onDragStateChange?.(false);
|
||||
};
|
||||
|
||||
// Metin Katmanı Pozisyon Güncelleme
|
||||
const handleUpdateLayerPosition = (id: string, x: number, y: number) => {
|
||||
onLayersChange((prev) =>
|
||||
prev.map((l) => (l.id === id ? { ...l, x, y } : l))
|
||||
);
|
||||
};
|
||||
|
||||
// QR Kod Pozisyon Güncelleme
|
||||
const handleUpdateQrPosition = (x: number, y: number) => {
|
||||
onQrConfigChange((prev) => ({ ...prev, x, y }));
|
||||
};
|
||||
|
||||
// QR Kodu Yatayda Ortala
|
||||
const handleCenterQrHorizontally = () => {
|
||||
const centeredX = Math.round((width - qrConfig.size) / 2);
|
||||
onQrConfigChange((prev) => ({ ...prev, x: centeredX }));
|
||||
try {
|
||||
Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success);
|
||||
} catch {}
|
||||
};
|
||||
|
||||
// Seçili Metni Yatayda Ortala
|
||||
const handleCenterLayerHorizontally = (id: string) => {
|
||||
const target = layers.find((l) => l.id === id);
|
||||
if (!target) return;
|
||||
const approxW = 80;
|
||||
const centeredX = Math.round((width - approxW) / 2);
|
||||
onLayersChange((prev) =>
|
||||
prev.map((l) => (l.id === id ? { ...l, x: centeredX } : l))
|
||||
);
|
||||
try {
|
||||
Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success);
|
||||
} catch {}
|
||||
};
|
||||
|
||||
// Yeni Metin Katmanı Ekle (Instagram "Aa" butonu)
|
||||
const handleAddTextLayer = () => {
|
||||
try {
|
||||
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Medium);
|
||||
} catch {}
|
||||
const newLayer: CanvasTextLayer = {
|
||||
id: `layer_${Date.now()}`,
|
||||
text: "Yeni Metin",
|
||||
x: Math.round(width / 2 - 45),
|
||||
y: Math.round(height / 2 - 20),
|
||||
rotation: 0,
|
||||
fontSize: 16,
|
||||
fontId: "sans",
|
||||
color: "#FFFFFF",
|
||||
bgBoxColor: "rgba(18, 18, 21, 0.88)",
|
||||
fontWeight: "800",
|
||||
textAlign: "center",
|
||||
};
|
||||
onLayersChange((prev) => [...prev, newLayer]);
|
||||
onSelectQr(false);
|
||||
onSelectLayer(newLayer.id);
|
||||
|
||||
// Doğrudan Instagram Text Editor'ı aç
|
||||
setEditingLayer(newLayer);
|
||||
setIsTextModalOpen(true);
|
||||
};
|
||||
|
||||
// Katman Düzenle Modalını Aç
|
||||
const handleOpenTextEditor = (layer: CanvasTextLayer) => {
|
||||
setEditingLayer(layer);
|
||||
setIsTextModalOpen(true);
|
||||
};
|
||||
|
||||
// Katman Düzenleme Kaydet
|
||||
const handleSaveTextEditor = (updated: Partial<CanvasTextLayer>) => {
|
||||
if (!editingLayer) return;
|
||||
onLayersChange((prev) =>
|
||||
prev.map((l) => (l.id === editingLayer.id ? { ...l, ...updated } : l))
|
||||
);
|
||||
};
|
||||
|
||||
// Katman Sil
|
||||
const handleDeleteLayer = (id: string) => {
|
||||
try {
|
||||
Haptics.notificationAsync(Haptics.NotificationFeedbackType.Warning);
|
||||
} catch {}
|
||||
onLayersChange((prev) => prev.filter((l) => l.id !== id));
|
||||
if (selectedLayerId === id) {
|
||||
onSelectLayer(null);
|
||||
}
|
||||
};
|
||||
|
||||
// Katman Çoğalt
|
||||
const handleDuplicateLayer = (id: string) => {
|
||||
try {
|
||||
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
|
||||
} catch {}
|
||||
const target = layers.find((l) => l.id === id);
|
||||
if (!target) return;
|
||||
const duplicated: CanvasTextLayer = {
|
||||
...target,
|
||||
id: `layer_${Date.now()}`,
|
||||
x: Math.min(width - 50, target.x + 12),
|
||||
y: Math.min(height - 30, target.y + 12),
|
||||
};
|
||||
onLayersChange((prev) => [...prev, duplicated]);
|
||||
onSelectQr(false);
|
||||
onSelectLayer(duplicated.id);
|
||||
};
|
||||
|
||||
// Şablonu Sıfırla
|
||||
const handleResetLayers = () => {
|
||||
try {
|
||||
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Medium);
|
||||
} catch {}
|
||||
const defaultLayers = getDefaultLayersForTemplate(templateKey, restaurantName);
|
||||
const defaultQr = getDefaultQrConfigForTemplate(templateKey, width, height);
|
||||
onLayersChange(defaultLayers);
|
||||
onQrConfigChange(defaultQr);
|
||||
onSelectQr(false);
|
||||
onSelectLayer(null);
|
||||
};
|
||||
|
||||
return (
|
||||
<View style={{ width: "100%", alignItems: "center" }}>
|
||||
{/* 1. INSTAGRAM STORY / CANVA STİLİ ÜST HIZLI ARAÇLAR */}
|
||||
<View
|
||||
style={{
|
||||
width: "100%",
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
marginBottom: 12,
|
||||
paddingHorizontal: 4,
|
||||
}}
|
||||
>
|
||||
<View style={{ flexDirection: "row", alignItems: "center", gap: 8 }}>
|
||||
{/* Metin Ekle (+ Aa) */}
|
||||
<Pressable
|
||||
onPress={handleAddTextLayer}
|
||||
hitSlop={{ top: 6, bottom: 6, left: 6, right: 6 }}
|
||||
accessible={true}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel="Yeni Metin Ekle"
|
||||
style={({ pressed }) => ({
|
||||
backgroundColor: "#D4AF37",
|
||||
paddingHorizontal: 14,
|
||||
paddingVertical: 8,
|
||||
borderRadius: 20,
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
gap: 6,
|
||||
opacity: pressed ? 0.8 : 1,
|
||||
})}
|
||||
>
|
||||
<Text style={{ color: "#0C0C0E", fontSize: 13, fontWeight: "900" }}>+ Aa</Text>
|
||||
<Text style={{ color: "#0C0C0E", fontSize: 12, fontWeight: "700" }}>Metin</Text>
|
||||
</Pressable>
|
||||
|
||||
{/* QR Seçici / Ayar */}
|
||||
<Pressable
|
||||
onPress={() => {
|
||||
try { Haptics.selectionAsync(); } catch {}
|
||||
onSelectLayer(null);
|
||||
onSelectQr(!isQrSelected);
|
||||
}}
|
||||
hitSlop={{ top: 6, bottom: 6, left: 6, right: 6 }}
|
||||
style={({ pressed }) => ({
|
||||
backgroundColor: isQrSelected ? "#D4AF37" : "#18181B",
|
||||
borderWidth: 1,
|
||||
borderColor: isQrSelected ? "#D4AF37" : "rgba(255,255,255,0.12)",
|
||||
paddingHorizontal: 12,
|
||||
paddingVertical: 8,
|
||||
borderRadius: 20,
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
gap: 5,
|
||||
opacity: pressed ? 0.8 : 1,
|
||||
})}
|
||||
>
|
||||
<Ionicons
|
||||
name="qr-code-outline"
|
||||
size={15}
|
||||
color={isQrSelected ? "#0C0C0E" : "#FFFFFF"}
|
||||
/>
|
||||
<Text
|
||||
style={{
|
||||
color: isQrSelected ? "#0C0C0E" : "#FFFFFF",
|
||||
fontSize: 12,
|
||||
fontWeight: "700",
|
||||
}}
|
||||
>
|
||||
QR Kodu
|
||||
</Text>
|
||||
</Pressable>
|
||||
</View>
|
||||
|
||||
{/* Sağ: Sıfırla */}
|
||||
<Pressable
|
||||
onPress={handleResetLayers}
|
||||
hitSlop={{ top: 6, bottom: 6, left: 6, right: 6 }}
|
||||
style={({ pressed }) => ({
|
||||
backgroundColor: "#18181B",
|
||||
borderWidth: 1,
|
||||
borderColor: "rgba(255,255,255,0.1)",
|
||||
paddingHorizontal: 10,
|
||||
paddingVertical: 7,
|
||||
borderRadius: 16,
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
gap: 4,
|
||||
opacity: pressed ? 0.8 : 1,
|
||||
})}
|
||||
>
|
||||
<Ionicons name="refresh-outline" size={14} color="#A1A1AA" />
|
||||
<Text style={{ color: "#A1A1AA", fontSize: 11, fontWeight: "600" }}>Sıfırla</Text>
|
||||
</Pressable>
|
||||
</View>
|
||||
|
||||
{/* 2. TUVAL ALANI (INSTAGRAM KORUYUCU DOKUNMATİK ÇERÇEVE) */}
|
||||
<Pressable
|
||||
onPress={() => {
|
||||
// Tuvalin boş alanına dokunulduğunda seçimi kaldır (temiz önizleme)
|
||||
onSelectLayer(null);
|
||||
onSelectQr(false);
|
||||
}}
|
||||
style={{
|
||||
width,
|
||||
height,
|
||||
borderRadius: 24,
|
||||
overflow: "hidden",
|
||||
backgroundColor: "#0C0C0E",
|
||||
shadowColor: "#000",
|
||||
shadowOffset: { width: 0, height: 10 },
|
||||
shadowOpacity: 0.5,
|
||||
shadowRadius: 20,
|
||||
elevation: 8,
|
||||
marginBottom: 14,
|
||||
position: "relative",
|
||||
}}
|
||||
>
|
||||
<ViewShot
|
||||
ref={viewShotRef}
|
||||
options={{ format: "png", quality: 1.0 }}
|
||||
style={{ width, height, position: "relative" }}
|
||||
>
|
||||
{/* Arka Plan Şablonu */}
|
||||
<View pointerEvents="none" style={{ position: "absolute", top: 0, left: 0, width, height }}>
|
||||
{renderBackground()}
|
||||
</View>
|
||||
|
||||
{/* Sürüklenebilir & Boyutlandırılabilir QR Kod */}
|
||||
<CanvasQrItem
|
||||
qrConfig={qrConfig}
|
||||
qrBase64={qrBase64}
|
||||
isSelected={isQrSelected}
|
||||
onSelect={() => {
|
||||
try { Haptics.selectionAsync(); } catch {}
|
||||
onSelectLayer(null);
|
||||
onSelectQr(true);
|
||||
}}
|
||||
onUpdatePosition={handleUpdateQrPosition}
|
||||
onDragStart={handleDragStart}
|
||||
onDragMove={handleSnap}
|
||||
onDragEnd={handleDragEnd}
|
||||
canvasWidth={width}
|
||||
canvasHeight={height}
|
||||
/>
|
||||
|
||||
{/* Sürüklenebilir & Çift Dokunmalı Metin Katmanları */}
|
||||
{layers.map((layer) => (
|
||||
<CanvasLayerItem
|
||||
key={layer.id}
|
||||
layer={layer}
|
||||
isSelected={selectedLayerId === layer.id}
|
||||
onSelect={(id) => {
|
||||
try { Haptics.selectionAsync(); } catch {}
|
||||
onSelectQr(false);
|
||||
onSelectLayer(id);
|
||||
}}
|
||||
onEdit={handleOpenTextEditor}
|
||||
onUpdatePosition={handleUpdateLayerPosition}
|
||||
onDelete={handleDeleteLayer}
|
||||
onDragStart={handleDragStart}
|
||||
onDragMove={handleSnap}
|
||||
onDragEnd={handleDragEnd}
|
||||
canvasWidth={width}
|
||||
canvasHeight={height}
|
||||
/>
|
||||
))}
|
||||
</ViewShot>
|
||||
|
||||
{/* 📐 FIGMA / INSTAGRAM MANYETİK KILAVUZ ÇİZGİLERİ */}
|
||||
{guideLines.verticalCenter && (
|
||||
<View
|
||||
pointerEvents="none"
|
||||
style={{
|
||||
position: "absolute",
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
left: Math.round(width / 2) - 0.75,
|
||||
width: 1.5,
|
||||
backgroundColor: "#D4AF37",
|
||||
zIndex: 999,
|
||||
shadowColor: "#D4AF37",
|
||||
shadowOffset: { width: 0, height: 0 },
|
||||
shadowOpacity: 0.9,
|
||||
shadowRadius: 4,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
{guideLines.horizontalCenter && (
|
||||
<View
|
||||
pointerEvents="none"
|
||||
style={{
|
||||
position: "absolute",
|
||||
left: 0,
|
||||
right: 0,
|
||||
top: Math.round(height / 2) - 0.75,
|
||||
height: 1.5,
|
||||
backgroundColor: "#D4AF37",
|
||||
zIndex: 999,
|
||||
shadowColor: "#D4AF37",
|
||||
shadowOffset: { width: 0, height: 0 },
|
||||
shadowOpacity: 0.9,
|
||||
shadowRadius: 4,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Pressable>
|
||||
|
||||
{/* 3. SEÇİLİ ÖĞE İÇİN INSTAGRAM TARZI YÜZEN HIZLI ARAÇLAR (FLOATING DOCK) */}
|
||||
{/* 3A: SEÇİLİ METİN KATMANI ARAÇLARI */}
|
||||
{selectedLayer && !isQrSelected && (
|
||||
<View
|
||||
style={{
|
||||
width: "100%",
|
||||
backgroundColor: "#18181B",
|
||||
borderRadius: 18,
|
||||
borderWidth: 1,
|
||||
borderColor: "rgba(212, 175, 55, 0.3)",
|
||||
padding: 12,
|
||||
marginBottom: 14,
|
||||
gap: 10,
|
||||
}}
|
||||
>
|
||||
{/* Üst Sıra: Hızlı Butonlar */}
|
||||
<View style={{ flexDirection: "row", alignItems: "center", justifyContent: "space-between" }}>
|
||||
<Text style={{ color: "#D4AF37", fontSize: 11, fontWeight: "800", letterSpacing: 0.5 }}>
|
||||
SEÇİLİ METİN
|
||||
</Text>
|
||||
|
||||
<View style={{ flexDirection: "row", alignItems: "center", gap: 6 }}>
|
||||
{/* Düzenle (Aa) Modalını Aç */}
|
||||
<Pressable
|
||||
onPress={() => handleOpenTextEditor(selectedLayer)}
|
||||
hitSlop={{ top: 6, bottom: 6, left: 6, right: 6 }}
|
||||
style={{
|
||||
backgroundColor: "#D4AF37",
|
||||
paddingHorizontal: 12,
|
||||
paddingVertical: 5,
|
||||
borderRadius: 14,
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
gap: 4,
|
||||
}}
|
||||
>
|
||||
<Ionicons name="create-outline" size={13} color="#0C0C0E" />
|
||||
<Text style={{ color: "#0C0C0E", fontSize: 11, fontWeight: "800" }}>Aa Düzenle</Text>
|
||||
</Pressable>
|
||||
|
||||
{/* Yatayda Ortala */}
|
||||
<Pressable
|
||||
onPress={() => handleCenterLayerHorizontally(selectedLayer.id)}
|
||||
hitSlop={{ top: 6, bottom: 6, left: 6, right: 6 }}
|
||||
style={{
|
||||
backgroundColor: "#242429",
|
||||
paddingHorizontal: 10,
|
||||
paddingVertical: 5,
|
||||
borderRadius: 14,
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
gap: 4,
|
||||
}}
|
||||
>
|
||||
<Ionicons name="swap-horizontal" size={12} color="#FFFFFF" />
|
||||
<Text style={{ color: "#FFFFFF", fontSize: 11, fontWeight: "600" }}>Ortala</Text>
|
||||
</Pressable>
|
||||
|
||||
{/* Çoğalt */}
|
||||
<Pressable
|
||||
onPress={() => handleDuplicateLayer(selectedLayer.id)}
|
||||
hitSlop={{ top: 6, bottom: 6, left: 6, right: 6 }}
|
||||
style={{
|
||||
backgroundColor: "#242429",
|
||||
paddingHorizontal: 8,
|
||||
paddingVertical: 5,
|
||||
borderRadius: 14,
|
||||
}}
|
||||
>
|
||||
<Ionicons name="copy-outline" size={13} color="#FFFFFF" />
|
||||
</Pressable>
|
||||
|
||||
{/* Sil */}
|
||||
<Pressable
|
||||
onPress={() => handleDeleteLayer(selectedLayer.id)}
|
||||
hitSlop={{ top: 6, bottom: 6, left: 6, right: 6 }}
|
||||
style={{
|
||||
backgroundColor: "rgba(239, 68, 68, 0.15)",
|
||||
paddingHorizontal: 8,
|
||||
paddingVertical: 5,
|
||||
borderRadius: 14,
|
||||
}}
|
||||
>
|
||||
<Ionicons name="trash-outline" size={13} color="#EF4444" />
|
||||
</Pressable>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* Alt Sıra: Hızlı Döndürme Açısı Hapları */}
|
||||
<View style={{ flexDirection: "row", alignItems: "center", gap: 6 }}>
|
||||
<Text style={{ color: "#71717A", fontSize: 10, fontWeight: "700" }}>EĞİM:</Text>
|
||||
<ScrollView horizontal showsHorizontalScrollIndicator={false} contentContainerStyle={{ gap: 6 }}>
|
||||
{[-15, -6, 0, 6, 15].map((deg) => {
|
||||
const isActive = (selectedLayer.rotation || 0) === deg;
|
||||
return (
|
||||
<Pressable
|
||||
key={deg}
|
||||
onPress={() => {
|
||||
try { Haptics.selectionAsync(); } catch {}
|
||||
onLayersChange((prev) =>
|
||||
prev.map((l) => (l.id === selectedLayer.id ? { ...l, rotation: deg } : l))
|
||||
);
|
||||
}}
|
||||
style={{
|
||||
paddingHorizontal: 10,
|
||||
paddingVertical: 4,
|
||||
borderRadius: 10,
|
||||
backgroundColor: isActive ? "#D4AF37" : "#242429",
|
||||
}}
|
||||
>
|
||||
<Text
|
||||
style={{
|
||||
color: isActive ? "#0C0C0E" : "#FFFFFF",
|
||||
fontSize: 10.5,
|
||||
fontWeight: isActive ? "800" : "600",
|
||||
}}
|
||||
>
|
||||
{deg}°
|
||||
</Text>
|
||||
</Pressable>
|
||||
);
|
||||
})}
|
||||
</ScrollView>
|
||||
</View>
|
||||
</View>
|
||||
)}
|
||||
|
||||
{/* 3B: SEÇİLİ QR KOD ARAÇLARI (TAM GELİŞMİŞ AYARLAR) */}
|
||||
{isQrSelected && (
|
||||
<View
|
||||
style={{
|
||||
width: "100%",
|
||||
backgroundColor: "#18181B",
|
||||
borderRadius: 20,
|
||||
borderWidth: 1.5,
|
||||
borderColor: "rgba(212, 175, 55, 0.45)",
|
||||
padding: 14,
|
||||
marginBottom: 14,
|
||||
gap: 12,
|
||||
shadowColor: "#000",
|
||||
shadowOffset: { width: 0, height: 6 },
|
||||
shadowOpacity: 0.35,
|
||||
shadowRadius: 10,
|
||||
elevation: 6,
|
||||
}}
|
||||
>
|
||||
{/* Üst Başlık & Ortala / Kapat */}
|
||||
<View style={{ flexDirection: "row", alignItems: "center", justifyContent: "space-between" }}>
|
||||
<View style={{ flexDirection: "row", alignItems: "center", gap: 6 }}>
|
||||
<Ionicons name="qr-code" size={15} color="#D4AF37" />
|
||||
<Text style={{ color: "#D4AF37", fontSize: 12, fontWeight: "800", letterSpacing: 0.5 }}>
|
||||
QR KOD AYARLARI
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
<View style={{ flexDirection: "row", alignItems: "center", gap: 8 }}>
|
||||
<Pressable
|
||||
onPress={handleCenterQrHorizontally}
|
||||
hitSlop={{ top: 6, bottom: 6, left: 6, right: 6 }}
|
||||
style={({ pressed }) => ({
|
||||
backgroundColor: "#242429",
|
||||
paddingHorizontal: 11,
|
||||
paddingVertical: 5,
|
||||
borderRadius: 14,
|
||||
borderWidth: 1,
|
||||
borderColor: "rgba(212, 175, 55, 0.3)",
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
gap: 5,
|
||||
opacity: pressed ? 0.8 : 1,
|
||||
})}
|
||||
>
|
||||
<Ionicons name="swap-horizontal" size={13} color="#D4AF37" />
|
||||
<Text style={{ color: "#D4AF37", fontSize: 11, fontWeight: "700" }}>Ortala</Text>
|
||||
</Pressable>
|
||||
|
||||
<Pressable
|
||||
onPress={() => onSelectQr(false)}
|
||||
hitSlop={{ top: 6, bottom: 6, left: 6, right: 6 }}
|
||||
style={{
|
||||
backgroundColor: "rgba(255,255,255,0.08)",
|
||||
paddingHorizontal: 9,
|
||||
paddingVertical: 5,
|
||||
borderRadius: 14,
|
||||
}}
|
||||
>
|
||||
<Text style={{ color: "#A1A1AA", fontSize: 11, fontWeight: "600" }}>Kapat</Text>
|
||||
</Pressable>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* 1. Hassas Boyut Stepper'ı & Hızlı Seçenekler */}
|
||||
<View style={{ gap: 6 }}>
|
||||
<View style={{ flexDirection: "row", alignItems: "center", justifyContent: "space-between" }}>
|
||||
<Text style={{ color: "#71717A", fontSize: 10.5, fontWeight: "700" }}>BOYUT:</Text>
|
||||
<View style={{ flexDirection: "row", alignItems: "center", gap: 6 }}>
|
||||
<Pressable
|
||||
onPress={() => {
|
||||
try { Haptics.selectionAsync(); } catch {}
|
||||
onQrConfigChange((prev) => ({ ...prev, size: Math.max(70, prev.size - 5) }));
|
||||
}}
|
||||
style={{
|
||||
backgroundColor: "#27272A",
|
||||
width: 28,
|
||||
height: 24,
|
||||
borderRadius: 8,
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
}}
|
||||
>
|
||||
<Text style={{ color: "#FFF", fontWeight: "800", fontSize: 13 }}>-</Text>
|
||||
</Pressable>
|
||||
|
||||
<View
|
||||
style={{
|
||||
backgroundColor: "#0C0C0E",
|
||||
paddingHorizontal: 8,
|
||||
paddingVertical: 3,
|
||||
borderRadius: 6,
|
||||
borderWidth: 1,
|
||||
borderColor: "rgba(255,255,255,0.1)",
|
||||
}}
|
||||
>
|
||||
<Text style={{ color: "#D4AF37", fontSize: 11, fontWeight: "800", fontVariant: ["tabular-nums"] }}>
|
||||
{qrConfig.size} px
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
<Pressable
|
||||
onPress={() => {
|
||||
try { Haptics.selectionAsync(); } catch {}
|
||||
onQrConfigChange((prev) => ({ ...prev, size: Math.min(220, prev.size + 5) }));
|
||||
}}
|
||||
style={{
|
||||
backgroundColor: "#27272A",
|
||||
width: 28,
|
||||
height: 24,
|
||||
borderRadius: 8,
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
}}
|
||||
>
|
||||
<Text style={{ color: "#FFF", fontWeight: "800", fontSize: 13 }}>+</Text>
|
||||
</Pressable>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View style={{ flexDirection: "row", gap: 5 }}>
|
||||
{[
|
||||
{ label: "Kompakt", size: 95 },
|
||||
{ label: "Standart", size: 115 },
|
||||
{ label: "Büyük", size: 135 },
|
||||
{ label: "Geniş", size: 155 },
|
||||
].map((p) => {
|
||||
const isActive = Math.abs(qrConfig.size - p.size) <= 5;
|
||||
return (
|
||||
<Pressable
|
||||
key={p.label}
|
||||
onPress={() => {
|
||||
try { Haptics.selectionAsync(); } catch {}
|
||||
onQrConfigChange((prev) => ({ ...prev, size: p.size }));
|
||||
}}
|
||||
style={{
|
||||
flex: 1,
|
||||
backgroundColor: isActive ? "#D4AF37" : "#242429",
|
||||
paddingVertical: 5,
|
||||
borderRadius: 8,
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<Text
|
||||
style={{
|
||||
color: isActive ? "#0C0C0E" : "#FFFFFF",
|
||||
fontSize: 10,
|
||||
fontWeight: isActive ? "800" : "600",
|
||||
}}
|
||||
>
|
||||
{p.label}
|
||||
</Text>
|
||||
</Pressable>
|
||||
);
|
||||
})}
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* 2. Kenarlık Rengi (Border Color) */}
|
||||
<View style={{ gap: 6 }}>
|
||||
<Text style={{ color: "#71717A", fontSize: 10.5, fontWeight: "700" }}>ÇERÇEVE KENARLIK RENGİ:</Text>
|
||||
<ScrollView horizontal showsHorizontalScrollIndicator={false} contentContainerStyle={{ gap: 8, alignItems: "center" }}>
|
||||
{[
|
||||
{ color: "#D4AF37", name: "Altın" },
|
||||
{ color: "#F6EFE2", name: "Krem" },
|
||||
{ color: "#FFFFFF", name: "Beyaz" },
|
||||
{ color: "#18181B", name: "Siyah" },
|
||||
{ color: "#8C6239", name: "Bronz" },
|
||||
{ color: "#0C4443", name: "Zümrüt" },
|
||||
{ color: "#B92B27", name: "Kırmızı" },
|
||||
{ color: "transparent", name: "Yok" },
|
||||
].map((item) => {
|
||||
const isSelected = (qrConfig.borderColor || "#D4AF37").toLowerCase() === item.color.toLowerCase();
|
||||
return (
|
||||
<Pressable
|
||||
key={item.name}
|
||||
onPress={() => {
|
||||
try { Haptics.selectionAsync(); } catch {}
|
||||
onQrConfigChange((prev) => ({ ...prev, borderColor: item.color }));
|
||||
}}
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
gap: 5,
|
||||
paddingHorizontal: 8,
|
||||
paddingVertical: 4,
|
||||
borderRadius: 10,
|
||||
backgroundColor: isSelected ? "rgba(212, 175, 55, 0.25)" : "#242429",
|
||||
borderWidth: 1.5,
|
||||
borderColor: isSelected ? "#D4AF37" : "transparent",
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
width: 14,
|
||||
height: 14,
|
||||
borderRadius: 7,
|
||||
backgroundColor: item.color === "transparent" ? "#3F3F46" : item.color,
|
||||
borderWidth: 1,
|
||||
borderColor: "rgba(255,255,255,0.4)",
|
||||
}}
|
||||
/>
|
||||
<Text style={{ color: isSelected ? "#D4AF37" : "#E4E4E7", fontSize: 10.5, fontWeight: "700" }}>
|
||||
{item.name}
|
||||
</Text>
|
||||
</Pressable>
|
||||
);
|
||||
})}
|
||||
</ScrollView>
|
||||
</View>
|
||||
|
||||
{/* 3. Köşe Yuvarlaklığı & Kenarlık Kalınlığı */}
|
||||
<View style={{ flexDirection: "row", gap: 10 }}>
|
||||
{/* Köşe */}
|
||||
<View style={{ flex: 1, gap: 5 }}>
|
||||
<Text style={{ color: "#71717A", fontSize: 10, fontWeight: "700" }}>KÖŞE OVAL:</Text>
|
||||
<View style={{ flexDirection: "row", gap: 4 }}>
|
||||
{[
|
||||
{ label: "Düz", radius: 0 },
|
||||
{ label: "Hafif", radius: 8 },
|
||||
{ label: "Zarif", radius: 14 },
|
||||
{ label: "Yuvarlak", radius: 24 },
|
||||
].map((r) => {
|
||||
const isActive = qrConfig.borderRadius === r.radius;
|
||||
return (
|
||||
<Pressable
|
||||
key={r.label}
|
||||
onPress={() => {
|
||||
try { Haptics.selectionAsync(); } catch {}
|
||||
onQrConfigChange((prev) => ({ ...prev, borderRadius: r.radius }));
|
||||
}}
|
||||
style={{
|
||||
flex: 1,
|
||||
backgroundColor: isActive ? "#D4AF37" : "#242429",
|
||||
paddingVertical: 5,
|
||||
borderRadius: 7,
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<Text
|
||||
style={{
|
||||
color: isActive ? "#0C0C0E" : "#FFFFFF",
|
||||
fontSize: 9.5,
|
||||
fontWeight: isActive ? "800" : "600",
|
||||
}}
|
||||
>
|
||||
{r.label}
|
||||
</Text>
|
||||
</Pressable>
|
||||
);
|
||||
})}
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* Kalınlık */}
|
||||
<View style={{ flex: 1, gap: 5 }}>
|
||||
<Text style={{ color: "#71717A", fontSize: 10, fontWeight: "700" }}>KALINLIK:</Text>
|
||||
<View style={{ flexDirection: "row", gap: 4 }}>
|
||||
{[
|
||||
{ label: "0px", width: 0 },
|
||||
{ label: "1px", width: 1 },
|
||||
{ label: "2px", width: 2 },
|
||||
{ label: "3px", width: 3 },
|
||||
].map((w) => {
|
||||
const isActive = (qrConfig.borderWidth ?? 2) === w.width;
|
||||
return (
|
||||
<Pressable
|
||||
key={w.label}
|
||||
onPress={() => {
|
||||
try { Haptics.selectionAsync(); } catch {}
|
||||
onQrConfigChange((prev) => ({ ...prev, borderWidth: w.width }));
|
||||
}}
|
||||
style={{
|
||||
flex: 1,
|
||||
backgroundColor: isActive ? "#D4AF37" : "#242429",
|
||||
paddingVertical: 5,
|
||||
borderRadius: 7,
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<Text
|
||||
style={{
|
||||
color: isActive ? "#0C0C0E" : "#FFFFFF",
|
||||
fontSize: 9.5,
|
||||
fontWeight: isActive ? "800" : "600",
|
||||
}}
|
||||
>
|
||||
{w.label}
|
||||
</Text>
|
||||
</Pressable>
|
||||
);
|
||||
})}
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
)}
|
||||
|
||||
{/* 4. INSTAGRAM METİN DÜZENLEME MODALI */}
|
||||
<InstagramTextEditorModal
|
||||
visible={isTextModalOpen}
|
||||
layer={editingLayer}
|
||||
onSave={handleSaveTextEditor}
|
||||
onClose={() => {
|
||||
setIsTextModalOpen(false);
|
||||
setEditingLayer(null);
|
||||
}}
|
||||
onDelete={handleDeleteLayer}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,5 @@
|
||||
export * from "./types";
|
||||
export * from "./CanvasLayerItem";
|
||||
export * from "./CanvasQrItem";
|
||||
export * from "./InstagramTextEditorModal";
|
||||
export * from "./QrCanvasStudio";
|
||||
@@ -0,0 +1,298 @@
|
||||
export interface CanvasTextLayer {
|
||||
id: string;
|
||||
text: string;
|
||||
x: number; // px from canvas left
|
||||
y: number; // px from canvas top
|
||||
rotation: number; // degrees (-180 to 180)
|
||||
fontSize: number;
|
||||
fontId: string;
|
||||
color: string;
|
||||
bgBoxColor?: string; // Instagram badge kutusu
|
||||
bgStyle?: "none" | "solid" | "soft" | "outline";
|
||||
textAlign?: "left" | "center" | "right";
|
||||
hasShadow?: boolean;
|
||||
letterSpacing?: number;
|
||||
fontWeight?: "normal" | "600" | "700" | "800" | "900";
|
||||
}
|
||||
|
||||
export interface CanvasQrConfig {
|
||||
x: number;
|
||||
y: number;
|
||||
size: number;
|
||||
borderRadius: number;
|
||||
borderWidth: number;
|
||||
borderColor: string;
|
||||
padding: number;
|
||||
bgBoxColor: string;
|
||||
}
|
||||
|
||||
export const CANVAS_FONT_OPTIONS = [
|
||||
{
|
||||
id: "sans",
|
||||
label: "MODERN",
|
||||
fontFamily: "System, -apple-system, Roboto, 'Helvetica Neue', Arial, sans-serif",
|
||||
},
|
||||
{
|
||||
id: "serif",
|
||||
label: "ZARİF",
|
||||
fontFamily: "Georgia, 'Times New Roman', serif",
|
||||
},
|
||||
{
|
||||
id: "times",
|
||||
label: "KLASİK",
|
||||
fontFamily: "'Times New Roman', Times, Georgia, serif",
|
||||
},
|
||||
{
|
||||
id: "typewriter",
|
||||
label: "DAKTİLO",
|
||||
fontFamily: "Courier, 'Courier New', monospace",
|
||||
},
|
||||
{
|
||||
id: "palatino",
|
||||
label: "LÜKS",
|
||||
fontFamily: "Palatino, 'Palatino Linotype', Georgia, serif",
|
||||
},
|
||||
{
|
||||
id: "trebuchet",
|
||||
label: "DİNAMİK",
|
||||
fontFamily: "'Trebuchet MS', 'Segoe UI', Arial, sans-serif",
|
||||
},
|
||||
{
|
||||
id: "heavy",
|
||||
label: "GÜÇLÜ",
|
||||
fontFamily: "Impact, 'Arial Black', sans-serif",
|
||||
},
|
||||
];
|
||||
|
||||
export const CANVAS_COLOR_PALETTE = [
|
||||
{ id: "gold", color: "#D4AF37", label: "Altın" },
|
||||
{ id: "white", color: "#FFFFFF", label: "Beyaz" },
|
||||
{ id: "black", color: "#121215", label: "Siyah" },
|
||||
{ id: "cream", color: "#F6EFE3", label: "Krem" },
|
||||
{ id: "emerald", color: "#10B981", label: "Yeşil" },
|
||||
{ id: "crimson", color: "#EF4444", label: "Kırmızı" },
|
||||
{ id: "amber", color: "#F59E0B", label: "Amber" },
|
||||
{ id: "rose", color: "#EC4899", label: "Pembe" },
|
||||
{ id: "indigo", color: "#6366F1", label: "Lila" },
|
||||
{ id: "cyan", color: "#06B6D4", label: "Turkuaz" },
|
||||
];
|
||||
|
||||
export const INSTAGRAM_BADGE_STYLES = [
|
||||
{ id: "none", label: "Düz", bgBoxColor: undefined },
|
||||
{ id: "dark", label: "Koyu", bgBoxColor: "rgba(18, 18, 21, 0.88)" },
|
||||
{ id: "gold", label: "Altın", bgBoxColor: "rgba(212, 175, 55, 0.92)" },
|
||||
{ id: "white", label: "Beyaz", bgBoxColor: "rgba(255, 255, 255, 0.92)" },
|
||||
{ id: "glass", label: "Cam", bgBoxColor: "rgba(255, 255, 255, 0.2)" },
|
||||
];
|
||||
|
||||
export const QR_SIZE_PRESETS = [
|
||||
{ label: "Kompakt", size: 85 },
|
||||
{ label: "Standart", size: 115 },
|
||||
{ label: "Büyük", size: 145 },
|
||||
{ label: "Geniş", size: 175 },
|
||||
];
|
||||
|
||||
export const QR_RADIUS_PRESETS = [
|
||||
{ label: "Düz", radius: 0 },
|
||||
{ label: "Hafif", radius: 10 },
|
||||
{ label: "Yuvarlak", radius: 18 },
|
||||
{ label: "Oval", radius: 28 },
|
||||
];
|
||||
|
||||
export const QR_BORDER_COLORS = [
|
||||
{ id: "gold", color: "#D4AF37", label: "Altın" },
|
||||
{ id: "black", color: "#121215", label: "Siyah" },
|
||||
{ id: "white", color: "#FFFFFF", label: "Beyaz" },
|
||||
{ id: "none", color: "transparent", label: "Yok" },
|
||||
];
|
||||
|
||||
export function getCanvasFontFamily(fontId?: string): string {
|
||||
const found = CANVAS_FONT_OPTIONS.find((f) => f.id === fontId);
|
||||
return found ? found.fontFamily : "System, -apple-system, Roboto, sans-serif";
|
||||
}
|
||||
|
||||
export function getDefaultQrConfigForTemplate(
|
||||
templateKey: string,
|
||||
canvasWidth: number,
|
||||
canvasHeight: number
|
||||
): CanvasQrConfig {
|
||||
if (templateKey === "qr-1" || templateKey === "qr-8" || templateKey === "qr-9") {
|
||||
const size = 110;
|
||||
return {
|
||||
x: Math.round((canvasWidth - size) / 2),
|
||||
y: 102,
|
||||
size,
|
||||
borderRadius: 14,
|
||||
borderWidth: 1.5,
|
||||
borderColor: "#D4AF37",
|
||||
padding: 6,
|
||||
bgBoxColor: "#FFFFFF",
|
||||
};
|
||||
}
|
||||
|
||||
// Standart Dikey Şablonlar (qr-2, qr-3, qr-4, qr-5, qr-6)
|
||||
const size = 125;
|
||||
return {
|
||||
x: Math.round((canvasWidth - size) / 2),
|
||||
y: Math.round(canvasHeight * 0.36),
|
||||
size,
|
||||
borderRadius: 14,
|
||||
borderWidth: 1.5,
|
||||
borderColor: "#D4AF37",
|
||||
padding: 6,
|
||||
bgBoxColor: "#FFFFFF",
|
||||
};
|
||||
}
|
||||
|
||||
// Her şablon için başlangıç varsayılan katmanları (1:1 veya standart orana göre)
|
||||
export function getDefaultLayersForTemplate(templateKey: string, restaurantName = "Gourmet Bistro"): CanvasTextLayer[] {
|
||||
if (templateKey === "qr-1") {
|
||||
return [
|
||||
{
|
||||
id: "rName",
|
||||
text: restaurantName.toUpperCase(),
|
||||
x: 40,
|
||||
y: 50,
|
||||
rotation: 0,
|
||||
fontSize: 18,
|
||||
fontId: "serif",
|
||||
color: "#D4AF37",
|
||||
fontWeight: "800",
|
||||
letterSpacing: 2,
|
||||
textAlign: "center",
|
||||
},
|
||||
{
|
||||
id: "sub",
|
||||
text: "RESTAURANT & BAR",
|
||||
x: 65,
|
||||
y: 78,
|
||||
rotation: 0,
|
||||
fontSize: 9,
|
||||
fontId: "sans",
|
||||
color: "#E5C378",
|
||||
fontWeight: "600",
|
||||
letterSpacing: 1.5,
|
||||
textAlign: "center",
|
||||
},
|
||||
{
|
||||
id: "cta",
|
||||
text: "MENÜYÜ GÖRMEK İÇİN OKUTUN",
|
||||
x: 35,
|
||||
y: 236,
|
||||
rotation: 0,
|
||||
fontSize: 10,
|
||||
fontId: "serif",
|
||||
color: "#D4AF37",
|
||||
fontWeight: "700",
|
||||
letterSpacing: 1,
|
||||
textAlign: "center",
|
||||
},
|
||||
{
|
||||
id: "table",
|
||||
text: "MASA NO: 01 • WiFi: Bistro",
|
||||
x: 45,
|
||||
y: 265,
|
||||
rotation: 0,
|
||||
fontSize: 9,
|
||||
fontId: "sans",
|
||||
color: "#C5A059",
|
||||
fontWeight: "600",
|
||||
textAlign: "center",
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
if (templateKey === "qr-8") {
|
||||
return [
|
||||
{
|
||||
id: "rName",
|
||||
text: "EXPLORE OUR MENU",
|
||||
x: 35,
|
||||
y: 35,
|
||||
rotation: 0,
|
||||
fontSize: 16,
|
||||
fontId: "sans",
|
||||
color: "#FFFFFF",
|
||||
fontWeight: "900",
|
||||
letterSpacing: 1,
|
||||
textAlign: "center",
|
||||
},
|
||||
{
|
||||
id: "sub",
|
||||
text: "THE DAILY SPECIALS",
|
||||
x: 50,
|
||||
y: 68,
|
||||
rotation: -4,
|
||||
fontSize: 13,
|
||||
fontId: "sans",
|
||||
color: "#D1A752",
|
||||
fontWeight: "800",
|
||||
textAlign: "center",
|
||||
},
|
||||
{
|
||||
id: "cta",
|
||||
text: "SCAN TO VIEW\nFULL MENU & SPECIALS",
|
||||
x: 55,
|
||||
y: 215,
|
||||
rotation: 0,
|
||||
fontSize: 9.5,
|
||||
fontId: "sans",
|
||||
color: "#FFFFFF",
|
||||
fontWeight: "800",
|
||||
textAlign: "center",
|
||||
},
|
||||
{
|
||||
id: "wifi",
|
||||
text: "SCAN TO ORDER & BROWSE",
|
||||
x: 55,
|
||||
y: 258,
|
||||
rotation: 0,
|
||||
fontSize: 9,
|
||||
fontId: "sans",
|
||||
color: "#D1A752",
|
||||
fontWeight: "800",
|
||||
textAlign: "center",
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
// Varsayılan
|
||||
return [
|
||||
{
|
||||
id: "rName",
|
||||
text: restaurantName,
|
||||
x: 50,
|
||||
y: 45,
|
||||
rotation: 0,
|
||||
fontSize: 17,
|
||||
fontId: "serif",
|
||||
color: "#D4AF37",
|
||||
fontWeight: "800",
|
||||
textAlign: "center",
|
||||
},
|
||||
{
|
||||
id: "sub",
|
||||
text: "SCAN TO ORDER",
|
||||
x: 75,
|
||||
y: 75,
|
||||
rotation: 0,
|
||||
fontSize: 10,
|
||||
fontId: "sans",
|
||||
color: "#FFFFFF",
|
||||
fontWeight: "700",
|
||||
textAlign: "center",
|
||||
},
|
||||
{
|
||||
id: "cta",
|
||||
text: "DİJİTAL MENÜ",
|
||||
x: 75,
|
||||
y: 245,
|
||||
rotation: 0,
|
||||
fontSize: 11,
|
||||
fontId: "serif",
|
||||
color: "#D4AF37",
|
||||
fontWeight: "800",
|
||||
textAlign: "center",
|
||||
},
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,316 @@
|
||||
import * as React from "react";
|
||||
import Svg, {
|
||||
Path,
|
||||
Rect,
|
||||
Circle,
|
||||
G,
|
||||
Ellipse,
|
||||
Text,
|
||||
Image,
|
||||
} from "react-native-svg";
|
||||
import { type QrTemplateProps, getFontFamily, renderSvgLines } from "./common";
|
||||
|
||||
const QrTemplate2 = ({
|
||||
qrBase64,
|
||||
fields,
|
||||
hideQr,
|
||||
...props
|
||||
}: QrTemplateProps) => {
|
||||
const rName = fields?.restaurantName;
|
||||
const sub = fields?.subtitle;
|
||||
const table = fields?.tableNo;
|
||||
const cta = fields?.ctaText;
|
||||
const wifi = fields?.wifiText;
|
||||
const social = fields?.socialText;
|
||||
|
||||
const rNameFont = getFontFamily(rName?.fontId, "Georgia, 'Times New Roman', serif");
|
||||
const subFont = getFontFamily(sub?.fontId, "System, -apple-system, Roboto, sans-serif");
|
||||
const ctaFont = getFontFamily(cta?.fontId, "Georgia, 'Times New Roman', serif");
|
||||
const infoFont = getFontFamily(wifi?.fontId, "System, -apple-system, Roboto, sans-serif");
|
||||
|
||||
return (
|
||||
<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 */}
|
||||
<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 */}
|
||||
{!hideQr && (
|
||||
<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="System, sans-serif"
|
||||
fontSize={11}
|
||||
fontWeight={700}
|
||||
letterSpacing={1}
|
||||
textAnchor="middle"
|
||||
>
|
||||
{"QR KOD ALANI"}
|
||||
</Text>
|
||||
</>
|
||||
)}
|
||||
</G>
|
||||
)}
|
||||
|
||||
{/* Metinler (Çok satırlı destekli) */}
|
||||
<G textAnchor="middle">
|
||||
<Text
|
||||
x={200}
|
||||
y={68}
|
||||
fill="#3a2312"
|
||||
fontFamily={rNameFont}
|
||||
fontSize={14}
|
||||
fontStyle="italic"
|
||||
fontWeight={700}
|
||||
letterSpacing={4}
|
||||
>
|
||||
{"THE"}
|
||||
</Text>
|
||||
|
||||
{/* Restoran Adı */}
|
||||
{rName?.visible !== false &&
|
||||
renderSvgLines({
|
||||
text: rName?.text || "The Daily Grind",
|
||||
x: 200,
|
||||
y: 110,
|
||||
offsetY: rName?.offsetY,
|
||||
fill: "#2c1a0e",
|
||||
fontFamily: rNameFont,
|
||||
fontSize: rName?.fontSize || 28,
|
||||
fontWeight: "bold",
|
||||
letterSpacing: 0.5,
|
||||
})}
|
||||
|
||||
{/* Slogan */}
|
||||
{sub?.visible !== false &&
|
||||
renderSvgLines({
|
||||
text: sub?.text || "COFFEE & KITCHEN",
|
||||
x: 200,
|
||||
y: 144,
|
||||
offsetY: sub?.offsetY,
|
||||
fill: "#3a2312",
|
||||
fontFamily: subFont,
|
||||
fontSize: sub?.fontSize || 12,
|
||||
fontWeight: 700,
|
||||
letterSpacing: 2.5,
|
||||
})}
|
||||
|
||||
{/* CTA */}
|
||||
{cta?.visible !== false &&
|
||||
renderSvgLines({
|
||||
text: cta?.text || "MENÜYÜ GÖRMEK İÇİN OKUTUN",
|
||||
x: 200,
|
||||
y: 486,
|
||||
offsetY: cta?.offsetY,
|
||||
fill: "#2c1a0e",
|
||||
fontFamily: ctaFont,
|
||||
fontSize: cta?.fontSize || 15,
|
||||
fontWeight: "bold",
|
||||
letterSpacing: 1.2,
|
||||
})}
|
||||
|
||||
{/* WiFi */}
|
||||
{wifi?.visible !== false &&
|
||||
renderSvgLines({
|
||||
text: wifi?.text || "WiFi: DailyGrind | Şifre: coffee2026",
|
||||
x: 200,
|
||||
y: 520,
|
||||
offsetY: wifi?.offsetY,
|
||||
fill: "#3a2312",
|
||||
fontFamily: infoFont,
|
||||
fontSize: wifi?.fontSize || 10.5,
|
||||
fontWeight: 700,
|
||||
letterSpacing: 0.5,
|
||||
})}
|
||||
|
||||
{/* Masa No & Sosyal */}
|
||||
{(table?.visible !== false || social?.visible !== false) &&
|
||||
renderSvgLines({
|
||||
text: [
|
||||
table?.visible !== false && table?.text ? table.text : "",
|
||||
social?.visible !== false && social?.text ? social.text : "",
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(" • "),
|
||||
x: 200,
|
||||
y: 544,
|
||||
offsetY: table?.offsetY || social?.offsetY,
|
||||
fill: "#8c6239",
|
||||
fontFamily: infoFont,
|
||||
fontSize: table?.fontSize || social?.fontSize || 10,
|
||||
fontWeight: 600,
|
||||
letterSpacing: 1,
|
||||
})}
|
||||
|
||||
|
||||
<Text
|
||||
x={200}
|
||||
y={611}
|
||||
fill="#3a2312"
|
||||
fontFamily={rNameFont}
|
||||
fontSize={11}
|
||||
fontStyle="italic"
|
||||
fontWeight={700}
|
||||
letterSpacing={2}
|
||||
>
|
||||
{"EST. 2026"}
|
||||
</Text>
|
||||
</G>
|
||||
</Svg>
|
||||
);
|
||||
};
|
||||
|
||||
export default QrTemplate2;
|
||||
@@ -0,0 +1,357 @@
|
||||
import * as React from "react";
|
||||
import Svg, {
|
||||
Defs,
|
||||
LinearGradient,
|
||||
Stop,
|
||||
Pattern,
|
||||
Path,
|
||||
G,
|
||||
Rect,
|
||||
Ellipse,
|
||||
Circle,
|
||||
Text,
|
||||
Image,
|
||||
} from "react-native-svg";
|
||||
import { type QrTemplateProps, getFontFamily, renderSvgLines } from "./common";
|
||||
|
||||
const QrTemplate3 = ({
|
||||
qrBase64,
|
||||
fields,
|
||||
hideQr,
|
||||
...props
|
||||
}: QrTemplateProps) => {
|
||||
const rName = fields?.restaurantName;
|
||||
const sub = fields?.subtitle;
|
||||
const table = fields?.tableNo;
|
||||
const cta = fields?.ctaText;
|
||||
const wifi = fields?.wifiText;
|
||||
const social = fields?.socialText;
|
||||
|
||||
const rNameFont = getFontFamily(rName?.fontId, "Georgia, 'Playfair Display', serif");
|
||||
const subFont = getFontFamily(sub?.fontId, "System, -apple-system, Roboto, sans-serif");
|
||||
const ctaFont = getFontFamily(cta?.fontId, "Georgia, 'Playfair Display', serif");
|
||||
const infoFont = getFontFamily(wifi?.fontId, "System, -apple-system, Roboto, sans-serif");
|
||||
|
||||
return (
|
||||
<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}
|
||||
/>
|
||||
{table?.visible !== false && table?.text ? (
|
||||
<Rect
|
||||
width={140}
|
||||
height={24}
|
||||
x={130}
|
||||
y={34}
|
||||
fill="#faf4e6"
|
||||
stroke="#8c6239"
|
||||
strokeWidth={1.2}
|
||||
rx={12}
|
||||
/>
|
||||
) : null}
|
||||
</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 */}
|
||||
{!hideQr && (
|
||||
<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="System, sans-serif"
|
||||
fontSize={12}
|
||||
fontWeight={700}
|
||||
textAnchor="middle"
|
||||
>
|
||||
{"QR KOD ALANI"}
|
||||
</Text>
|
||||
</>
|
||||
)}
|
||||
</G>
|
||||
)}
|
||||
|
||||
{/* Metinler (Çok satırlı destekli) */}
|
||||
<G id="layer-texts">
|
||||
{/* Masa No */}
|
||||
{table?.visible !== false &&
|
||||
renderSvgLines({
|
||||
text: table?.text || "MASA NO: 01",
|
||||
x: 200,
|
||||
y: 50,
|
||||
offsetY: table?.offsetY,
|
||||
fill: "#18181b",
|
||||
fontFamily: infoFont,
|
||||
fontSize: table?.fontSize || 11,
|
||||
fontWeight: "bold",
|
||||
letterSpacing: 1.2,
|
||||
})}
|
||||
|
||||
<Text
|
||||
x={200}
|
||||
y={96}
|
||||
fill="#3e2723"
|
||||
fontFamily={rNameFont}
|
||||
fontSize={24}
|
||||
textAnchor="middle"
|
||||
>
|
||||
{"PIZZERIA"}
|
||||
</Text>
|
||||
|
||||
{/* Restoran Adı */}
|
||||
{rName?.visible !== false &&
|
||||
renderSvgLines({
|
||||
text: rName?.text || "BELLA NAPOLI",
|
||||
x: 200,
|
||||
y: 130,
|
||||
offsetY: rName?.offsetY,
|
||||
fill: "#18181b",
|
||||
fontFamily: rNameFont,
|
||||
fontSize: rName?.fontSize || 26,
|
||||
fontWeight: "bold",
|
||||
letterSpacing: 1,
|
||||
})}
|
||||
|
||||
{/* Slogan (Enter ile çok satır olabilir) */}
|
||||
{sub?.visible !== false &&
|
||||
renderSvgLines({
|
||||
text: sub?.text || "AUTENTICA CUCINA ITALIANA",
|
||||
x: 200,
|
||||
y: 222,
|
||||
offsetY: sub?.offsetY,
|
||||
fill: "#2e7d32",
|
||||
fontFamily: subFont,
|
||||
fontSize: sub?.fontSize || 11.5,
|
||||
fontWeight: "bold",
|
||||
letterSpacing: 1.8,
|
||||
})}
|
||||
|
||||
{/* CTA */}
|
||||
{cta?.visible !== false &&
|
||||
renderSvgLines({
|
||||
text: cta?.text || "MENÜYÜ GÖRMEK İÇİN OKUTUN",
|
||||
x: 200,
|
||||
y: 474,
|
||||
offsetY: cta?.offsetY,
|
||||
fill: "#b92b27",
|
||||
fontFamily: ctaFont,
|
||||
fontSize: cta?.fontSize || 13,
|
||||
fontWeight: "bold",
|
||||
letterSpacing: 1.2,
|
||||
})}
|
||||
|
||||
{/* WiFi */}
|
||||
{wifi?.visible !== false &&
|
||||
renderSvgLines({
|
||||
text: wifi?.text || "WiFi: GourmetBistro | Şifre: lezzetli01",
|
||||
x: 200,
|
||||
y: 546,
|
||||
offsetY: wifi?.offsetY,
|
||||
fill: "#3e2723",
|
||||
fontFamily: infoFont,
|
||||
fontSize: wifi?.fontSize || 10.5,
|
||||
fontWeight: "bold",
|
||||
})}
|
||||
|
||||
{/* Sosyal Medya */}
|
||||
{social?.visible !== false &&
|
||||
renderSvgLines({
|
||||
text: social?.text || "BİZİ TAKİP EDİN | @gourmetbistro",
|
||||
x: 200,
|
||||
y: 570,
|
||||
offsetY: social?.offsetY,
|
||||
fill: "#b92b27",
|
||||
fontFamily: infoFont,
|
||||
fontSize: social?.fontSize || 10.5,
|
||||
fontWeight: "bold",
|
||||
letterSpacing: 0.5,
|
||||
})}
|
||||
|
||||
</G>
|
||||
</Svg>
|
||||
);
|
||||
};
|
||||
|
||||
export default QrTemplate3;
|
||||
@@ -0,0 +1,280 @@
|
||||
import * as React from "react";
|
||||
import Svg, {
|
||||
Defs,
|
||||
G,
|
||||
Path,
|
||||
Circle,
|
||||
Use,
|
||||
Rect,
|
||||
Text,
|
||||
Image,
|
||||
} from "react-native-svg";
|
||||
import { type QrTemplateProps, getFontFamily, renderSvgLines } from "./common";
|
||||
|
||||
const QrTemplate4 = ({
|
||||
qrBase64,
|
||||
fields,
|
||||
hideQr,
|
||||
...props
|
||||
}: QrTemplateProps) => {
|
||||
const rName = fields?.restaurantName;
|
||||
const sub = fields?.subtitle;
|
||||
const table = fields?.tableNo;
|
||||
const cta = fields?.ctaText;
|
||||
const wifi = fields?.wifiText;
|
||||
const social = fields?.socialText;
|
||||
|
||||
const rNameFont = getFontFamily(rName?.fontId, "Georgia, 'Times New Roman', serif");
|
||||
const subFont = getFontFamily(sub?.fontId, "System, -apple-system, Roboto, sans-serif");
|
||||
const ctaFont = getFontFamily(cta?.fontId, "Georgia, 'Times New Roman', serif");
|
||||
const infoFont = getFontFamily(wifi?.fontId, "System, -apple-system, Roboto, sans-serif");
|
||||
|
||||
return (
|
||||
<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 / Gerçek QR */}
|
||||
{!hideQr && (
|
||||
<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="System, sans-serif"
|
||||
fontSize={10}
|
||||
fontWeight={700}
|
||||
letterSpacing={1}
|
||||
textAnchor="middle"
|
||||
>
|
||||
{"QR KOD ALANI"}
|
||||
</Text>
|
||||
</>
|
||||
)}
|
||||
</G>
|
||||
)}
|
||||
|
||||
{/* Metinler */}
|
||||
<G fill="#111" textAnchor="middle">
|
||||
{/* Masa No */}
|
||||
{table?.visible !== false &&
|
||||
renderSvgLines({
|
||||
text: table?.text || "MASA NO: 01",
|
||||
x: 200,
|
||||
y: 56,
|
||||
offsetY: table?.offsetY,
|
||||
fill: "#c59b4b",
|
||||
fontFamily: infoFont,
|
||||
fontSize: table?.fontSize || 11,
|
||||
fontWeight: "bold",
|
||||
letterSpacing: 1.5,
|
||||
})}
|
||||
|
||||
{/* Restoran Başlığı (Enter ile alt satıra geçebilir) */}
|
||||
{rName?.visible !== false &&
|
||||
renderSvgLines({
|
||||
text: rName?.text || "THE CRIMSON PETAL",
|
||||
x: 200,
|
||||
y: 114,
|
||||
offsetY: rName?.offsetY,
|
||||
fill: "#111111",
|
||||
fontFamily: rNameFont,
|
||||
fontSize: rName?.fontSize || 24,
|
||||
fontWeight: "bold",
|
||||
letterSpacing: 1.5,
|
||||
})}
|
||||
|
||||
{/* Slogan */}
|
||||
{sub?.visible !== false &&
|
||||
renderSvgLines({
|
||||
text: sub?.text || "FINE DINING EXPERIENCE",
|
||||
x: 200,
|
||||
y: 154,
|
||||
offsetY: sub?.offsetY,
|
||||
fill: "#71717a",
|
||||
fontFamily: subFont,
|
||||
fontSize: sub?.fontSize || 11,
|
||||
fontWeight: 600,
|
||||
letterSpacing: 1.8,
|
||||
})}
|
||||
|
||||
{/* CTA Metni */}
|
||||
{cta?.visible !== false &&
|
||||
renderSvgLines({
|
||||
text: cta?.text || "MENÜYÜ GÖRMEK İÇİN OKUTUN",
|
||||
x: 200,
|
||||
y: 440,
|
||||
offsetY: cta?.offsetY,
|
||||
fill: "#111111",
|
||||
fontFamily: ctaFont,
|
||||
fontSize: cta?.fontSize || 15,
|
||||
fontWeight: "bold",
|
||||
letterSpacing: 1,
|
||||
})}
|
||||
|
||||
{/* WiFi Bilgisi */}
|
||||
{wifi?.visible !== false &&
|
||||
renderSvgLines({
|
||||
text: wifi?.text || "WiFi: CrimsonGuest | Şifre: petal2026",
|
||||
x: 200,
|
||||
y: 488,
|
||||
offsetY: wifi?.offsetY,
|
||||
fill: "#71717a",
|
||||
fontFamily: infoFont,
|
||||
fontSize: wifi?.fontSize || 10.5,
|
||||
fontWeight: 600,
|
||||
letterSpacing: 0.5,
|
||||
})}
|
||||
|
||||
{/* Sosyal Medya */}
|
||||
{social?.visible !== false &&
|
||||
renderSvgLines({
|
||||
text: social?.text || "@thecrimsonpetal",
|
||||
x: 200,
|
||||
y: 514,
|
||||
offsetY: social?.offsetY,
|
||||
fill: "#c59b4b",
|
||||
fontFamily: infoFont,
|
||||
fontSize: social?.fontSize || 10.5,
|
||||
fontWeight: 600,
|
||||
letterSpacing: 0.5,
|
||||
})}
|
||||
</G>
|
||||
|
||||
</Svg>
|
||||
);
|
||||
};
|
||||
|
||||
export default QrTemplate4;
|
||||
@@ -0,0 +1,188 @@
|
||||
import * as React from "react";
|
||||
import Svg, {
|
||||
Image,
|
||||
G,
|
||||
Rect,
|
||||
Path,
|
||||
Text,
|
||||
} from "react-native-svg";
|
||||
import { type QrTemplateProps, getFontFamily, renderSvgLines } from "./common";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
||||
const bgImage = require("../../../assets/templates/5.png");
|
||||
|
||||
const QrTemplate5 = ({
|
||||
qrBase64,
|
||||
fields,
|
||||
hideQr,
|
||||
...props
|
||||
}: QrTemplateProps) => {
|
||||
const rName = fields?.restaurantName;
|
||||
const sub = fields?.subtitle;
|
||||
const table = fields?.tableNo;
|
||||
const cta = fields?.ctaText;
|
||||
const wifi = fields?.wifiText;
|
||||
const social = fields?.socialText;
|
||||
|
||||
const rNameFont = getFontFamily(rName?.fontId, "System, -apple-system, Roboto, sans-serif");
|
||||
const subFont = getFontFamily(sub?.fontId, "System, -apple-system, Roboto, sans-serif");
|
||||
const ctaFont = getFontFamily(cta?.fontId, "System, -apple-system, Roboto, sans-serif");
|
||||
const infoFont = getFontFamily(wifi?.fontId, "System, -apple-system, Roboto, sans-serif");
|
||||
|
||||
return (
|
||||
<Svg width={300} height={394.7} viewBox="0 0 380 500" {...props}>
|
||||
{/* PNG Arka Plan */}
|
||||
<Image
|
||||
width={380}
|
||||
height={500}
|
||||
href={bgImage}
|
||||
preserveAspectRatio="none"
|
||||
/>
|
||||
|
||||
{/* QR Placeholder / Live QR */}
|
||||
{!hideQr && (
|
||||
<G transform="translate(108 206)">
|
||||
<Rect
|
||||
width={164}
|
||||
height={164}
|
||||
fill="#fff"
|
||||
stroke="#dce5ea"
|
||||
strokeWidth={1.2}
|
||||
rx={16}
|
||||
/>
|
||||
<Path
|
||||
fill="none"
|
||||
stroke="#003554"
|
||||
strokeLinecap="round"
|
||||
strokeWidth={3}
|
||||
d="M6 30V12a6 6 0 0 1 6-6h18m128 24V12a6 6 0 0 0-6-6h-18M6 134v18a6 6 0 0 0 6 6h18m128-24v18a6 6 0 0 1-6 6h-18"
|
||||
/>
|
||||
{qrBase64 ? (
|
||||
<Image
|
||||
href={`data:image/png;base64,${qrBase64}`}
|
||||
x={12}
|
||||
y={12}
|
||||
width={140}
|
||||
height={140}
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
<Rect
|
||||
width={140}
|
||||
height={140}
|
||||
x={12}
|
||||
y={12}
|
||||
fill="#fafcfd"
|
||||
stroke="#003554"
|
||||
strokeDasharray="4 3"
|
||||
strokeOpacity={0.2}
|
||||
rx={10}
|
||||
/>
|
||||
<Text
|
||||
x={82}
|
||||
y={87}
|
||||
fill="#003554"
|
||||
fontFamily="System, sans-serif"
|
||||
fontSize={11}
|
||||
fontWeight={700}
|
||||
letterSpacing={1}
|
||||
textAnchor="middle"
|
||||
>
|
||||
{"QR KOD ALANI"}
|
||||
</Text>
|
||||
</>
|
||||
)}
|
||||
</G>
|
||||
)}
|
||||
|
||||
{/* Metinler */}
|
||||
<G fill="#003554" textAnchor="middle">
|
||||
{/* Masa Numarası */}
|
||||
{table?.visible !== false &&
|
||||
renderSvgLines({
|
||||
text: table?.text || "MASA NO: 01",
|
||||
x: 190,
|
||||
y: 40,
|
||||
offsetY: table?.offsetY,
|
||||
fill: "#003554",
|
||||
fontFamily: infoFont,
|
||||
fontSize: table?.fontSize || 11,
|
||||
fontWeight: "bold",
|
||||
letterSpacing: 1.5,
|
||||
})}
|
||||
|
||||
{/* Restoran Başlığı */}
|
||||
{rName?.visible !== false &&
|
||||
renderSvgLines({
|
||||
text: rName?.text || "THE URBAN FORK",
|
||||
x: 190,
|
||||
y: 86,
|
||||
offsetY: rName?.offsetY,
|
||||
fill: "#003554",
|
||||
fontFamily: rNameFont,
|
||||
fontSize: rName?.fontSize || 24,
|
||||
fontWeight: "bold",
|
||||
letterSpacing: 1,
|
||||
})}
|
||||
|
||||
{/* Yönlendirme (CTA) */}
|
||||
{cta?.visible !== false &&
|
||||
renderSvgLines({
|
||||
text: cta?.text || "DİJİTAL MENÜYÜ GÖRÜNTÜLE",
|
||||
x: 190,
|
||||
y: 160,
|
||||
offsetY: cta?.offsetY,
|
||||
fill: "#003554",
|
||||
fontFamily: ctaFont,
|
||||
fontSize: cta?.fontSize || 13,
|
||||
fontWeight: "bold",
|
||||
letterSpacing: 0.5,
|
||||
})}
|
||||
|
||||
{/* Slogan (Enter ile çok satır olabilir) */}
|
||||
{sub?.visible !== false &&
|
||||
renderSvgLines({
|
||||
text: sub?.text || "FRESH FLAVORS DAILY",
|
||||
x: 190,
|
||||
y: 402,
|
||||
offsetY: sub?.offsetY,
|
||||
fill: "#003554",
|
||||
fontFamily: subFont,
|
||||
fontSize: sub?.fontSize || 13,
|
||||
fontWeight: "bold",
|
||||
letterSpacing: 0.8,
|
||||
})}
|
||||
|
||||
{/* WiFi */}
|
||||
{wifi?.visible !== false &&
|
||||
renderSvgLines({
|
||||
text: wifi?.text || "WiFi: UrbanFork | Şifre: urban2026",
|
||||
x: 190,
|
||||
y: 430,
|
||||
offsetY: wifi?.offsetY,
|
||||
fill: "#003554",
|
||||
fontFamily: infoFont,
|
||||
fontSize: wifi?.fontSize || 10,
|
||||
fontWeight: 700,
|
||||
letterSpacing: 0.2,
|
||||
})}
|
||||
|
||||
{/* Sosyal Medya */}
|
||||
{social?.visible !== false &&
|
||||
renderSvgLines({
|
||||
text: social?.text || "@theurbanfork",
|
||||
x: 190,
|
||||
y: 454,
|
||||
offsetY: social?.offsetY,
|
||||
fill: "#003554",
|
||||
fontFamily: infoFont,
|
||||
fontSize: social?.fontSize || 10,
|
||||
fontWeight: 700,
|
||||
letterSpacing: 0.2,
|
||||
})}
|
||||
</G>
|
||||
</Svg>
|
||||
);
|
||||
};
|
||||
|
||||
export default QrTemplate5;
|
||||
@@ -0,0 +1,263 @@
|
||||
import * as React from "react";
|
||||
import Svg, {
|
||||
Defs,
|
||||
G,
|
||||
Path,
|
||||
Circle,
|
||||
Rect,
|
||||
Use,
|
||||
Text,
|
||||
Image,
|
||||
} from "react-native-svg";
|
||||
import { type QrTemplateProps, getFontFamily, renderSvgLines } from "./common";
|
||||
|
||||
const QrTemplate6 = ({
|
||||
qrBase64,
|
||||
fields,
|
||||
hideQr,
|
||||
...props
|
||||
}: QrTemplateProps) => {
|
||||
const rName = fields?.restaurantName;
|
||||
const sub = fields?.subtitle;
|
||||
const table = fields?.tableNo;
|
||||
const cta = fields?.ctaText;
|
||||
const wifi = fields?.wifiText;
|
||||
const social = fields?.socialText;
|
||||
|
||||
const rNameFont = getFontFamily(rName?.fontId, "Georgia, 'Times New Roman', serif");
|
||||
const subFont = getFontFamily(sub?.fontId, "System, -apple-system, Roboto, sans-serif");
|
||||
const ctaFont = getFontFamily(cta?.fontId, "Georgia, 'Times New Roman', serif");
|
||||
const infoFont = getFontFamily(wifi?.fontId, "System, -apple-system, Roboto, sans-serif");
|
||||
|
||||
return (
|
||||
<Svg width={300} height={435} viewBox="0 0 400 580" {...props}>
|
||||
<Defs>
|
||||
<G id="b6">
|
||||
<Path
|
||||
fill="none"
|
||||
stroke="#c59b58"
|
||||
strokeLinecap="round"
|
||||
strokeWidth={1.2}
|
||||
d="M0 0c10 25 12 55 5 85"
|
||||
/>
|
||||
<Path
|
||||
fill="#f4efe6"
|
||||
stroke="#c59b58"
|
||||
strokeWidth={1.1}
|
||||
d="M3 15c-11-5-15 9-1 9 4 0 4-6 1-9Z"
|
||||
/>
|
||||
<Path
|
||||
fill="#f4efe6"
|
||||
stroke="#9e4d28"
|
||||
strokeWidth={1.1}
|
||||
d="M5 22c11-6 17 6 3 10-4 0-4-6-3-10Zm2 20c-12-6-17 8-3 10 4 0 4-6 3-10Z"
|
||||
/>
|
||||
<Path
|
||||
fill="#f4efe6"
|
||||
stroke="#c59b58"
|
||||
strokeWidth={1.1}
|
||||
d="M8 50c12-6 16 8 2 12-4 0-4-7-2-12ZM6 70c-10-5-14 8-2 10 3 0 4-6 2-10Z"
|
||||
/>
|
||||
<Path
|
||||
fill="#f4efe6"
|
||||
stroke="#9e4d28"
|
||||
strokeWidth={1.1}
|
||||
d="M5 78c11-4 15 8 3 12-4 0-4-6-3-12Z"
|
||||
/>
|
||||
<Circle cx={-10} cy={18} r={1.5} fill="#c59b58" />
|
||||
<Circle cx={18} cy={46} r={1.5} fill="#9e4d28" />
|
||||
</G>
|
||||
<G id="a6">
|
||||
<Path
|
||||
fill="#a64423"
|
||||
d="M0-18C4-12 8-6 8 0c6-4 8 6 4 12 4-2 6 4 2 8S2 26 0 26s-10-2-14-6-2-10 2-8c-4-6-2-16 4-12 0-6 4-12 8-18"
|
||||
/>
|
||||
<Path
|
||||
fill="#f4efe6"
|
||||
d="M-4 4v8c0 3 2 4 4 4v8c2-8 4-9 4-12V4H3v7c0 2-1.5 3-3 3s-3-1-3-3V4Z"
|
||||
/>
|
||||
<Path stroke="#f4efe6" d="M0 4v8" />
|
||||
</G>
|
||||
</Defs>
|
||||
<Path fill="#434b54" d="M0 0h400v580H0z" />
|
||||
<Rect
|
||||
width={360}
|
||||
height={548}
|
||||
x={20}
|
||||
y={16}
|
||||
fill="#f6efe3"
|
||||
stroke="#122a42"
|
||||
strokeWidth={1.5}
|
||||
rx={16}
|
||||
/>
|
||||
<Path
|
||||
fill="none"
|
||||
stroke="#122a42"
|
||||
strokeWidth={1.2}
|
||||
d="M46 32h308a12 12 0 0 1 12 12v492a12 12 0 0 1-12 12H46a12 12 0 0 1-12-12V44a12 12 0 0 1 12-12Z"
|
||||
/>
|
||||
<Path
|
||||
fill="none"
|
||||
stroke="#122a42"
|
||||
strokeWidth={1.2}
|
||||
d="M34 46a12 12 0 0 0 12-12m320 12a12 12 0 0 1-12-12M34 534a12 12 0 0 1 12 12m320-12a12 12 0 0 0-12 12"
|
||||
/>
|
||||
<Path stroke="#c59b58" d="M34 135h332M34 500h332" />
|
||||
<Use href="#a6" transform="matrix(.85 0 0 .85 200 52)" />
|
||||
<Use href="#b6" transform="matrix(.8 0 0 .8 42 42)" />
|
||||
<Use href="#b6" transform="matrix(-.8 0 0 .8 358 42)" />
|
||||
<Use href="#b6" transform="matrix(.95 0 0 .95 40 185)" />
|
||||
<Use href="#b6" transform="matrix(-.95 0 0 .95 360 185)" />
|
||||
<Use href="#a6" transform="matrix(.65 0 0 .65 68 320)" />
|
||||
<Use href="#a6" transform="matrix(.65 0 0 .65 332 320)" />
|
||||
<Use href="#b6" transform="matrix(.95 0 0 .95 40 360)" />
|
||||
<Use href="#b6" transform="matrix(-.95 0 0 .95 360 360)" />
|
||||
|
||||
{/* QR Placeholder / Gerçek QR */}
|
||||
{!hideQr && (
|
||||
<G transform="translate(95 205)">
|
||||
<Path
|
||||
fill="#fdfbf7"
|
||||
stroke="#122a42"
|
||||
strokeWidth={3}
|
||||
d="M0 0h210v210H0z"
|
||||
/>
|
||||
<Path
|
||||
fill="none"
|
||||
stroke="#c59b58"
|
||||
strokeWidth={1.5}
|
||||
d="M4 4h202v202H4z"
|
||||
/>
|
||||
{qrBase64 ? (
|
||||
<Image
|
||||
href={`data:image/png;base64,${qrBase64}`}
|
||||
x={10}
|
||||
y={10}
|
||||
width={190}
|
||||
height={190}
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
<Path
|
||||
fill="#faf8f5"
|
||||
stroke="#122a42"
|
||||
strokeDasharray="4 3"
|
||||
d="M12 12h186v186H12z"
|
||||
/>
|
||||
<Circle
|
||||
cx={105}
|
||||
cy={95}
|
||||
r={16}
|
||||
fill="#f6efe3"
|
||||
stroke="#c59b58"
|
||||
strokeWidth={1.2}
|
||||
/>
|
||||
<Use href="#a6" transform="matrix(.45 0 0 .45 105 93)" />
|
||||
<Text
|
||||
x={105}
|
||||
y={128}
|
||||
fill="#122a42"
|
||||
fontFamily="System, sans-serif"
|
||||
fontSize={11}
|
||||
fontWeight="bold"
|
||||
letterSpacing={1.5}
|
||||
textAnchor="middle"
|
||||
>
|
||||
{"QR KOD ALANI"}
|
||||
</Text>
|
||||
</>
|
||||
)}
|
||||
</G>
|
||||
)}
|
||||
|
||||
{/* Metinler */}
|
||||
<G textAnchor="middle">
|
||||
{/* Masa Numarası */}
|
||||
{table?.visible !== false &&
|
||||
renderSvgLines({
|
||||
text: table?.text || "MASA NO: 01",
|
||||
x: 200,
|
||||
y: 36,
|
||||
offsetY: table?.offsetY,
|
||||
fill: "#122a42",
|
||||
fontFamily: infoFont,
|
||||
fontSize: table?.fontSize || 10,
|
||||
fontWeight: "bold",
|
||||
letterSpacing: 1.5,
|
||||
})}
|
||||
|
||||
{/* Restoran Başlığı */}
|
||||
{rName?.visible !== false &&
|
||||
renderSvgLines({
|
||||
text: rName?.text || "THE STONEHEARTH GRILL",
|
||||
x: 200,
|
||||
y: 84,
|
||||
offsetY: rName?.offsetY,
|
||||
fill: "#a64423",
|
||||
fontFamily: rNameFont,
|
||||
fontSize: rName?.fontSize || 19,
|
||||
fontWeight: "bold",
|
||||
letterSpacing: 1.5,
|
||||
})}
|
||||
|
||||
{/* Slogan */}
|
||||
{sub?.visible !== false &&
|
||||
renderSvgLines({
|
||||
text: sub?.text || "SCAN. ORDER. ENJOY.",
|
||||
x: 200,
|
||||
y: 118,
|
||||
offsetY: sub?.offsetY,
|
||||
fill: "#122a42",
|
||||
fontFamily: subFont,
|
||||
fontSize: sub?.fontSize || 10,
|
||||
fontWeight: "bold",
|
||||
letterSpacing: 2,
|
||||
})}
|
||||
|
||||
{/* CTA */}
|
||||
{cta?.visible !== false &&
|
||||
renderSvgLines({
|
||||
text: cta?.text || "VIEW OUR MENU & ORDER ONLINE",
|
||||
x: 200,
|
||||
y: 160,
|
||||
offsetY: cta?.offsetY,
|
||||
fill: "#122a42",
|
||||
fontFamily: ctaFont,
|
||||
fontSize: cta?.fontSize || 13,
|
||||
fontWeight: "bold",
|
||||
letterSpacing: 0.5,
|
||||
})}
|
||||
|
||||
{/* WiFi */}
|
||||
{wifi?.visible !== false &&
|
||||
renderSvgLines({
|
||||
text: wifi?.text || "FREE WIFI: StonehearthGuest | PW: Grill720",
|
||||
x: 200,
|
||||
y: 524,
|
||||
offsetY: wifi?.offsetY,
|
||||
fill: "#222",
|
||||
fontFamily: infoFont,
|
||||
fontSize: wifi?.fontSize || 9,
|
||||
fontWeight: "bold",
|
||||
letterSpacing: 0.2,
|
||||
})}
|
||||
|
||||
{/* Sosyal Medya */}
|
||||
{social?.visible !== false &&
|
||||
renderSvgLines({
|
||||
text: social?.text || "Follow Us @StonehearthGrill",
|
||||
x: 200,
|
||||
y: 540,
|
||||
offsetY: social?.offsetY,
|
||||
fill: "#222",
|
||||
fontFamily: infoFont,
|
||||
fontSize: social?.fontSize || 9,
|
||||
fontWeight: "bold",
|
||||
letterSpacing: 0.2,
|
||||
})}
|
||||
</G>
|
||||
</Svg>
|
||||
);
|
||||
};
|
||||
|
||||
export default QrTemplate6;
|
||||
@@ -0,0 +1,212 @@
|
||||
import * as React from "react";
|
||||
import Svg, {
|
||||
G,
|
||||
Path,
|
||||
Circle,
|
||||
Rect,
|
||||
Text,
|
||||
Image,
|
||||
} from "react-native-svg";
|
||||
import { type QrTemplateProps, getFontFamily, renderSvgLines } from "./common";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const template8Bg = require("../../../assets/templates/8.jpeg");
|
||||
|
||||
const QrTemplate8 = ({
|
||||
qrBase64,
|
||||
fields,
|
||||
hideQr,
|
||||
...props
|
||||
}: QrTemplateProps) => {
|
||||
const rName = fields?.restaurantName;
|
||||
const sub = fields?.subtitle;
|
||||
const table = fields?.tableNo;
|
||||
const cta = fields?.ctaText;
|
||||
const wifi = fields?.wifiText;
|
||||
const social = fields?.socialText;
|
||||
|
||||
const rNameFont = getFontFamily(rName?.fontId, "System, -apple-system, Roboto, sans-serif");
|
||||
const subFont = getFontFamily(sub?.fontId, "System, -apple-system, Roboto, sans-serif");
|
||||
const ctaFont = getFontFamily(cta?.fontId, "System, -apple-system, Roboto, sans-serif");
|
||||
const infoFont = getFontFamily(wifi?.fontId, "System, -apple-system, Roboto, sans-serif");
|
||||
|
||||
return (
|
||||
<Svg width={300} height={300} viewBox="0 0 1024 1024" {...props}>
|
||||
<Path fill="#0c4443" d="M0 0h1024v1024H0z" />
|
||||
<Image
|
||||
width={1024}
|
||||
height={1024}
|
||||
href={template8Bg}
|
||||
preserveAspectRatio="none"
|
||||
/>
|
||||
|
||||
{/* QR Alanı (Merkez) */}
|
||||
{!hideQr && (
|
||||
<G transform="translate(348 305)">
|
||||
<Rect
|
||||
width={328}
|
||||
height={336}
|
||||
fill="#f9f5eb"
|
||||
stroke="#d1a752"
|
||||
strokeWidth={1.5}
|
||||
rx={16}
|
||||
/>
|
||||
<Rect
|
||||
width={304}
|
||||
height={312}
|
||||
x={12}
|
||||
y={12}
|
||||
fill="#faf8f2"
|
||||
stroke="#0c4443"
|
||||
strokeDasharray="6 4"
|
||||
strokeOpacity={0.3}
|
||||
strokeWidth={1.5}
|
||||
rx={12}
|
||||
/>
|
||||
{qrBase64 ? (
|
||||
<Image
|
||||
href={`data:image/png;base64,${qrBase64}`}
|
||||
x={18}
|
||||
y={18}
|
||||
width={292}
|
||||
height={300}
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
<Circle cx={164} cy={148} r={28} fill="#0c4443" />
|
||||
<G fill="#d1a752">
|
||||
<Path d="M164 132.6c4.4 6.6 4.4 15.4 0 24.2-4.4-8.8-4.4-17.6 0-24.2" />
|
||||
<Path d="M161.8 150.2c-8.8-4.4-13.2 4.4-6.6 8.8 6.6 0 8.8-6.6 6.6-8.8m4.4 0c8.8-4.4 13.2 4.4 6.6 8.8-6.6 0-8.8-6.6-6.6-8.8" />
|
||||
</G>
|
||||
<Text
|
||||
x={164}
|
||||
y={218}
|
||||
fill="#0c4443"
|
||||
fontFamily="System, -apple-system, Roboto, sans-serif"
|
||||
fontSize={14}
|
||||
fontWeight={800}
|
||||
letterSpacing={2}
|
||||
textAnchor="middle"
|
||||
>
|
||||
{"QR KOD ALANI"}
|
||||
</Text>
|
||||
</>
|
||||
)}
|
||||
</G>
|
||||
)}
|
||||
|
||||
{/* Üst Çizgiler & OR Ayracı */}
|
||||
<Path
|
||||
stroke="#d1a752"
|
||||
strokeLinecap="round"
|
||||
strokeWidth={2}
|
||||
d="M210 188h240"
|
||||
/>
|
||||
<Text
|
||||
x={512}
|
||||
y={194}
|
||||
fill="#d1a752"
|
||||
fontFamily="System, -apple-system, Roboto, sans-serif"
|
||||
fontSize={24}
|
||||
fontWeight={900}
|
||||
letterSpacing={2}
|
||||
textAnchor="middle"
|
||||
>
|
||||
{"OR"}
|
||||
</Text>
|
||||
<Path
|
||||
stroke="#d1a752"
|
||||
strokeLinecap="round"
|
||||
strokeWidth={2}
|
||||
d="M574 188h240"
|
||||
/>
|
||||
|
||||
{/* Masa Numarası (En Üst) */}
|
||||
{table?.visible !== false &&
|
||||
renderSvgLines({
|
||||
text: table?.text || "MASA NO: 01",
|
||||
x: 512,
|
||||
y: 70,
|
||||
offsetY: table?.offsetY,
|
||||
fill: "#d1a752",
|
||||
fontFamily: infoFont,
|
||||
fontSize: table?.fontSize ? table.fontSize * 1.8 : 22,
|
||||
fontWeight: 800,
|
||||
letterSpacing: 2,
|
||||
})}
|
||||
|
||||
{/* Restoran Başlığı / Üst Başlık */}
|
||||
{rName?.visible !== false &&
|
||||
renderSvgLines({
|
||||
text: rName?.text || "EXPLORE OUR MENU",
|
||||
x: 512,
|
||||
y: 136,
|
||||
offsetY: rName?.offsetY,
|
||||
fill: "#fff",
|
||||
fontFamily: rNameFont,
|
||||
fontSize: rName?.fontSize ? rName.fontSize * 1.8 : 50,
|
||||
fontWeight: 900,
|
||||
letterSpacing: 2,
|
||||
})}
|
||||
|
||||
{/* Slogan / Alt Başlık */}
|
||||
{sub?.visible !== false &&
|
||||
renderSvgLines({
|
||||
text: sub?.text || "THE DAILY SPECIALS",
|
||||
x: 512,
|
||||
y: 242,
|
||||
offsetY: sub?.offsetY,
|
||||
fill: "#d1a752",
|
||||
fontFamily: subFont,
|
||||
fontSize: sub?.fontSize ? sub.fontSize * 1.8 : 42,
|
||||
fontWeight: 900,
|
||||
letterSpacing: 2,
|
||||
})}
|
||||
|
||||
{/* CTA Metni */}
|
||||
{cta?.visible !== false &&
|
||||
renderSvgLines({
|
||||
text: cta?.text || "SCAN TO VIEW OUR\nFULL MENU & SPECIALS",
|
||||
x: 512,
|
||||
y: 730,
|
||||
offsetY: cta?.offsetY,
|
||||
fill: "#fff",
|
||||
fontFamily: ctaFont,
|
||||
fontSize: cta?.fontSize ? cta.fontSize * 1.8 : 28,
|
||||
fontWeight: 900,
|
||||
letterSpacing: 1,
|
||||
lineHeight: 38,
|
||||
})}
|
||||
|
||||
{/* WiFi / Sipariş Bilgisi */}
|
||||
{wifi?.visible !== false &&
|
||||
renderSvgLines({
|
||||
text: wifi?.text || "SCAN TO ORDER & BROWSE",
|
||||
x: 512,
|
||||
y: 846,
|
||||
offsetY: wifi?.offsetY,
|
||||
fill: "#d1a752",
|
||||
fontFamily: infoFont,
|
||||
fontSize: wifi?.fontSize ? wifi.fontSize * 1.8 : 22,
|
||||
fontWeight: 900,
|
||||
letterSpacing: 1.5,
|
||||
})}
|
||||
|
||||
{/* Sosyal Medya */}
|
||||
{social?.visible !== false &&
|
||||
renderSvgLines({
|
||||
text: social?.text || "@menulio.app",
|
||||
x: 512,
|
||||
y: 916,
|
||||
offsetY: social?.offsetY,
|
||||
fill: "#e5c378",
|
||||
fontFamily: infoFont,
|
||||
fontSize: social?.fontSize ? social.fontSize * 1.8 : 20,
|
||||
fontWeight: 700,
|
||||
letterSpacing: 1,
|
||||
})}
|
||||
</Svg>
|
||||
);
|
||||
};
|
||||
|
||||
export default QrTemplate8;
|
||||
@@ -0,0 +1,281 @@
|
||||
import * as React from "react";
|
||||
import Svg, {
|
||||
Defs,
|
||||
G,
|
||||
Path,
|
||||
Circle,
|
||||
Rect,
|
||||
Text,
|
||||
TextPath,
|
||||
Image,
|
||||
} from "react-native-svg";
|
||||
import { type QrTemplateProps, getFontFamily, renderSvgLines } from "./common";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const template9Bg = require("../../../assets/templates/9.jpeg");
|
||||
|
||||
const QrTemplate9 = ({
|
||||
qrBase64,
|
||||
fields,
|
||||
hideQr,
|
||||
...props
|
||||
}: QrTemplateProps) => {
|
||||
const rName = fields?.restaurantName;
|
||||
const sub = fields?.subtitle;
|
||||
const table = fields?.tableNo;
|
||||
const cta = fields?.ctaText;
|
||||
const wifi = fields?.wifiText;
|
||||
const social = fields?.socialText;
|
||||
|
||||
const rNameFont = getFontFamily(rName?.fontId, "System, -apple-system, Roboto, sans-serif");
|
||||
const subFont = getFontFamily(sub?.fontId, "System, -apple-system, Roboto, sans-serif");
|
||||
const ctaFont = getFontFamily(cta?.fontId, "System, -apple-system, Roboto, sans-serif");
|
||||
const infoFont = getFontFamily(wifi?.fontId, "System, -apple-system, Roboto, sans-serif");
|
||||
|
||||
return (
|
||||
<Svg width={300} height={300} viewBox="0 0 1024 1024" {...props}>
|
||||
<Defs>
|
||||
<Path id="arcTop9" fill="none" d="M330 260q182-115 364 0" />
|
||||
</Defs>
|
||||
|
||||
<Path fill="#f8f6f0" d="M0 0h1024v1024H0z" />
|
||||
<Image
|
||||
width={1024}
|
||||
height={1024}
|
||||
href={template9Bg}
|
||||
preserveAspectRatio="none"
|
||||
/>
|
||||
|
||||
{/* QR Alanı (Merkez) */}
|
||||
{!hideQr && (
|
||||
<G transform="translate(362 428)">
|
||||
<Rect
|
||||
width={300}
|
||||
height={300}
|
||||
fill="#fff"
|
||||
stroke="#d5cebe"
|
||||
strokeWidth={1.5}
|
||||
rx={24}
|
||||
/>
|
||||
<Rect
|
||||
width={276}
|
||||
height={276}
|
||||
x={12}
|
||||
y={12}
|
||||
fill="#fafaf7"
|
||||
stroke="#1e3246"
|
||||
strokeDasharray="6 4"
|
||||
strokeOpacity={0.35}
|
||||
strokeWidth={1.5}
|
||||
rx={16}
|
||||
/>
|
||||
|
||||
{qrBase64 ? (
|
||||
<Image
|
||||
href={`data:image/png;base64,${qrBase64}`}
|
||||
x={16}
|
||||
y={16}
|
||||
width={268}
|
||||
height={268}
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
<Circle
|
||||
cx={150}
|
||||
cy={132}
|
||||
r={28}
|
||||
fill="#f5efe6"
|
||||
stroke="#1e3246"
|
||||
strokeWidth={1.5}
|
||||
/>
|
||||
<Path
|
||||
fill="none"
|
||||
stroke="#1e3246"
|
||||
strokeLinecap="round"
|
||||
strokeWidth={1.9}
|
||||
d="M142.4 116.8v11.4c0 3.8 5.7 3.8 5.7 0v-11.4m-2.85 0v11.4m0 3.8v15.2m9.5-30.4c4.75 3.8 4.75 15.2 0 17.1v13.3"
|
||||
/>
|
||||
<Text
|
||||
x={150}
|
||||
y={192}
|
||||
fill="#1e3246"
|
||||
fontFamily="System, -apple-system, Roboto, sans-serif"
|
||||
fontSize={14}
|
||||
fontWeight={800}
|
||||
letterSpacing={2}
|
||||
textAnchor="middle"
|
||||
>
|
||||
{"QR KOD ALANI"}
|
||||
</Text>
|
||||
</>
|
||||
)}
|
||||
</G>
|
||||
)}
|
||||
|
||||
{/* Üst Amblem & Rozet */}
|
||||
<G stroke="#a88b58" transform="translate(290 142)">
|
||||
<Circle cx={-16} cy={-8} r={4.5} fill="none" strokeWidth={1.5} />
|
||||
<Path strokeLinecap="round" strokeWidth={1.5} d="M-16-12v8m-4-4h8" />
|
||||
<Path
|
||||
fill="none"
|
||||
strokeLinecap="round"
|
||||
strokeWidth={1.5}
|
||||
d="M0-12c4 0 8 6 8 12S4 12 0 12"
|
||||
/>
|
||||
<Path
|
||||
fill="none"
|
||||
strokeLinecap="round"
|
||||
strokeWidth={1.2}
|
||||
d="M-32 20c12 8 52 8 64 0"
|
||||
/>
|
||||
</G>
|
||||
|
||||
{/* Masa Numarası / Rozet Metni */}
|
||||
{table?.visible !== false &&
|
||||
renderSvgLines({
|
||||
text: table?.text || "TABLE MENU\nEST. 2026",
|
||||
x: 290,
|
||||
y: 164,
|
||||
offsetY: table?.offsetY,
|
||||
fill: "#1e3246",
|
||||
fontFamily: infoFont,
|
||||
fontSize: table?.fontSize ? table.fontSize * 1.3 : 15,
|
||||
fontWeight: 900,
|
||||
letterSpacing: 1,
|
||||
lineHeight: 18,
|
||||
})}
|
||||
|
||||
{/* Sol / Sağ Yönelticiler (SIAT / VIEW) */}
|
||||
<Text
|
||||
x={364}
|
||||
y={248}
|
||||
fill="#1e3246"
|
||||
fontFamily="System, -apple-system, Roboto, sans-serif"
|
||||
fontSize={23}
|
||||
fontWeight={900}
|
||||
letterSpacing={1}
|
||||
textAnchor="middle"
|
||||
transform="rotate(-26 364 248)"
|
||||
>
|
||||
{"SCAN"}
|
||||
</Text>
|
||||
<Text
|
||||
x={670}
|
||||
y={244}
|
||||
fill="#1e3246"
|
||||
fontFamily="System, -apple-system, Roboto, sans-serif"
|
||||
fontSize={25}
|
||||
fontWeight={900}
|
||||
letterSpacing={1}
|
||||
textAnchor="middle"
|
||||
transform="rotate(24 670 244)"
|
||||
>
|
||||
{"VIEW"}
|
||||
</Text>
|
||||
|
||||
{/* Kavisli Üst Yazı */}
|
||||
{sub?.visible !== false && (
|
||||
<Text
|
||||
fill="#1e3246"
|
||||
fontFamily={subFont}
|
||||
fontSize={sub?.fontSize ? sub.fontSize * 1.8 : 46}
|
||||
fontWeight={900}
|
||||
letterSpacing={2}
|
||||
>
|
||||
<TextPath href="#arcTop9" startOffset="50%" textAnchor="middle">
|
||||
{sub?.text || "DIGITAL MENU"}
|
||||
</TextPath>
|
||||
</Text>
|
||||
)}
|
||||
|
||||
{/* Restoran Başlığı / Ana Başlık */}
|
||||
{rName?.visible !== false &&
|
||||
renderSvgLines({
|
||||
text: rName?.text || "OUR MENU",
|
||||
x: 512,
|
||||
y: 326,
|
||||
offsetY: rName?.offsetY,
|
||||
fill: "#1e3246",
|
||||
fontFamily: rNameFont,
|
||||
fontSize: rName?.fontSize ? rName.fontSize * 2.4 : 76,
|
||||
fontWeight: 900,
|
||||
letterSpacing: 3,
|
||||
})}
|
||||
|
||||
{/* Yönlendirici Oklar */}
|
||||
<G stroke="#1e3246">
|
||||
<Path
|
||||
fill="none"
|
||||
strokeLinecap="round"
|
||||
strokeWidth={3}
|
||||
d="M390 780c-16-8-16-26-8-36"
|
||||
/>
|
||||
<Path
|
||||
fill="#1e3246"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={1.5}
|
||||
d="m374 750 8-6 6 8Z"
|
||||
/>
|
||||
</G>
|
||||
<G stroke="#1e3246">
|
||||
<Path
|
||||
fill="none"
|
||||
strokeLinecap="round"
|
||||
strokeWidth={3}
|
||||
d="M634 780c16-8 16-26 8-36"
|
||||
/>
|
||||
<Path
|
||||
fill="#1e3246"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={1.5}
|
||||
d="m650 750-8-6-6 8Z"
|
||||
/>
|
||||
</G>
|
||||
|
||||
{/* CTA Metni */}
|
||||
{cta?.visible !== false &&
|
||||
renderSvgLines({
|
||||
text: cta?.text || "VIEW OUR\nDELICIOUS MENU",
|
||||
x: 512,
|
||||
y: 752,
|
||||
offsetY: cta?.offsetY,
|
||||
fill: "#1e3246",
|
||||
fontFamily: ctaFont,
|
||||
fontSize: cta?.fontSize ? cta.fontSize * 1.6 : 20,
|
||||
fontWeight: 900,
|
||||
letterSpacing: 1,
|
||||
lineHeight: 28,
|
||||
})}
|
||||
|
||||
{/* Alt Bilgi / Karşılama & WiFi */}
|
||||
{wifi?.visible !== false &&
|
||||
renderSvgLines({
|
||||
text: wifi?.text || "WELCOME TO OUR TABLE",
|
||||
x: 500,
|
||||
y: 894,
|
||||
offsetY: wifi?.offsetY,
|
||||
fill: "#1e3246",
|
||||
fontFamily: infoFont,
|
||||
fontSize: wifi?.fontSize ? wifi.fontSize * 1.5 : 18,
|
||||
fontWeight: 900,
|
||||
letterSpacing: 1.5,
|
||||
})}
|
||||
|
||||
{/* Sosyal Medya */}
|
||||
{social?.visible !== false &&
|
||||
renderSvgLines({
|
||||
text: social?.text || "@menulio.table",
|
||||
x: 500,
|
||||
y: 934,
|
||||
offsetY: social?.offsetY,
|
||||
fill: "#a88b58",
|
||||
fontFamily: infoFont,
|
||||
fontSize: social?.fontSize ? social.fontSize * 1.5 : 16,
|
||||
fontWeight: 700,
|
||||
letterSpacing: 1,
|
||||
})}
|
||||
</Svg>
|
||||
);
|
||||
};
|
||||
|
||||
export default QrTemplate9;
|
||||
@@ -0,0 +1,217 @@
|
||||
import * as React from "react";
|
||||
import Svg, {
|
||||
Path,
|
||||
Rect,
|
||||
Circle,
|
||||
G,
|
||||
Text,
|
||||
Image,
|
||||
} from "react-native-svg";
|
||||
import { type QrTemplateProps, getFontFamily, renderSvgLines } from "./common";
|
||||
|
||||
const QrTemplateLumiere = ({
|
||||
qrBase64,
|
||||
fields,
|
||||
hideQr,
|
||||
...props
|
||||
}: QrTemplateProps) => {
|
||||
const rName = fields?.restaurantName;
|
||||
const sub = fields?.subtitle;
|
||||
const table = fields?.tableNo;
|
||||
const cta = fields?.ctaText;
|
||||
const wifi = fields?.wifiText;
|
||||
const social = fields?.socialText;
|
||||
|
||||
const rNameFont = getFontFamily(rName?.fontId, "Georgia, 'Playfair Display', serif");
|
||||
const subFont = getFontFamily(sub?.fontId, "System, -apple-system, Roboto, sans-serif");
|
||||
const ctaFont = getFontFamily(cta?.fontId, "Georgia, 'Playfair Display', serif");
|
||||
const infoFont = getFontFamily(wifi?.fontId, "System, -apple-system, Roboto, sans-serif");
|
||||
|
||||
return (
|
||||
<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 */}
|
||||
{!hideQr && (
|
||||
<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="System, sans-serif"
|
||||
fontSize={12}
|
||||
fontWeight={600}
|
||||
textAnchor="middle"
|
||||
>
|
||||
{"QR KOD ALANI"}
|
||||
</Text>
|
||||
</>
|
||||
)}
|
||||
</G>
|
||||
)}
|
||||
|
||||
{/* Metinler (Çok satırlı ve bağımsız font destekli) */}
|
||||
<G textAnchor="middle">
|
||||
{/* Restoran Adı */}
|
||||
{rName?.visible !== false &&
|
||||
renderSvgLines({
|
||||
text: rName?.text || "LUMIÈRE",
|
||||
x: 300,
|
||||
y: 148,
|
||||
offsetY: rName?.offsetY,
|
||||
fill: "#d4af37",
|
||||
fontFamily: rNameFont,
|
||||
fontSize: rName?.fontSize || 30,
|
||||
fontWeight: "bold",
|
||||
letterSpacing: 4,
|
||||
})}
|
||||
|
||||
{/* Slogan / Alt Başlık (Enter ile alt satıra geçebilir) */}
|
||||
{sub?.visible !== false &&
|
||||
renderSvgLines({
|
||||
text: sub?.text || "RESTAURANT & BAR | MODERN CUISINE",
|
||||
x: 300,
|
||||
y: 178,
|
||||
offsetY: sub?.offsetY,
|
||||
fill: "#e5c378",
|
||||
fontFamily: subFont,
|
||||
fontSize: sub?.fontSize || 11,
|
||||
fontWeight: 600,
|
||||
letterSpacing: 2.5,
|
||||
})}
|
||||
|
||||
{/* CTA */}
|
||||
{cta?.visible !== false &&
|
||||
renderSvgLines({
|
||||
text: cta?.text || "MENÜYÜ GÖRMEK İÇİN OKUTUN",
|
||||
x: 300,
|
||||
y: 472,
|
||||
offsetY: cta?.offsetY,
|
||||
fill: "#d4af37",
|
||||
fontFamily: ctaFont,
|
||||
fontSize: cta?.fontSize || 13,
|
||||
fontWeight: "bold",
|
||||
letterSpacing: 2,
|
||||
})}
|
||||
|
||||
{/* Sosyal Medya / İletişim */}
|
||||
{social?.visible !== false &&
|
||||
renderSvgLines({
|
||||
text: social?.text || "Rezervasyon: 0212 555 0199",
|
||||
x: 300,
|
||||
y: 504,
|
||||
offsetY: social?.offsetY,
|
||||
fill: "#e2ddd2",
|
||||
fontFamily: infoFont,
|
||||
fontSize: social?.fontSize || 10.5,
|
||||
fontWeight: 500,
|
||||
letterSpacing: 1,
|
||||
})}
|
||||
|
||||
{/* Masa No & WiFi */}
|
||||
{(table?.visible !== false || wifi?.visible !== false) &&
|
||||
renderSvgLines({
|
||||
text: [
|
||||
table?.visible !== false && table?.text ? table.text : "",
|
||||
wifi?.visible !== false && wifi?.text ? wifi.text : "",
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(" • "),
|
||||
x: 300,
|
||||
y: 526,
|
||||
offsetY: table?.offsetY || wifi?.offsetY,
|
||||
fill: "#c5a059",
|
||||
fontFamily: infoFont,
|
||||
fontSize: table?.fontSize || wifi?.fontSize || 10,
|
||||
fontWeight: 600,
|
||||
letterSpacing: 1,
|
||||
})}
|
||||
|
||||
</G>
|
||||
</Svg>
|
||||
);
|
||||
};
|
||||
|
||||
export default QrTemplateLumiere;
|
||||
@@ -0,0 +1,112 @@
|
||||
import React from "react";
|
||||
import { Text as SvgText, type SvgProps } from "react-native-svg";
|
||||
|
||||
export interface TextItemConfig {
|
||||
text: string;
|
||||
fontSize?: number;
|
||||
fontId?: string;
|
||||
offsetY?: number; // Dikey konum kaydırma (yukarı / aşağı)
|
||||
visible?: boolean;
|
||||
}
|
||||
|
||||
export interface QrCustomFields {
|
||||
restaurantName: TextItemConfig;
|
||||
subtitle: TextItemConfig;
|
||||
tableNo: TextItemConfig;
|
||||
ctaText: TextItemConfig;
|
||||
wifiText: TextItemConfig;
|
||||
socialText: TextItemConfig;
|
||||
}
|
||||
|
||||
export interface QrTemplateProps extends SvgProps {
|
||||
qrBase64?: string;
|
||||
fields?: Partial<QrCustomFields>;
|
||||
hideQr?: boolean;
|
||||
}
|
||||
|
||||
// Türkçe karakterleri (İ, ı, Ş, ş, Ğ, ğ, Ü, ü, Ö, ö, Ç, ç) eksiksiz destekleyen sistem fontları
|
||||
export const FONT_OPTIONS = [
|
||||
{
|
||||
id: "sans",
|
||||
label: "Modern Sans",
|
||||
fontFamily: "System, -apple-system, Roboto, 'Helvetica Neue', Arial, sans-serif",
|
||||
},
|
||||
{
|
||||
id: "serif",
|
||||
label: "Zarif Serif",
|
||||
fontFamily: "Georgia, 'Times New Roman', serif",
|
||||
},
|
||||
{
|
||||
id: "times",
|
||||
label: "Klasik (Times)",
|
||||
fontFamily: "'Times New Roman', Times, Georgia, serif",
|
||||
},
|
||||
{
|
||||
id: "palatino",
|
||||
label: "Lüks Palatino",
|
||||
fontFamily: "Palatino, 'Palatino Linotype', Georgia, serif",
|
||||
},
|
||||
{
|
||||
id: "trebuchet",
|
||||
label: "Dinamik",
|
||||
fontFamily: "'Trebuchet MS', 'Segoe UI', Arial, sans-serif",
|
||||
},
|
||||
];
|
||||
|
||||
export function getFontFamily(fontId?: string, defaultFamily = "Georgia, serif"): string {
|
||||
if (!fontId) return defaultFamily;
|
||||
const found = FONT_OPTIONS.find((f) => f.id === fontId);
|
||||
return found ? found.fontFamily : defaultFamily;
|
||||
}
|
||||
|
||||
// Çok satırlı (Enter / \n) ve dikey kaydırmalı (offsetY) SVG metin render fonksiyonu
|
||||
export function renderSvgLines({
|
||||
text,
|
||||
x,
|
||||
y,
|
||||
offsetY = 0,
|
||||
lineHeight,
|
||||
fill,
|
||||
fontFamily,
|
||||
fontSize,
|
||||
fontWeight = "normal",
|
||||
letterSpacing,
|
||||
textAnchor = "middle",
|
||||
}: {
|
||||
text?: string;
|
||||
x: number;
|
||||
y: number;
|
||||
offsetY?: number;
|
||||
lineHeight?: number;
|
||||
fill?: string;
|
||||
fontFamily?: string;
|
||||
fontSize?: number;
|
||||
fontWeight?: string | number;
|
||||
letterSpacing?: number;
|
||||
textAnchor?: "middle" | "start" | "end";
|
||||
}) {
|
||||
if (!text) return null;
|
||||
const lines = text.split("\n");
|
||||
const computedLineHeight = lineHeight || (fontSize ? fontSize * 1.25 : 18);
|
||||
const startY = y + (offsetY || 0);
|
||||
|
||||
return (
|
||||
<>
|
||||
{lines.map((line, idx) => (
|
||||
<SvgText
|
||||
key={idx}
|
||||
x={x}
|
||||
y={startY + idx * computedLineHeight}
|
||||
fill={fill}
|
||||
fontFamily={fontFamily}
|
||||
fontSize={fontSize}
|
||||
fontWeight={fontWeight}
|
||||
letterSpacing={letterSpacing}
|
||||
textAnchor={textAnchor}
|
||||
>
|
||||
{line}
|
||||
</SvgText>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
import React from "react";
|
||||
import QrTemplate1 from "./QrTemplateLumiere";
|
||||
import QrTemplate2 from "./QrTemplate2";
|
||||
import QrTemplate3 from "./QrTemplate3";
|
||||
import QrTemplate4 from "./QrTemplate4";
|
||||
import QrTemplate5 from "./QrTemplate5";
|
||||
import QrTemplate6 from "./QrTemplate6";
|
||||
import QrTemplate8 from "./QrTemplate8";
|
||||
import QrTemplate9 from "./QrTemplate9";
|
||||
import { type QrTemplateProps } from "./common";
|
||||
|
||||
export * from "./common";
|
||||
|
||||
// Yeni component eklemek için buraya import edip COMPONENT_TEMPLATES'e kaydet
|
||||
export const COMPONENT_TEMPLATES: Record<
|
||||
string,
|
||||
React.FC<QrTemplateProps>
|
||||
> = {
|
||||
"qr-1": QrTemplate1,
|
||||
"qr-2": QrTemplate2,
|
||||
"qr-3": QrTemplate3,
|
||||
"qr-4": QrTemplate4,
|
||||
"qr-5": QrTemplate5,
|
||||
"qr-6": QrTemplate6,
|
||||
"qr-8": QrTemplate8,
|
||||
"qr-9": QrTemplate9,
|
||||
};
|
||||
|
||||
// Her template'in aspect ratio'su (width/height)
|
||||
export const TEMPLATE_ASPECT_RATIOS: Record<string, number> = {
|
||||
"qr-1": 1, // 600x600 = 1:1
|
||||
"qr-2": 400 / 650, // 400x650 = standart kart
|
||||
"qr-3": 400 / 650, // 400x650 = standart kart
|
||||
"qr-4": 400 / 580, // 400x580 = zarif altın çerçeve
|
||||
"qr-5": 380 / 500, // 380x500 = PNG arka planlı modern kart
|
||||
"qr-6": 400 / 580, // 400x580 = taş & ızgara vintage stant
|
||||
"qr-8": 1, // 1024x1024 = 1:1 Emerald Gold Özel Tasarım
|
||||
"qr-9": 380 / 500, // 380x500 = Vintage Table Menu Özel Tasarım
|
||||
};
|
||||
@@ -0,0 +1,177 @@
|
||||
import * as React from "react"
|
||||
import Svg, { SvgProps, Path, Rect, Circle, G, Text } from "react-native-svg"
|
||||
const SvgComponent = (props: SvgProps) => (
|
||||
<Svg xmlns="http://www.w3.org/2000/svg" width={600} height={600} {...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}
|
||||
/>
|
||||
<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" />
|
||||
<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>
|
||||
<Path
|
||||
stroke="#d4af37"
|
||||
strokeOpacity={0.4}
|
||||
strokeWidth={0.8}
|
||||
d="M120 198h360"
|
||||
/>
|
||||
<G transform="translate(190 215)">
|
||||
<Rect
|
||||
width={220}
|
||||
height={220}
|
||||
fill="#fff"
|
||||
stroke="#d4af37"
|
||||
strokeWidth={2}
|
||||
rx={20}
|
||||
/>
|
||||
<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>
|
||||
<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 SvgComponent
|
||||
@@ -0,0 +1,286 @@
|
||||
import * as React from "react"
|
||||
import Svg, {
|
||||
SvgProps,
|
||||
Path,
|
||||
Rect,
|
||||
G,
|
||||
Circle,
|
||||
Ellipse,
|
||||
Text,
|
||||
} from "react-native-svg"
|
||||
const SvgComponent = (props: SvgProps) => (
|
||||
<Svg xmlns="http://www.w3.org/2000/svg" width={400} height={650} {...props}>
|
||||
<Path fill="#252c36" d="M0 0h400v650H0z" />
|
||||
<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}
|
||||
/>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<Path
|
||||
fill="none"
|
||||
stroke="#8c6239"
|
||||
strokeLinecap="round"
|
||||
strokeWidth={1.2}
|
||||
d="M142 66c6-2 12 2 18 0m80 0c6 2 12-2 18 0"
|
||||
/>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<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" />
|
||||
<G transform="translate(90 215)">
|
||||
<Rect
|
||||
width={220}
|
||||
height={220}
|
||||
fill="#fbf8f2"
|
||||
stroke="#d8c7b0"
|
||||
strokeWidth={1.5}
|
||||
rx={20}
|
||||
/>
|
||||
<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>
|
||||
<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', 'Brush Script MT', 'Great Vibes', Georgia, cursive, 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 SvgComponent
|
||||
@@ -0,0 +1,416 @@
|
||||
import * as React from "react"
|
||||
import Svg, {
|
||||
SvgProps,
|
||||
Defs,
|
||||
LinearGradient,
|
||||
Stop,
|
||||
Pattern,
|
||||
Path,
|
||||
G,
|
||||
Rect,
|
||||
Ellipse,
|
||||
Circle,
|
||||
Text,
|
||||
TSpan,
|
||||
} from "react-native-svg"
|
||||
/* SVGR has dropped some elements not supported by react-native-svg: style */
|
||||
const SvgComponent = (props: SvgProps) => (
|
||||
<Svg width={400} height={650} {...props}>
|
||||
<Defs>
|
||||
<LinearGradient id="card-grad" 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" 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" x1="0%" x2="0%" y1="0%" y2="100%">
|
||||
<Stop offset="0%" stopColor="#7b1123" />
|
||||
<Stop offset="100%" stopColor="#4a0512" />
|
||||
</LinearGradient>
|
||||
<Pattern
|
||||
id="checkered-border"
|
||||
width={20}
|
||||
height={20}
|
||||
patternUnits="userSpaceOnUse"
|
||||
>
|
||||
<Path fill="#b92b27" d="M0 0h20v20H0z" />
|
||||
<Path fill="#fdf8ee" d="M0 0h10v10H0zm10 10h10v10H10z" />
|
||||
</Pattern>
|
||||
</Defs>
|
||||
<Path
|
||||
id="layer-background"
|
||||
fill="url(#checkered-border)"
|
||||
d="M0 0h400v650H0z"
|
||||
/>
|
||||
<G id="layer-card">
|
||||
<Rect
|
||||
width={356}
|
||||
height={606}
|
||||
x={22}
|
||||
y={22}
|
||||
fill="url(#card-grad)"
|
||||
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>
|
||||
<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"
|
||||
/>
|
||||
<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)"
|
||||
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>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<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)"
|
||||
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>
|
||||
<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>
|
||||
<G id="qr-placeholder" transform="translate(90 215)">
|
||||
<Rect
|
||||
width={220}
|
||||
height={220}
|
||||
fill="#fff"
|
||||
stroke="#e4e4e7"
|
||||
strokeWidth={2}
|
||||
rx={20}
|
||||
/>
|
||||
<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"
|
||||
dominantBaseline="middle"
|
||||
fontFamily='"Inter",Arial,sans-serif'
|
||||
fontSize={12}
|
||||
fontWeight={700}
|
||||
letterSpacing={0.5}
|
||||
textAnchor="middle"
|
||||
>
|
||||
{"QR KOD ALANI"}
|
||||
</Text>
|
||||
</G>
|
||||
<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}
|
||||
className="font-brand-arc"
|
||||
fontSize={24}
|
||||
textAnchor="middle"
|
||||
>
|
||||
{"PIZZERIA"}
|
||||
</Text>
|
||||
<Text
|
||||
x={200}
|
||||
y={132}
|
||||
className="font-brand-arc"
|
||||
fontSize={31}
|
||||
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} className="font-cta" textAnchor="middle">
|
||||
{"MEN\xDCY\xDC G\xD6RMEK"}
|
||||
</Text>
|
||||
<Text x={200} y={494} className="font-cta" textAnchor="middle">
|
||||
{"\u0130\xC7\u0130N OKUTUN"}
|
||||
</Text>
|
||||
<Text x={200} y={546} textAnchor="middle">
|
||||
<TSpan className="font-footer-bold">{"WiFi: "}</TSpan>
|
||||
<TSpan className="font-footer-reg">{"GourmetBistro"}</TSpan>
|
||||
<TSpan fill="#a1a1aa" className="font-footer-reg">
|
||||
{" | "}
|
||||
</TSpan>
|
||||
<TSpan className="font-footer-bold">{"\u015Eifre: "}</TSpan>
|
||||
<TSpan className="font-footer-reg">{"lezzetli01"}</TSpan>
|
||||
</Text>
|
||||
<Text x={200} y={570} textAnchor="middle">
|
||||
<TSpan fill="#b92b27" className="font-footer-bold">
|
||||
{"B\u0130Z\u0130 TAK\u0130P ED\u0130N"}
|
||||
</TSpan>
|
||||
<TSpan fill="#a1a1aa" className="font-footer-reg">
|
||||
{" | "}
|
||||
</TSpan>
|
||||
<TSpan className="font-footer-reg">{"@gourmetbistro"}</TSpan>
|
||||
</Text>
|
||||
</G>
|
||||
</Svg>
|
||||
)
|
||||
export default SvgComponent
|
||||
@@ -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
|
||||
|
After Width: | Height: | Size: 278 KiB |
@@ -0,0 +1,156 @@
|
||||
import Svg, { SvgProps, Image, G, Rect, Path, Text } from "react-native-svg"
|
||||
import { memo } from "react"
|
||||
const SvgComponent = (props: SvgProps) => (
|
||||
<Svg width={380} height={500} {...props}>
|
||||
<Image
|
||||
width={380}
|
||||
height={500}
|
||||
href="arka_plan_rengini_deg\u0306is\u0327_202608210045 1.jpg"
|
||||
preserveAspectRatio="none"
|
||||
/>
|
||||
<G transform="translate(108 206)">
|
||||
<Rect
|
||||
width={164}
|
||||
height={164}
|
||||
fill="#fff"
|
||||
stroke="#dce5ea"
|
||||
strokeWidth={1.2}
|
||||
rx={16}
|
||||
/>
|
||||
<Path
|
||||
fill="none"
|
||||
stroke="#003554"
|
||||
strokeLinecap="round"
|
||||
strokeWidth={3}
|
||||
d="M6 30V12a6 6 0 0 1 6-6h18m128 24V12a6 6 0 0 0-6-6h-18M6 134v18a6 6 0 0 0 6 6h18m128-24v18a6 6 0 0 1-6 6h-18"
|
||||
/>
|
||||
<Rect
|
||||
width={140}
|
||||
height={140}
|
||||
x={12}
|
||||
y={12}
|
||||
fill="#fafcfd"
|
||||
stroke="#003554"
|
||||
strokeDasharray="4 3"
|
||||
strokeOpacity={0.2}
|
||||
rx={10}
|
||||
/>
|
||||
<Text
|
||||
x={82}
|
||||
y={87}
|
||||
fill="#003554"
|
||||
fontFamily="Inter, Arial, sans-serif"
|
||||
fontSize={11}
|
||||
fontWeight={700}
|
||||
letterSpacing={1}
|
||||
textAnchor="middle"
|
||||
>
|
||||
{"QR KOD ALANI"}
|
||||
</Text>
|
||||
</G>
|
||||
<G fill="#003554" textAnchor="middle">
|
||||
<Text
|
||||
x={190}
|
||||
y={50}
|
||||
fontFamily="'Arial Black', 'Montserrat', sans-serif"
|
||||
fontSize={13}
|
||||
fontWeight={900}
|
||||
letterSpacing={2}
|
||||
>
|
||||
{"THE"}
|
||||
</Text>
|
||||
<Text
|
||||
x={190}
|
||||
y={80}
|
||||
fontFamily="'Arial Black', 'Impact', sans-serif"
|
||||
fontSize={27}
|
||||
fontWeight={900}
|
||||
letterSpacing={0.5}
|
||||
>
|
||||
{"URBAN"}
|
||||
</Text>
|
||||
<Text
|
||||
x={190}
|
||||
y={110}
|
||||
fontFamily="'Arial Black', 'Impact', sans-serif"
|
||||
fontSize={27}
|
||||
fontWeight={900}
|
||||
letterSpacing={0.5}
|
||||
>
|
||||
{"FORK"}
|
||||
</Text>
|
||||
<Text
|
||||
x={190}
|
||||
y={148}
|
||||
fontFamily="'Arial Black', 'Montserrat', sans-serif"
|
||||
fontSize={15.5}
|
||||
fontWeight={900}
|
||||
letterSpacing={0.2}
|
||||
>
|
||||
{"SCAN TO VIEW"}
|
||||
</Text>
|
||||
<Text
|
||||
x={190}
|
||||
y={172}
|
||||
fontFamily="'Arial Black', 'Montserrat', sans-serif"
|
||||
fontSize={15.5}
|
||||
fontWeight={900}
|
||||
letterSpacing={0.2}
|
||||
>
|
||||
{"DIGITAL MENU"}
|
||||
</Text>
|
||||
<Text
|
||||
x={190}
|
||||
y={402}
|
||||
fontFamily="'Arial Black', 'Montserrat', sans-serif"
|
||||
fontSize={15.5}
|
||||
fontWeight={900}
|
||||
letterSpacing={0.8}
|
||||
>
|
||||
{"FRESH FLAVORS DAILY"}
|
||||
</Text>
|
||||
<Text
|
||||
x={190}
|
||||
y={420}
|
||||
fontFamily="Inter, Arial, sans-serif"
|
||||
fontSize={8.5}
|
||||
fontWeight={600}
|
||||
letterSpacing={0.2}
|
||||
>
|
||||
{"Contactless ordering to scancnal bresers"}
|
||||
</Text>
|
||||
<Text
|
||||
x={190}
|
||||
y={432}
|
||||
fontFamily="Inter, Arial, sans-serif"
|
||||
fontSize={8.5}
|
||||
fontWeight={600}
|
||||
letterSpacing={0.2}
|
||||
>
|
||||
{"and enstand mes with your menu."}
|
||||
</Text>
|
||||
<Text
|
||||
x={190}
|
||||
y={454}
|
||||
fontFamily="'Arial Black', Inter, sans-serif"
|
||||
fontSize={10}
|
||||
fontWeight={900}
|
||||
letterSpacing={0.2}
|
||||
>
|
||||
{"contactless ordering"}
|
||||
</Text>
|
||||
<Text
|
||||
x={190}
|
||||
y={468}
|
||||
fontFamily="'Arial Black', Inter, sans-serif"
|
||||
fontSize={10}
|
||||
fontWeight={900}
|
||||
letterSpacing={0.2}
|
||||
>
|
||||
{"scan with camera"}
|
||||
</Text>
|
||||
</G>
|
||||
</Svg>
|
||||
)
|
||||
const Memo = memo(SvgComponent)
|
||||
export default Memo
|
||||
@@ -0,0 +1,327 @@
|
||||
import Svg, {
|
||||
SvgProps,
|
||||
Defs,
|
||||
G,
|
||||
Path,
|
||||
Circle,
|
||||
Rect,
|
||||
Use,
|
||||
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="#c59b58"
|
||||
strokeLinecap="round"
|
||||
strokeWidth={1.2}
|
||||
d="M0 0c10 25 12 55 5 85"
|
||||
/>
|
||||
<Path
|
||||
fill="#f4efe6"
|
||||
stroke="#c59b58"
|
||||
strokeWidth={1.1}
|
||||
d="M3 15c-11-5-15 9-1 9 4 0 4-6 1-9Z"
|
||||
/>
|
||||
<Path
|
||||
fill="#f4efe6"
|
||||
stroke="#9e4d28"
|
||||
strokeWidth={1.1}
|
||||
d="M5 22c11-6 17 6 3 10-4 0-4-6-3-10Zm2 20c-12-6-17 8-3 10 4 0 4-6 3-10Z"
|
||||
/>
|
||||
<Path
|
||||
fill="#f4efe6"
|
||||
stroke="#c59b58"
|
||||
strokeWidth={1.1}
|
||||
d="M8 50c12-6 16 8 2 12-4 0-4-7-2-12ZM6 70c-10-5-14 8-2 10 3 0 4-6 2-10Z"
|
||||
/>
|
||||
<Path
|
||||
fill="#f4efe6"
|
||||
stroke="#9e4d28"
|
||||
strokeWidth={1.1}
|
||||
d="M5 78c11-4 15 8 3 12-4 0-4-6-3-12Z"
|
||||
/>
|
||||
<Circle cx={-10} cy={18} r={1.5} fill="#c59b58" />
|
||||
<Circle cx={18} cy={46} r={1.5} fill="#9e4d28" />
|
||||
</G>
|
||||
<G id="a">
|
||||
<Path
|
||||
fill="#a64423"
|
||||
d="M0-18C4-12 8-6 8 0c6-4 8 6 4 12 4-2 6 4 2 8S2 26 0 26s-10-2-14-6-2-10 2-8c-4-6-2-16 4-12 0-6 4-12 8-18"
|
||||
/>
|
||||
<Path
|
||||
fill="#f4efe6"
|
||||
d="M-4 4v8c0 3 2 4 4 4v8c2-8 4-9 4-12V4H3v7c0 2-1.5 3-3 3s-3-1-3-3V4Z"
|
||||
/>
|
||||
<Path stroke="#f4efe6" d="M0 4v8" />
|
||||
</G>
|
||||
</Defs>
|
||||
<Path fill="#434b54" d="M0 0h400v580H0z" />
|
||||
<Rect
|
||||
width={360}
|
||||
height={548}
|
||||
x={20}
|
||||
y={16}
|
||||
fill="#f6efe3"
|
||||
stroke="#122a42"
|
||||
strokeWidth={1.5}
|
||||
rx={16}
|
||||
/>
|
||||
<Path
|
||||
fill="none"
|
||||
stroke="#122a42"
|
||||
strokeWidth={1.2}
|
||||
d="M46 32h308a12 12 0 0 1 12 12v492a12 12 0 0 1-12 12H46a12 12 0 0 1-12-12V44a12 12 0 0 1 12-12Z"
|
||||
/>
|
||||
<Path
|
||||
fill="none"
|
||||
stroke="#122a42"
|
||||
strokeWidth={1.2}
|
||||
d="M34 46a12 12 0 0 0 12-12m320 12a12 12 0 0 1-12-12M34 534a12 12 0 0 1 12 12m320-12a12 12 0 0 0-12 12"
|
||||
/>
|
||||
<Path stroke="#c59b58" d="M34 135h332M34 500h332" />
|
||||
<Use href="#a" transform="matrix(.85 0 0 .85 200 52)" />
|
||||
<Use href="#b" transform="matrix(.8 0 0 .8 42 42)" />
|
||||
<Use href="#b" transform="matrix(-.8 0 0 .8 358 42)" />
|
||||
<Use href="#b" transform="matrix(.95 0 0 .95 40 185)" />
|
||||
<Use href="#b" transform="matrix(-.95 0 0 .95 360 185)" />
|
||||
<Use href="#a" transform="matrix(.65 0 0 .65 68 320)" />
|
||||
<Use href="#a" transform="matrix(.65 0 0 .65 332 320)" />
|
||||
<Use href="#b" transform="matrix(.95 0 0 .95 40 360)" />
|
||||
<Use href="#b" transform="matrix(-.95 0 0 .95 360 360)" />
|
||||
<G transform="translate(200 465)">
|
||||
<Rect
|
||||
width={18}
|
||||
height={28}
|
||||
x={-9}
|
||||
y={-14}
|
||||
fill="#122a42"
|
||||
stroke="#122a42"
|
||||
rx={3.5}
|
||||
/>
|
||||
<Rect width={14} height={20} x={-7} y={-11} fill="#fff" rx={1.5} />
|
||||
<Path fill="#122a42" d="M-4.5-8.5h9v9h-9z" />
|
||||
<Path fill="#fff" d="M-3-7h6v6h-6z" />
|
||||
<Path fill="#122a42" d="M-1.5-5.5h3v3h-3z" />
|
||||
<Circle cy={11} r={1} fill="#fff" />
|
||||
</G>
|
||||
<Circle cx={258} cy={537} r={4.5} fill="#a64423" />
|
||||
<Path
|
||||
fill="#f6efe3"
|
||||
d="M259 541h-1.5v-4h-1v-1.5h1v-1c0-1 .5-1.5 2-1.5h1v1.5h-1c-.5 0-.5.3-.5.7v.3h1.5l-.3 1.5H259Z"
|
||||
/>
|
||||
<Rect
|
||||
width={9}
|
||||
height={9}
|
||||
x={266}
|
||||
y={532.5}
|
||||
fill="none"
|
||||
stroke="#a64423"
|
||||
strokeWidth={1.2}
|
||||
rx={2.5}
|
||||
/>
|
||||
<Circle cx={270.5} cy={537} r={2} fill="none" stroke="#a64423" />
|
||||
<Circle cx={273} cy={534.5} r={0.5} fill="#a64423" />
|
||||
<G transform="translate(95 205)">
|
||||
<Path
|
||||
fill="#fdfbf7"
|
||||
stroke="#122a42"
|
||||
strokeWidth={3}
|
||||
d="M0 0h210v210H0z"
|
||||
/>
|
||||
<Path
|
||||
fill="none"
|
||||
stroke="#c59b58"
|
||||
strokeWidth={1.5}
|
||||
d="M4 4h202v202H4z"
|
||||
/>
|
||||
<Path
|
||||
fill="#faf8f5"
|
||||
stroke="#122a42"
|
||||
strokeDasharray="4 3"
|
||||
d="M12 12h186v186H12z"
|
||||
/>
|
||||
<Circle
|
||||
cx={105}
|
||||
cy={95}
|
||||
r={16}
|
||||
fill="#f6efe3"
|
||||
stroke="#c59b58"
|
||||
strokeWidth={1.2}
|
||||
/>
|
||||
<Use href="#a" transform="matrix(.45 0 0 .45 105 93)" />
|
||||
<Text
|
||||
x={105}
|
||||
y={128}
|
||||
fill="#122a42"
|
||||
fontFamily="Inter, Arial, sans-serif"
|
||||
fontSize={11}
|
||||
fontWeight={800}
|
||||
letterSpacing={1.5}
|
||||
textAnchor="middle"
|
||||
>
|
||||
{"QR KOD ALANI"}
|
||||
</Text>
|
||||
</G>
|
||||
<Text
|
||||
x={172}
|
||||
y={55}
|
||||
fill="#122a42"
|
||||
fontFamily="Inter, Arial, sans-serif"
|
||||
fontSize={8.5}
|
||||
fontWeight={800}
|
||||
letterSpacing={1}
|
||||
textAnchor="middle"
|
||||
>
|
||||
{"EST."}
|
||||
</Text>
|
||||
<Text
|
||||
x={228}
|
||||
y={55}
|
||||
fill="#122a42"
|
||||
fontFamily="Inter, Arial, sans-serif"
|
||||
fontSize={8.5}
|
||||
fontWeight={800}
|
||||
letterSpacing={1}
|
||||
textAnchor="middle"
|
||||
>
|
||||
{"2018"}
|
||||
</Text>
|
||||
<Text
|
||||
x={200}
|
||||
y={84}
|
||||
fill="#a64423"
|
||||
fontFamily="'Playfair Display', Georgia, 'Times New Roman', serif"
|
||||
fontSize={19}
|
||||
fontWeight={900}
|
||||
letterSpacing={1.5}
|
||||
textAnchor="middle"
|
||||
>
|
||||
{"THE STONEHEARTH GRILL"}
|
||||
</Text>
|
||||
<Text
|
||||
x={200}
|
||||
y={102}
|
||||
fill="#122a42"
|
||||
fontFamily="Inter, Arial, sans-serif"
|
||||
fontSize={8.5}
|
||||
fontWeight={700}
|
||||
letterSpacing={2}
|
||||
textAnchor="middle"
|
||||
>
|
||||
{"EST. 2018"}
|
||||
</Text>
|
||||
<Text
|
||||
x={200}
|
||||
y={118}
|
||||
fill="#122a42"
|
||||
fontFamily="Inter, Arial, sans-serif"
|
||||
fontSize={10}
|
||||
fontWeight={800}
|
||||
letterSpacing={2}
|
||||
textAnchor="middle"
|
||||
>
|
||||
{"SCAN. ORDER. ENJOY."}
|
||||
</Text>
|
||||
<Text
|
||||
x={200}
|
||||
y={158}
|
||||
fill="#122a42"
|
||||
fontFamily="'Playfair Display', Georgia, 'Times New Roman', serif"
|
||||
fontSize={15.5}
|
||||
fontWeight={900}
|
||||
letterSpacing={0.5}
|
||||
textAnchor="middle"
|
||||
>
|
||||
{"VIEW OUR MENU & ORDER ONLINE"}
|
||||
</Text>
|
||||
<Text
|
||||
x={200}
|
||||
y={176}
|
||||
fill="#222"
|
||||
fontFamily="Inter, Arial, sans-serif"
|
||||
fontSize={9}
|
||||
fontWeight={500}
|
||||
letterSpacing={0.1}
|
||||
textAnchor="middle"
|
||||
>
|
||||
{"Simply scan the QR code with your phone camera to access our"}
|
||||
</Text>
|
||||
<Text
|
||||
x={200}
|
||||
y={189}
|
||||
fill="#222"
|
||||
fontFamily="Inter, Arial, sans-serif"
|
||||
fontSize={9}
|
||||
fontWeight={500}
|
||||
letterSpacing={0.1}
|
||||
textAnchor="middle"
|
||||
>
|
||||
{"digital menu, daily specials, wine list, and place your order."}
|
||||
</Text>
|
||||
<Text
|
||||
x={162}
|
||||
y={468}
|
||||
fill="#122a42"
|
||||
fontFamily="Inter, Arial, sans-serif"
|
||||
fontSize={11}
|
||||
fontWeight={800}
|
||||
letterSpacing={1}
|
||||
textAnchor="end"
|
||||
>
|
||||
{"NO APP"}
|
||||
</Text>
|
||||
<Text
|
||||
x={238}
|
||||
y={468}
|
||||
fill="#122a42"
|
||||
fontFamily="Inter, Arial, sans-serif"
|
||||
fontSize={11}
|
||||
fontWeight={800}
|
||||
letterSpacing={1}
|
||||
>
|
||||
{"REQUIRED"}
|
||||
</Text>
|
||||
<Text
|
||||
x={200}
|
||||
y={515}
|
||||
fill="#122a42"
|
||||
fontFamily="Inter, Arial, sans-serif"
|
||||
fontSize={10.5}
|
||||
fontWeight={800}
|
||||
letterSpacing={1}
|
||||
textAnchor="middle"
|
||||
>
|
||||
{"WWW.THESTONEHEARTHGRILL.COM"}
|
||||
</Text>
|
||||
<Text
|
||||
x={200}
|
||||
y={527}
|
||||
fill="#222"
|
||||
fontFamily="Inter, Arial, sans-serif"
|
||||
fontSize={9}
|
||||
fontWeight={600}
|
||||
letterSpacing={0.2}
|
||||
textAnchor="middle"
|
||||
>
|
||||
{"FREE WIFI: StonehearthGuest (PW: Grill720)"}
|
||||
</Text>
|
||||
<Text
|
||||
x={195}
|
||||
y={540}
|
||||
fill="#222"
|
||||
fontFamily="Inter, Arial, sans-serif"
|
||||
fontSize={9}
|
||||
fontWeight={600}
|
||||
letterSpacing={0.2}
|
||||
textAnchor="middle"
|
||||
>
|
||||
{"Follow Us @StonehearthGrill"}
|
||||
</Text>
|
||||
</Svg>
|
||||
)
|
||||
const Memo = memo(SvgComponent)
|
||||
export default Memo
|
||||
|
After Width: | Height: | Size: 666 KiB |
@@ -0,0 +1,158 @@
|
||||
import Svg, {
|
||||
SvgProps,
|
||||
Path,
|
||||
Image,
|
||||
G,
|
||||
Rect,
|
||||
Circle,
|
||||
Text,
|
||||
} from "react-native-svg"
|
||||
import { memo } from "react"
|
||||
const SvgComponent = (props: SvgProps) => (
|
||||
<Svg width={1024} height={1024} {...props}>
|
||||
<Path fill="#0c4443" d="M0 0h1024v1024H0z" />
|
||||
<Image
|
||||
width={1024}
|
||||
height={1024}
|
||||
href="8.jpeg"
|
||||
preserveAspectRatio="none"
|
||||
/>
|
||||
<G transform="translate(348 305)">
|
||||
<Rect
|
||||
width={328}
|
||||
height={336}
|
||||
fill="#f9f5eb"
|
||||
stroke="#d1a752"
|
||||
strokeWidth={1.5}
|
||||
rx={16}
|
||||
/>
|
||||
<Rect
|
||||
width={304}
|
||||
height={312}
|
||||
x={12}
|
||||
y={12}
|
||||
fill="#faf8f2"
|
||||
stroke="#0c4443"
|
||||
strokeDasharray="6 4"
|
||||
strokeOpacity={0.3}
|
||||
strokeWidth={1.5}
|
||||
rx={12}
|
||||
/>
|
||||
<Circle cx={164} cy={148} r={28} fill="#0c4443" />
|
||||
<G fill="#d1a752">
|
||||
<Path d="M164 132.6c4.4 6.6 4.4 15.4 0 24.2-4.4-8.8-4.4-17.6 0-24.2" />
|
||||
<Path d="M161.8 150.2c-8.8-4.4-13.2 4.4-6.6 8.8 6.6 0 8.8-6.6 6.6-8.8m4.4 0c8.8-4.4 13.2 4.4 6.6 8.8-6.6 0-8.8-6.6-6.6-8.8" />
|
||||
</G>
|
||||
<Text
|
||||
x={164}
|
||||
y={218}
|
||||
fill="#0c4443"
|
||||
fontFamily="Inter, Arial, sans-serif"
|
||||
fontSize={14}
|
||||
fontWeight={800}
|
||||
letterSpacing={2}
|
||||
textAnchor="middle"
|
||||
>
|
||||
{"QR KOD ALANI"}
|
||||
</Text>
|
||||
</G>
|
||||
<Text
|
||||
x={512}
|
||||
y={142}
|
||||
fill="#fff"
|
||||
fontFamily="'Arial Black', 'Montserrat', sans-serif"
|
||||
fontSize={52}
|
||||
fontWeight={900}
|
||||
letterSpacing={2}
|
||||
textAnchor="middle"
|
||||
>
|
||||
{"EXPLORE OUR MENU"}
|
||||
</Text>
|
||||
<Path
|
||||
stroke="#d1a752"
|
||||
strokeLinecap="round"
|
||||
strokeWidth={2}
|
||||
d="M210 188h240"
|
||||
/>
|
||||
<Text
|
||||
x={512}
|
||||
y={194}
|
||||
fill="#d1a752"
|
||||
fontFamily="'Arial Black', 'Montserrat', sans-serif"
|
||||
fontSize={24}
|
||||
fontWeight={900}
|
||||
letterSpacing={2}
|
||||
textAnchor="middle"
|
||||
>
|
||||
{"OR"}
|
||||
</Text>
|
||||
<Path
|
||||
stroke="#d1a752"
|
||||
strokeLinecap="round"
|
||||
strokeWidth={2}
|
||||
d="M574 188h240"
|
||||
/>
|
||||
<Text
|
||||
x={512}
|
||||
y={242}
|
||||
fill="#d1a752"
|
||||
fontFamily="'Arial Black', 'Montserrat', sans-serif"
|
||||
fontSize={44}
|
||||
fontWeight={900}
|
||||
letterSpacing={2}
|
||||
textAnchor="middle"
|
||||
>
|
||||
{"THE DAILY SPECIALS"}
|
||||
</Text>
|
||||
<Text
|
||||
x={512}
|
||||
y={738}
|
||||
fill="#fff"
|
||||
fontFamily="'Arial Black', 'Montserrat', sans-serif"
|
||||
fontSize={28}
|
||||
fontWeight={900}
|
||||
letterSpacing={1}
|
||||
textAnchor="middle"
|
||||
>
|
||||
{"SCAN TO VIEW OUR"}
|
||||
</Text>
|
||||
<Text
|
||||
x={512}
|
||||
y={776}
|
||||
fill="#fff"
|
||||
fontFamily="'Arial Black', 'Montserrat', sans-serif"
|
||||
fontSize={28}
|
||||
fontWeight={900}
|
||||
letterSpacing={1}
|
||||
textAnchor="middle"
|
||||
>
|
||||
{"FULL MENU & SPECIALS"}
|
||||
</Text>
|
||||
<Text
|
||||
x={512}
|
||||
y={842}
|
||||
fill="#d1a752"
|
||||
fontFamily="'Arial Black', 'Montserrat', sans-serif"
|
||||
fontSize={21}
|
||||
fontWeight={900}
|
||||
letterSpacing={1.5}
|
||||
textAnchor="middle"
|
||||
>
|
||||
{"SCAN TO ORDER"}
|
||||
</Text>
|
||||
<Text
|
||||
x={512}
|
||||
y={872}
|
||||
fill="#d1a752"
|
||||
fontFamily="'Arial Black', 'Montserrat', sans-serif"
|
||||
fontSize={21}
|
||||
fontWeight={900}
|
||||
letterSpacing={1.5}
|
||||
textAnchor="middle"
|
||||
>
|
||||
{"& BROWSE"}
|
||||
</Text>
|
||||
</Svg>
|
||||
)
|
||||
const Memo = memo(SvgComponent)
|
||||
export default Memo
|
||||
|
After Width: | Height: | Size: 516 KiB |
@@ -0,0 +1,231 @@
|
||||
import Svg, {
|
||||
SvgProps,
|
||||
Defs,
|
||||
Path,
|
||||
Image,
|
||||
G,
|
||||
Rect,
|
||||
Circle,
|
||||
Text,
|
||||
TextPath,
|
||||
} from "react-native-svg"
|
||||
import { memo } from "react"
|
||||
const SvgComponent = (props: SvgProps) => (
|
||||
<Svg width={1024} height={1024} {...props}>
|
||||
<Defs>
|
||||
<Path id="a" fill="none" d="M330 260q182-115 364 0" />
|
||||
</Defs>
|
||||
<Path fill="#f8f6f0" d="M0 0h1024v1024H0z" />
|
||||
<Image
|
||||
width={1024}
|
||||
height={1024}
|
||||
href="xx.png"
|
||||
preserveAspectRatio="none"
|
||||
/>
|
||||
<G transform="translate(362 428)">
|
||||
<Rect
|
||||
width={300}
|
||||
height={300}
|
||||
fill="#fff"
|
||||
stroke="#d5cebe"
|
||||
strokeWidth={1.5}
|
||||
rx={24}
|
||||
/>
|
||||
<Rect
|
||||
width={276}
|
||||
height={276}
|
||||
x={12}
|
||||
y={12}
|
||||
fill="#fafaf7"
|
||||
stroke="#1e3246"
|
||||
strokeDasharray="6 4"
|
||||
strokeOpacity={0.35}
|
||||
strokeWidth={1.5}
|
||||
rx={16}
|
||||
/>
|
||||
<Circle
|
||||
cx={150}
|
||||
cy={132}
|
||||
r={28}
|
||||
fill="#f5efe6"
|
||||
stroke="#1e3246"
|
||||
strokeWidth={1.5}
|
||||
/>
|
||||
<Path
|
||||
fill="none"
|
||||
stroke="#1e3246"
|
||||
strokeLinecap="round"
|
||||
strokeWidth={1.9}
|
||||
d="M142.4 116.8v11.4c0 3.8 5.7 3.8 5.7 0v-11.4m-2.85 0v11.4m0 3.8v15.2m9.5-30.4c4.75 3.8 4.75 15.2 0 17.1v13.3"
|
||||
/>
|
||||
<Text
|
||||
x={150}
|
||||
y={192}
|
||||
fill="#1e3246"
|
||||
fontFamily="Inter, Arial, sans-serif"
|
||||
fontSize={14}
|
||||
fontWeight={800}
|
||||
letterSpacing={2}
|
||||
textAnchor="middle"
|
||||
>
|
||||
{"QR KOD ALANI"}
|
||||
</Text>
|
||||
</G>
|
||||
<G stroke="#a88b58" transform="translate(290 142)">
|
||||
<Circle cx={-16} cy={-8} r={4.5} fill="none" strokeWidth={1.5} />
|
||||
<Path strokeLinecap="round" strokeWidth={1.5} d="M-16-12v8m-4-4h8" />
|
||||
<Path
|
||||
fill="none"
|
||||
strokeLinecap="round"
|
||||
strokeWidth={1.5}
|
||||
d="M0-12c4 0 8 6 8 12S4 12 0 12"
|
||||
/>
|
||||
<Path
|
||||
fill="none"
|
||||
strokeLinecap="round"
|
||||
strokeWidth={1.2}
|
||||
d="M-32 20c12 8 52 8 64 0"
|
||||
/>
|
||||
</G>
|
||||
<G stroke="#1e3246">
|
||||
<Path
|
||||
fill="none"
|
||||
strokeLinecap="round"
|
||||
strokeWidth={3}
|
||||
d="M390 780c-16-8-16-26-8-36"
|
||||
/>
|
||||
<Path
|
||||
fill="#1e3246"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={1.5}
|
||||
d="m374 750 8-6 6 8Z"
|
||||
/>
|
||||
</G>
|
||||
<G stroke="#1e3246">
|
||||
<Path
|
||||
fill="none"
|
||||
strokeLinecap="round"
|
||||
strokeWidth={3}
|
||||
d="M634 780c16-8 16-26 8-36"
|
||||
/>
|
||||
<Path
|
||||
fill="#1e3246"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={1.5}
|
||||
d="m650 750-8-6-6 8Z"
|
||||
/>
|
||||
</G>
|
||||
<Text
|
||||
x={290}
|
||||
y={164}
|
||||
fill="#1e3246"
|
||||
fontFamily="'Arial Black', 'Impact', sans-serif"
|
||||
fontSize={16}
|
||||
fontWeight={900}
|
||||
letterSpacing={1}
|
||||
textAnchor="middle"
|
||||
>
|
||||
{"TABLE MENU"}
|
||||
</Text>
|
||||
<Text
|
||||
x={290}
|
||||
y={180}
|
||||
fill="#8c7348"
|
||||
fontFamily="Georgia, serif"
|
||||
fontSize={9}
|
||||
fontStyle="italic"
|
||||
fontWeight={700}
|
||||
letterSpacing={1}
|
||||
textAnchor="middle"
|
||||
>
|
||||
{"EST. 2023"}
|
||||
</Text>
|
||||
<Text
|
||||
x={364}
|
||||
y={248}
|
||||
fill="#1e3246"
|
||||
fontFamily="'Arial Black', 'Impact', sans-serif"
|
||||
fontSize={23}
|
||||
fontWeight={900}
|
||||
letterSpacing={1}
|
||||
textAnchor="middle"
|
||||
transform="rotate(-26 364 248)"
|
||||
>
|
||||
{"SIAT"}
|
||||
</Text>
|
||||
<Text
|
||||
fill="#1e3246"
|
||||
fontFamily="'Arial Black', 'Impact', sans-serif"
|
||||
fontSize={52}
|
||||
fontWeight={900}
|
||||
letterSpacing={2}
|
||||
>
|
||||
<TextPath href="#a" startOffset="50%" textAnchor="middle">
|
||||
{"\n SCAN TO\n "}
|
||||
</TextPath>
|
||||
</Text>
|
||||
<Text
|
||||
x={670}
|
||||
y={244}
|
||||
fill="#1e3246"
|
||||
fontFamily="'Arial Black', 'Impact', sans-serif"
|
||||
fontSize={25}
|
||||
fontWeight={900}
|
||||
letterSpacing={1}
|
||||
textAnchor="middle"
|
||||
transform="rotate(24 670 244)"
|
||||
>
|
||||
{"VIEW"}
|
||||
</Text>
|
||||
<Text
|
||||
x={512}
|
||||
y={320}
|
||||
fill="#1e3246"
|
||||
fontFamily="'Arial Black', 'Impact', sans-serif"
|
||||
fontSize={82}
|
||||
fontWeight={900}
|
||||
letterSpacing={3}
|
||||
textAnchor="middle"
|
||||
>
|
||||
{"OUR MENU"}
|
||||
</Text>
|
||||
<Text
|
||||
x={512}
|
||||
y={748}
|
||||
fill="#1e3246"
|
||||
fontFamily="'Arial Black', sans-serif"
|
||||
fontSize={18.5}
|
||||
fontWeight={900}
|
||||
letterSpacing={1}
|
||||
textAnchor="middle"
|
||||
>
|
||||
{"VIEW OUR"}
|
||||
</Text>
|
||||
<Text
|
||||
x={512}
|
||||
y={774}
|
||||
fill="#1e3246"
|
||||
fontFamily="'Arial Black', sans-serif"
|
||||
fontSize={18.5}
|
||||
fontWeight={900}
|
||||
letterSpacing={1}
|
||||
textAnchor="middle"
|
||||
>
|
||||
{"DELICIOUS MENU"}
|
||||
</Text>
|
||||
<Text
|
||||
x={500}
|
||||
y={894}
|
||||
fill="#1e3246"
|
||||
fontFamily="'Arial Black', 'Montserrat', sans-serif"
|
||||
fontSize={18}
|
||||
fontWeight={900}
|
||||
letterSpacing={1.5}
|
||||
textAnchor="middle"
|
||||
>
|
||||
{"WELCOME TO OUR TABLE"}
|
||||
</Text>
|
||||
</Svg>
|
||||
)
|
||||
const Memo = memo(SvgComponent)
|
||||
export default Memo
|
||||
|
Before Width: | Height: | Size: 1.6 MiB After Width: | Height: | Size: 4.5 KiB |
|
Before Width: | Height: | Size: 357 KiB After Width: | Height: | Size: 4.5 KiB |
@@ -0,0 +1,71 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 400 650" width="400" height="650">
|
||||
<defs>
|
||||
<!-- Define a linear gradient for gold effect -->
|
||||
<linearGradient id="goldGradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" stop-color="#D4AF37"/>
|
||||
<stop offset="100%" stop-color="#FFD700"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<!-- Background Canvas -->
|
||||
<rect id="bg-canvas" fill="#451A03" width="400" height="650"/>
|
||||
<!-- Main Menu Card Panel -->
|
||||
<rect id="main-card" x="20" y="20" width="360" height="610" rx="24" fill="#000000" stroke="url(#goldGradient)" stroke-width="2"/>
|
||||
<!-- Layer for Icons/Graphics -->
|
||||
<g id="layer-icons">
|
||||
<!-- Top decorative line -->
|
||||
<line x1="80" y1="165" x2="320" y2="165" stroke="url(#goldGradient)" stroke-width="1" fill="#D97706"/>
|
||||
<!-- Bottom decorative line -->
|
||||
<line x1="80" y1="535" x2="320" y2="535" stroke="url(#goldGradient)" stroke-width="1" fill="#B45309"/>
|
||||
<!-- Subtle corner accents (L-shapes) -->
|
||||
<path d="M40 40 L40 70 M40 40 L70 40" stroke="url(#goldGradient)" stroke-width="1.5" stroke-linecap="round" fill="#D97706"/>
|
||||
<path d="M360 40 L360 70 M360 40 L330 40" stroke="url(#goldGradient)" stroke-width="1.5" stroke-linecap="round" fill="#B45309"/>
|
||||
<path d="M40 610 L40 580 M40 610 L70 610" stroke="url(#goldGradient)" stroke-width="1.5" stroke-linecap="round" fill="#D97706"/>
|
||||
<path d="M360 610 L360 580 M360 610 L330 610" stroke="url(#goldGradient)" stroke-width="1.5" stroke-linecap="round" fill="#B45309"/>
|
||||
<!-- Minimalist Fork and Knife Line Art -->
|
||||
<g transform="translate(0, 15)">
|
||||
<!-- Fork -->
|
||||
<path d="M185 550 L185 560 M188 550 L188 560 M191 550 L191 560 M188 560 L188 565" stroke="url(#goldGradient)" stroke-width="1.5" stroke-linecap="round" fill="#D97706"/>
|
||||
<!-- Knife -->
|
||||
<path d="M205 550 L205 560 L208 560 L208 552 C208 550 206.5 549 205 549 Z M206.5 560 L206.5 565" stroke="url(#goldGradient)" stroke-width="1.5" stroke-linecap="round" fill="#B45309"/>
|
||||
</g>
|
||||
</g>
|
||||
<!-- QR Placeholder -->
|
||||
<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>
|
||||
<!-- Layer for Texts -->
|
||||
<g id="layer-texts" fill="#c10101">
|
||||
<!-- Table Info badge -->
|
||||
<rect x="140" y="40" width="120" height="30" rx="15" fill="url(#goldGradient)"/>
|
||||
<text x="200" y="60" font-family="Inter, sans-serif" font-size="14" font-weight="600" fill="#1A1A1D" text-anchor="middle">
|
||||
MASA NO: 01
|
||||
</text>
|
||||
<!-- Restaurant Name -->
|
||||
<text x="200" y="120" font-family="Inter, sans-serif" font-size="26" font-weight="700" fill="url(#goldGradient)" text-anchor="middle">
|
||||
Gourmet Bistro
|
||||
</text>
|
||||
<!-- Tagline / Subtitle -->
|
||||
<text x="200" y="145" font-family="Inter, sans-serif" font-size="12" font-weight="400" fill="#E4E4E7" text-anchor="middle">
|
||||
Temassız Dijital Menü
|
||||
</text>
|
||||
<!-- "MENÜYÜ GÖRMEK İÇİN OKUTUN" -->
|
||||
<text x="200" y="480" font-family="Inter, sans-serif" font-size="16" font-weight="700" fill="url(#goldGradient)" text-anchor="middle">
|
||||
MENÜYÜ GÖRMEK İÇİN OKUTUN
|
||||
</text>
|
||||
<!-- Instruction note -->
|
||||
<text x="200" y="505" font-family="Inter, sans-serif" font-size="11" font-weight="400" fill="#A1A1AA" text-anchor="middle">
|
||||
Kameranızı QR Koda Odaklayın
|
||||
</text>
|
||||
<!-- WiFi & Social Media footer info -->
|
||||
<text x="200" y="595" font-family="Inter, sans-serif" font-size="11" font-weight="400" fill="#A1A1AA" text-anchor="middle">
|
||||
Ücretsiz Wi-Fi: GourmetBistro_Guest
|
||||
</text>
|
||||
<text x="200" y="610" font-family="Inter, sans-serif" font-size="11" font-weight="400" fill="#A1A1AA" text-anchor="middle">
|
||||
Bizi Takip Edin: @GourmetBistro
|
||||
</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.0 KiB |
@@ -0,0 +1,285 @@
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 400 650"
|
||||
width="400"
|
||||
height="650"
|
||||
>
|
||||
<g id="layer-background">
|
||||
<rect
|
||||
id="bg-canvas"
|
||||
x="0"
|
||||
y="0"
|
||||
width="400"
|
||||
height="650"
|
||||
fill="#0B0E14"
|
||||
/>
|
||||
</g>
|
||||
|
||||
<g id="layer-card">
|
||||
<!-- Ana Lüks Kart Arka Planı (Gece Yarısı Laciverti & Kömür) -->
|
||||
<rect
|
||||
id="main-card"
|
||||
x="20"
|
||||
y="20"
|
||||
width="360"
|
||||
height="610"
|
||||
rx="24"
|
||||
fill="#121721"
|
||||
stroke="#D4AF37"
|
||||
stroke-width="1.5"
|
||||
/>
|
||||
|
||||
<!-- İç Çift Altın Bordür Hattı -->
|
||||
<rect
|
||||
id="inner-border"
|
||||
x="28"
|
||||
y="28"
|
||||
width="344"
|
||||
height="594"
|
||||
rx="18"
|
||||
fill="none"
|
||||
stroke="#C5A059"
|
||||
stroke-width="0.8"
|
||||
stroke-opacity="0.4"
|
||||
/>
|
||||
<rect
|
||||
x="32"
|
||||
y="32"
|
||||
width="336"
|
||||
height="586"
|
||||
rx="14"
|
||||
fill="none"
|
||||
stroke="#C5A059"
|
||||
stroke-width="0.5"
|
||||
stroke-opacity="0.2"
|
||||
/>
|
||||
</g>
|
||||
|
||||
<g id="layer-icons">
|
||||
<!-- Üst Köşe Geometrik Art-Deco Motifleri -->
|
||||
<path
|
||||
d="M 38 48 L 48 38 M 38 38 L 52 38 M 38 38 L 38 52"
|
||||
stroke="#D4AF37"
|
||||
stroke-width="1"
|
||||
stroke-linecap="round"
|
||||
stroke-opacity="0.6"
|
||||
fill="none"
|
||||
/>
|
||||
<path
|
||||
d="M 362 48 L 352 38 M 362 38 L 348 38 M 362 38 L 362 52"
|
||||
stroke="#D4AF37"
|
||||
stroke-width="1"
|
||||
stroke-linecap="round"
|
||||
stroke-opacity="0.6"
|
||||
fill="none"
|
||||
/>
|
||||
|
||||
<!-- Üst Masa Numarası Kapsülü -->
|
||||
<rect
|
||||
x="148"
|
||||
y="44"
|
||||
width="104"
|
||||
height="22"
|
||||
rx="11"
|
||||
fill="#1A2230"
|
||||
stroke="#D4AF37"
|
||||
stroke-width="0.8"
|
||||
/>
|
||||
<circle cx="158" cy="55" r="1.5" fill="#D4AF37" />
|
||||
<circle cx="242" cy="55" r="1.5" fill="#D4AF37" />
|
||||
|
||||
<!-- Merkezi Gastronomi & Yıldız Amblemi -->
|
||||
<g transform="translate(200, 84)">
|
||||
<circle cx="0" cy="0" r="11" fill="none" stroke="#D4AF37" stroke-width="0.8" stroke-dasharray="2 2" />
|
||||
<polygon points="0,-7 2,-2 7,0 2,2 0,7 -2,2 -7,0 -2,-2" fill="#D4AF37" />
|
||||
</g>
|
||||
|
||||
<!-- Zarif Tipografik Ayırıcı -->
|
||||
<line x1="75" y1="172" x2="175" y2="172" stroke="#D4AF37" stroke-width="0.75" stroke-opacity="0.5" />
|
||||
<polygon points="200,169.5 202.5,172 200,174.5 197.5,172" fill="#D4AF37" />
|
||||
<line x1="225" y1="172" x2="325" y2="172" stroke="#D4AF37" stroke-width="0.75" stroke-opacity="0.5" />
|
||||
|
||||
<!-- QR Alanı Hedef Odak Çerçeveleri -->
|
||||
<path d="M 80 205 L 80 195 L 90 195" fill="none" stroke="#D4AF37" stroke-width="2" stroke-linecap="round" />
|
||||
<path d="M 320 205 L 320 195 L 310 195" fill="none" stroke="#D4AF37" stroke-width="2" stroke-linecap="round" />
|
||||
<path d="M 80 445 L 80 455 L 90 455" fill="none" stroke="#D4AF37" stroke-width="2" stroke-linecap="round" />
|
||||
<path d="M 320 445 L 320 455 L 310 455" fill="none" stroke="#D4AF37" stroke-width="2" stroke-linecap="round" />
|
||||
|
||||
<!-- Alt Bilgi Kartı Paneli -->
|
||||
<rect
|
||||
x="36"
|
||||
y="520"
|
||||
width="328"
|
||||
height="84"
|
||||
rx="14"
|
||||
fill="#171F2C"
|
||||
stroke="#D4AF37"
|
||||
stroke-width="0.8"
|
||||
stroke-opacity="0.4"
|
||||
/>
|
||||
<line x1="200" y1="530" x2="200" y2="594" stroke="#D4AF37" stroke-width="0.5" stroke-opacity="0.25" />
|
||||
|
||||
<!-- Minimal WiFi Vektörü -->
|
||||
<g transform="translate(62, 545)">
|
||||
<path d="M 0 4 A 12 12 0 0 1 18 4" fill="none" stroke="#D4AF37" stroke-width="1.4" stroke-linecap="round" />
|
||||
<path d="M 3.5 8 A 7 7 0 0 1 14.5 8" fill="none" stroke="#D4AF37" stroke-width="1.4" stroke-linecap="round" />
|
||||
<circle cx="9" cy="12" r="1.2" fill="#D4AF37" />
|
||||
</g>
|
||||
|
||||
<!-- Minimal Instagram / Web Vektörü -->
|
||||
<g transform="translate(224, 545)">
|
||||
<rect x="0" y="0" width="15" height="15" rx="4" fill="none" stroke="#D4AF37" stroke-width="1.4" />
|
||||
<circle cx="7.5" cy="7.5" r="3.2" fill="none" stroke="#D4AF37" stroke-width="1.1" />
|
||||
<circle cx="11.5" cy="3.5" r="0.75" fill="#D4AF37" />
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<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, Arial, sans-serif"
|
||||
font-size="12"
|
||||
font-weight="600"
|
||||
fill="#71717A"
|
||||
text-anchor="middle"
|
||||
>QR KOD ALANI</text>
|
||||
</g>
|
||||
|
||||
<g id="layer-texts">
|
||||
<!-- Masa Numarası -->
|
||||
<text
|
||||
x="200"
|
||||
y="59"
|
||||
font-family="Inter, Arial, sans-serif"
|
||||
font-size="9"
|
||||
font-weight="700"
|
||||
letter-spacing="2.5"
|
||||
fill="#E5C378"
|
||||
text-anchor="middle"
|
||||
>TABLE • 01</text>
|
||||
|
||||
<!-- Restoran Logosu & İsmi -->
|
||||
<text
|
||||
x="200"
|
||||
y="126"
|
||||
font-family="'Playfair Display', 'Cinzel', Georgia, serif"
|
||||
font-size="28"
|
||||
font-weight="700"
|
||||
letter-spacing="1.5"
|
||||
fill="#F9F6F0"
|
||||
text-anchor="middle"
|
||||
>L’ATELIER BISTRO</text>
|
||||
|
||||
<!-- Fransızca / Uluslararası Alt Başlık -->
|
||||
<text
|
||||
x="200"
|
||||
y="152"
|
||||
font-family="Inter, Arial, sans-serif"
|
||||
font-size="8.5"
|
||||
font-weight="600"
|
||||
letter-spacing="4"
|
||||
fill="#D4AF37"
|
||||
text-anchor="middle"
|
||||
>FINE DINING & COCKTAILS</text>
|
||||
|
||||
<!-- Çağrı Metni / Talimat -->
|
||||
<text
|
||||
x="200"
|
||||
y="480"
|
||||
font-family="Inter, Arial, sans-serif"
|
||||
font-size="11.5"
|
||||
font-weight="700"
|
||||
letter-spacing="2"
|
||||
fill="#F9F6F0"
|
||||
text-anchor="middle"
|
||||
>DİJİTAL MENÜ İÇİN OKUTUN</text>
|
||||
|
||||
<text
|
||||
x="200"
|
||||
y="498"
|
||||
font-family="'Playfair Display', Georgia, serif"
|
||||
font-size="10.5"
|
||||
font-style="italic"
|
||||
font-weight="400"
|
||||
letter-spacing="0.5"
|
||||
fill="#94A3B8"
|
||||
text-anchor="middle"
|
||||
>Kameranızı QR koda yaklaştırınız</text>
|
||||
|
||||
<!-- Sol Sütun: Yüksek Hızlı WiFi Bilgileri -->
|
||||
<text
|
||||
x="126"
|
||||
y="550"
|
||||
font-family="Inter, Arial, sans-serif"
|
||||
font-size="9"
|
||||
font-weight="700"
|
||||
letter-spacing="1.2"
|
||||
fill="#E5C378"
|
||||
text-anchor="middle"
|
||||
>GUEST WIFI</text>
|
||||
|
||||
<text
|
||||
x="126"
|
||||
y="567"
|
||||
font-family="Inter, Arial, sans-serif"
|
||||
font-size="8.5"
|
||||
font-weight="500"
|
||||
fill="#CBD5E1"
|
||||
text-anchor="middle"
|
||||
>Ağ: Atelier-Guest</text>
|
||||
|
||||
<text
|
||||
x="126"
|
||||
y="582"
|
||||
font-family="Inter, Arial, sans-serif"
|
||||
font-size="8.5"
|
||||
font-weight="500"
|
||||
fill="#CBD5E1"
|
||||
text-anchor="middle"
|
||||
>Şifre: bistro2026</text>
|
||||
|
||||
<!-- Sağ Sütun: Sosyal Medya & Rezervasyon -->
|
||||
<text
|
||||
x="286"
|
||||
y="550"
|
||||
font-family="Inter, Arial, sans-serif"
|
||||
font-size="8.5"
|
||||
font-weight="700"
|
||||
letter-spacing="1.2"
|
||||
fill="#E5C378"
|
||||
text-anchor="middle"
|
||||
>SOSYAL MEDYA</text>
|
||||
|
||||
<text
|
||||
x="286"
|
||||
y="575"
|
||||
font-family="Inter, Arial, sans-serif"
|
||||
font-size="10.5"
|
||||
font-weight="600"
|
||||
letter-spacing="0.5"
|
||||
fill="#F9F6F0"
|
||||
text-anchor="middle"
|
||||
>@latelier.bistro</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 7.1 KiB |
@@ -0,0 +1,275 @@
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 400 650"
|
||||
width="400"
|
||||
height="650"
|
||||
>
|
||||
<g id="layer-background">
|
||||
<!-- Koyu Doku / Kontrast Arka Plan -->
|
||||
<rect
|
||||
id="bg-canvas"
|
||||
x="0"
|
||||
y="0"
|
||||
width="400"
|
||||
height="650"
|
||||
fill="#252C36"
|
||||
/>
|
||||
</g>
|
||||
|
||||
<g id="layer-card">
|
||||
<!-- Sıcak Parşömen / Vintage Krem Ana Kart -->
|
||||
<rect
|
||||
id="main-card"
|
||||
x="20"
|
||||
y="20"
|
||||
width="360"
|
||||
height="610"
|
||||
rx="20"
|
||||
fill="#F6EFE2"
|
||||
stroke="#3A2312"
|
||||
stroke-width="1.5"
|
||||
/>
|
||||
<!-- Çift İnce Vintage Çerçeve -->
|
||||
<rect
|
||||
x="28"
|
||||
y="28"
|
||||
width="344"
|
||||
height="594"
|
||||
rx="14"
|
||||
fill="none"
|
||||
stroke="#8C6239"
|
||||
stroke-width="1"
|
||||
stroke-opacity="0.4"
|
||||
/>
|
||||
<rect
|
||||
x="32"
|
||||
y="32"
|
||||
width="336"
|
||||
height="586"
|
||||
rx="10"
|
||||
fill="none"
|
||||
stroke="#8C6239"
|
||||
stroke-width="0.5"
|
||||
stroke-opacity="0.25"
|
||||
/>
|
||||
</g>
|
||||
|
||||
<g id="layer-icons">
|
||||
<!-- Sol Üst: Botanik Kahve / Çay Dalı -->
|
||||
<g transform="translate(36, 36) scale(0.75)" stroke="#3A2312" stroke-width="1.4" fill="none" stroke-linecap="round">
|
||||
<path d="M 0 0 C 15 15 25 35 30 65" />
|
||||
<path d="M 10 12 C 5 2 20 -2 22 8 C 22 18 10 12 10 12 Z" fill="#EADCC7" />
|
||||
<path d="M 18 27 C 12 20 28 14 32 23 C 32 32 18 27 18 27 Z" fill="#EADCC7" />
|
||||
<path d="M 24 45 C 32 38 45 48 38 56 C 30 62 24 45 24 45 Z" fill="#EADCC7" />
|
||||
</g>
|
||||
|
||||
<!-- Sağ Üst: Kuşbakışı Latte Art Fincanı -->
|
||||
<g transform="translate(340, 52)">
|
||||
<!-- Tabak -->
|
||||
<circle cx="0" cy="0" r="32" fill="#EADCC7" stroke="#3A2312" stroke-width="1.2" />
|
||||
<circle cx="0" cy="0" r="26" fill="#F6EFE2" stroke="#8C6239" stroke-width="0.8" stroke-dasharray="2 2" />
|
||||
<!-- Fincan Kenarı & Kahve -->
|
||||
<circle cx="0" cy="0" r="21" fill="#7A4B26" stroke="#3A2312" stroke-width="1.4" />
|
||||
<!-- Latte Art / Rosetta Yaprakları -->
|
||||
<path d="M 0 10 C -8 4 -4 -6 0 -12 C 4 -6 8 4 0 10 Z" fill="#F6EFE2" />
|
||||
<path d="M 0 4 C -5 0 -2 -5 0 -8 C 2 -5 5 0 0 4 Z" fill="#7A4B26" />
|
||||
<circle cx="0" cy="-14" r="1.8" fill="#F6EFE2" />
|
||||
</g>
|
||||
|
||||
<!-- Sol Orta: Küçük Kahve Fincanı & Tabağı -->
|
||||
<g transform="translate(48, 250)">
|
||||
<ellipse cx="0" cy="0" rx="26" ry="26" fill="#EADCC7" stroke="#3A2312" stroke-width="1" />
|
||||
<circle cx="0" cy="0" r="17" fill="#6A3B18" stroke="#3A2312" stroke-width="1.2" />
|
||||
<!-- Basit Kalp Latte Deseni -->
|
||||
<path d="M -5 -3 C -8 -7 0 -10 0 -4 C 0 -10 8 -7 5 -3 C 2 3 0 7 0 7 C 0 7 -2 3 -5 -3 Z" fill="#F6EFE2" />
|
||||
<!-- Fincan Kulpu -->
|
||||
<path d="M 17 -4 C 23 -4 23 4 17 4" fill="none" stroke="#3A2312" stroke-width="1.5" />
|
||||
</g>
|
||||
|
||||
<!-- Sağ Orta: Dikey Yaprak İllüstrasyonu -->
|
||||
<g transform="translate(345, 230) scale(0.7)" stroke="#3A2312" stroke-width="1.3" fill="none">
|
||||
<path d="M 10 0 C 0 30 -5 70 5 110" />
|
||||
<path d="M 6 25 C 18 15 28 28 14 36 C 6 36 6 25 6 25 Z" fill="#EADCC7" />
|
||||
<path d="M 2 60 C -12 48 -20 62 -8 70 C 2 70 2 60 2 60 Z" fill="#EADCC7" />
|
||||
<path d="M 5 88 C 18 78 24 92 12 100 C 5 100 5 88 5 88 Z" fill="#EADCC7" />
|
||||
</g>
|
||||
|
||||
<!-- Üst Başlık Yan Süsleme Çizgileri -->
|
||||
<path d="M 142 66 C 148 64 154 68 160 66" fill="none" stroke="#8C6239" stroke-width="1.2" stroke-linecap="round" />
|
||||
<path d="M 240 66 C 246 68 252 64 258 66" fill="none" stroke="#8C6239" stroke-width="1.2" stroke-linecap="round" />
|
||||
|
||||
<!-- Sol Alt: Çekirdek ve Yaprak Grubu -->
|
||||
<g transform="translate(42, 530) scale(0.65)" stroke="#3A2312" stroke-width="1.4" fill="none">
|
||||
<path d="M 0 110 C 20 70 15 30 0 0" />
|
||||
<path d="M 8 30 C 24 15 35 30 18 42 Z" fill="#EADCC7" />
|
||||
<path d="M 12 70 C 30 55 40 72 20 84 Z" fill="#EADCC7" />
|
||||
</g>
|
||||
|
||||
<!-- Kahve Çekirdekleri Motifleri -->
|
||||
<!-- Sol Alt Çekirdek 1 -->
|
||||
<g transform="translate(108, 595) rotate(-20) scale(0.8)">
|
||||
<ellipse cx="0" cy="0" rx="9" ry="6.5" fill="#CBB296" stroke="#3A2312" stroke-width="1.2" />
|
||||
<path d="M -7 0 Q 0 3 0 0 Q 0 -3 7 0" fill="none" stroke="#3A2312" stroke-width="1.2" />
|
||||
</g>
|
||||
<!-- Sol Alt Çekirdek 2 -->
|
||||
<g transform="translate(128, 584) rotate(35) scale(0.8)">
|
||||
<ellipse cx="0" cy="0" rx="9" ry="6.5" fill="#CBB296" stroke="#3A2312" stroke-width="1.2" />
|
||||
<path d="M -7 0 Q 0 3 0 0 Q 0 -3 7 0" fill="none" stroke="#3A2312" stroke-width="1.2" />
|
||||
</g>
|
||||
<!-- Sağ Orta Çekirdek -->
|
||||
<g transform="translate(336, 360) rotate(-45) scale(0.7)">
|
||||
<ellipse cx="0" cy="0" rx="9" ry="6.5" fill="#CBB296" stroke="#3A2312" stroke-width="1.2" />
|
||||
<path d="M -7 0 Q 0 3 0 0 Q 0 -3 7 0" fill="none" stroke="#3A2312" stroke-width="1.2" />
|
||||
</g>
|
||||
|
||||
<!-- Sağ Alt: Sıcak Kahve Fincanı İllüstrasyonu -->
|
||||
<g transform="translate(325, 590)">
|
||||
<!-- Tabak -->
|
||||
<ellipse cx="0" cy="18" rx="28" ry="7" fill="#EADCC7" stroke="#3A2312" stroke-width="1.2" />
|
||||
<!-- Fincan Gövdesi -->
|
||||
<path d="M -18 2 C -18 16 18 16 18 2 Z" fill="#F6EFE2" stroke="#3A2312" stroke-width="1.4" />
|
||||
<ellipse cx="0" cy="2" rx="18" ry="4" fill="#6A3B18" stroke="#3A2312" stroke-width="1.2" />
|
||||
<!-- Kulp -->
|
||||
<path d="M 16 4 C 24 4 24 12 16 14" fill="none" stroke="#3A2312" stroke-width="1.3" />
|
||||
<!-- Buhar Çizgileri -->
|
||||
<path d="M -6 -4 C -8 -9 -4 -12 -6 -17" fill="none" stroke="#8C6239" stroke-width="1" stroke-linecap="round" />
|
||||
<path d="M 2 -5 C 0 -10 4 -13 2 -19" fill="none" stroke="#8C6239" stroke-width="1" stroke-linecap="round" />
|
||||
</g>
|
||||
|
||||
<!-- QR Alanı İnce Köşe Süsleri -->
|
||||
<path d="M 80 205 C 80 195 80 195 90 195" fill="none" stroke="#3A2312" stroke-width="2" stroke-linecap="round" />
|
||||
<path d="M 320 205 C 320 195 320 195 310 195" fill="none" stroke="#3A2312" stroke-width="2" stroke-linecap="round" />
|
||||
<path d="M 80 445 C 80 455 80 455 90 455" fill="none" stroke="#3A2312" stroke-width="2" stroke-linecap="round" />
|
||||
<path d="M 320 445 C 320 455 320 455 310 455" fill="none" stroke="#3A2312" stroke-width="2" stroke-linecap="round" />
|
||||
|
||||
<!-- Alt Est. Bölümü Çizgileri -->
|
||||
<line x1="145" y1="608" x2="175" y2="608" stroke="#8C6239" stroke-width="0.8" />
|
||||
<line x1="225" y1="608" x2="255" y2="608" stroke="#8C6239" stroke-width="0.8" />
|
||||
</g>
|
||||
|
||||
<g id="qr-placeholder" transform="translate(90, 215)">
|
||||
<!-- QR Kod Arka Planı (Hafif Sıcak Ton) -->
|
||||
<rect
|
||||
width="220"
|
||||
height="220"
|
||||
rx="20"
|
||||
fill="#FBF8F2"
|
||||
stroke="#D8C7B0"
|
||||
stroke-width="1.5"
|
||||
/>
|
||||
<rect
|
||||
x="10"
|
||||
y="10"
|
||||
width="200"
|
||||
height="200"
|
||||
rx="14"
|
||||
fill="#F5EFE6"
|
||||
stroke="#D8C7B0"
|
||||
stroke-dasharray="4 3"
|
||||
/>
|
||||
<!-- QR Merkezine Kahve Çekirdeği İkonu Rozeti -->
|
||||
<circle cx="110" cy="98" r="18" fill="#FBF8F2" stroke="#8C6239" stroke-width="1" />
|
||||
<g transform="translate(110, 98) scale(0.65)">
|
||||
<ellipse cx="-5" cy="2" rx="7" ry="5" fill="#3A2312" transform="rotate(-30)" />
|
||||
<ellipse cx="5" cy="0" rx="7" ry="5" fill="#3A2312" transform="rotate(40)" />
|
||||
</g>
|
||||
<text
|
||||
x="110"
|
||||
y="132"
|
||||
font-family="Inter, Arial, sans-serif"
|
||||
font-size="11"
|
||||
font-weight="700"
|
||||
letter-spacing="1"
|
||||
fill="#8C6239"
|
||||
text-anchor="middle"
|
||||
>QR KOD ALANI</text>
|
||||
</g>
|
||||
|
||||
<g id="layer-texts">
|
||||
<!-- Üst Başlık Takısı -->
|
||||
<text
|
||||
x="200"
|
||||
y="70"
|
||||
font-family="'Playfair Display', Georgia, serif"
|
||||
font-size="14"
|
||||
font-style="italic"
|
||||
font-weight="700"
|
||||
letter-spacing="4"
|
||||
fill="#3A2312"
|
||||
text-anchor="middle"
|
||||
>THE</text>
|
||||
|
||||
<!-- Ana Tipografi: El Yazısı / Retro Tabela Havası -->
|
||||
<text
|
||||
x="200"
|
||||
y="114"
|
||||
font-family="'Playfair Display', 'Brush Script MT', 'Great Vibes', Georgia, cursive, serif"
|
||||
font-size="34"
|
||||
font-weight="800"
|
||||
letter-spacing="0.5"
|
||||
fill="#2C1A0E"
|
||||
text-anchor="middle"
|
||||
>The Daily Grind</text>
|
||||
|
||||
<!-- Alt Kategori Başlığı -->
|
||||
<text
|
||||
x="200"
|
||||
y="146"
|
||||
font-family="Inter, Arial, sans-serif"
|
||||
font-size="13"
|
||||
font-weight="800"
|
||||
letter-spacing="3"
|
||||
fill="#3A2312"
|
||||
text-anchor="middle"
|
||||
>COFFEE & KITCHEN</text>
|
||||
|
||||
<!-- Büyük ve Belirgin Çağrı Metni -->
|
||||
<text
|
||||
x="200"
|
||||
y="488"
|
||||
font-family="Inter, Arial, sans-serif"
|
||||
font-size="19"
|
||||
font-weight="900"
|
||||
letter-spacing="1.5"
|
||||
fill="#2C1A0E"
|
||||
text-anchor="middle"
|
||||
>SCAN TO VIEW MENU</text>
|
||||
|
||||
<!-- Restoran & WiFi Bilgi Bandı -->
|
||||
<text
|
||||
x="200"
|
||||
y="520"
|
||||
font-family="Inter, Arial, sans-serif"
|
||||
font-size="10.5"
|
||||
font-weight="700"
|
||||
letter-spacing="0.5"
|
||||
fill="#3A2312"
|
||||
text-anchor="middle"
|
||||
>DAILY GRIND CAFE | WIFI: DAILY_GRIND_GUEST</text>
|
||||
|
||||
<!-- Şifre ve Masa Detayı -->
|
||||
<text
|
||||
x="200"
|
||||
y="542"
|
||||
font-family="Inter, Arial, sans-serif"
|
||||
font-size="9"
|
||||
font-weight="600"
|
||||
letter-spacing="1"
|
||||
fill="#8C6239"
|
||||
text-anchor="middle"
|
||||
>ŞİFRE: coffee2026 • MASA NO: 04</text>
|
||||
|
||||
<!-- Kuruluş / Est. Yılı -->
|
||||
<text
|
||||
x="200"
|
||||
y="611"
|
||||
font-family="'Playfair Display', Georgia, serif"
|
||||
font-size="11"
|
||||
font-style="italic"
|
||||
font-weight="700"
|
||||
letter-spacing="2"
|
||||
fill="#3A2312"
|
||||
text-anchor="middle"
|
||||
>EST. 2018</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 9.3 KiB |
@@ -0,0 +1,147 @@
|
||||
import type { Metadata } from "next";
|
||||
import Link from "next/link";
|
||||
import React from "react";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Menulio Admin Panel | Şablon & Sistem Yönetimi",
|
||||
description: "Menulio QR Şablon Stüdyosu, Masa Standı Tasarım Motoru ve Sistem Yönetimi",
|
||||
};
|
||||
|
||||
export default function AdminLayout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<div className="min-h-screen bg-[#09090b] text-[#fafafa] flex flex-col md:flex-row antialiased selection:bg-[#D4AF37]/30 selection:text-[#D4AF37]">
|
||||
{/* Sol Sidebar */}
|
||||
<aside className="w-full md:w-72 bg-[#121215] border-b md:border-b-0 md:border-r border-white/10 flex flex-col justify-between shrink-0">
|
||||
<div>
|
||||
{/* Logo & Başlık */}
|
||||
<div className="p-6 border-b border-white/10 flex items-center justify-between">
|
||||
<Link href="/admin" className="flex items-center gap-3 group">
|
||||
<div className="w-9 h-9 rounded-xl bg-gradient-to-tr from-[#D4AF37] to-[#F59E0B] flex items-center justify-center shadow-lg shadow-[#D4AF37]/20">
|
||||
<span className="font-extrabold text-[#0C0C0E] text-lg tracking-wider">M</span>
|
||||
</div>
|
||||
<div>
|
||||
<div className="font-bold text-white tracking-wide text-base group-hover:text-[#D4AF37] transition-colors">
|
||||
MENULIO
|
||||
</div>
|
||||
<div className="text-[10px] font-semibold text-[#D4AF37] tracking-widest uppercase">
|
||||
Admin Stüdyosu
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{/* Navigasyon Linkleri */}
|
||||
<nav className="p-4 space-y-1.5">
|
||||
<div className="text-[10px] font-bold text-zinc-500 uppercase tracking-wider px-3 mb-2">
|
||||
Tasarım & Şablonlar
|
||||
</div>
|
||||
|
||||
<Link
|
||||
href="/admin/templates"
|
||||
className="flex items-center gap-3 px-3 py-2.5 rounded-xl bg-[#D4AF37]/10 text-[#D4AF37] border border-[#D4AF37]/25 font-semibold text-sm transition-all shadow-sm"
|
||||
>
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z" />
|
||||
</svg>
|
||||
QR Şablon Stüdyosu
|
||||
<span className="ml-auto text-[10px] bg-[#D4AF37] text-black font-extrabold px-1.5 py-0.5 rounded-full">
|
||||
JSON
|
||||
</span>
|
||||
</Link>
|
||||
|
||||
<Link
|
||||
href="/admin"
|
||||
className="flex items-center gap-3 px-3 py-2.5 rounded-xl text-zinc-400 hover:text-white hover:bg-white/5 text-sm font-medium transition-all"
|
||||
>
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" />
|
||||
</svg>
|
||||
Genel Bakış & İstatistikler
|
||||
</Link>
|
||||
|
||||
<div className="text-[10px] font-bold text-zinc-500 uppercase tracking-wider px-3 pt-4 mb-2">
|
||||
Önizleme & Test
|
||||
</div>
|
||||
|
||||
<Link
|
||||
href="/templates"
|
||||
target="_blank"
|
||||
className="flex items-center gap-3 px-3 py-2.5 rounded-xl text-zinc-400 hover:text-white hover:bg-white/5 text-sm font-medium transition-all"
|
||||
>
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M12 18h.01M8 21h8a2 2 0 002-2V5a2 2 0 00-2-2H8a2 2 0 00-2 2v14a2 2 0 002 2z" />
|
||||
</svg>
|
||||
Web Menü Simülatörü
|
||||
<svg className="w-3.5 h-3.5 ml-auto text-zinc-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" />
|
||||
</svg>
|
||||
</Link>
|
||||
|
||||
<Link
|
||||
href="/qr"
|
||||
target="_blank"
|
||||
className="flex items-center gap-3 px-3 py-2.5 rounded-xl text-zinc-400 hover:text-white hover:bg-white/5 text-sm font-medium transition-all"
|
||||
>
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M12 4v1m6 11h2m-6 0h-2v4m0-11v3m0 0h.01M12 12h4.01M16 20h4M4 12h4m12 0h.01M5 8h2a1 1 0 001-1V5a1 1 0 00-1-1H5a1 1 0 00-1 1v2a1 1 0 001 1zm12 0h2a1 1 0 001-1V5a1 1 0 00-1-1h-2a1 1 0 00-1 1v2a1 1 0 001 1zM5 20h2a1 1 0 001-1v-2a1 1 0 00-1-1H5a1 1 0 00-1 1v2a1 1 0 001 1z" />
|
||||
</svg>
|
||||
Web Masa Standı Üretici
|
||||
<svg className="w-3.5 h-3.5 ml-auto text-zinc-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" />
|
||||
</svg>
|
||||
</Link>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
{/* Alt Durum Kartı */}
|
||||
<div className="p-4 border-t border-white/10">
|
||||
<div className="p-3.5 rounded-xl bg-white/[0.04] border border-white/5 space-y-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-[11px] font-medium text-zinc-400">Şablon Motoru</span>
|
||||
<span className="flex items-center gap-1.5 text-[11px] font-bold text-emerald-400">
|
||||
<span className="w-1.5 h-1.5 rounded-full bg-emerald-400 animate-pulse" />
|
||||
CANLI (V2)
|
||||
</span>
|
||||
</div>
|
||||
<div className="text-[11px] text-zinc-500">
|
||||
Uygulama güncellemesi gerektirmeden JSON ile anında yayınlanır.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
{/* Sağ Ana İçerik */}
|
||||
<main className="flex-1 flex flex-col min-w-0 bg-[#0C0C0E]">
|
||||
{/* Üst Bar */}
|
||||
<header className="h-16 border-b border-white/10 px-6 flex items-center justify-between bg-[#121215]/50 backdrop-blur shrink-0">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="text-sm font-semibold text-zinc-300">
|
||||
Dinamik Şablon & İçerik Yönetimi
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="hidden sm:flex items-center gap-2 px-3 py-1.5 rounded-full bg-white/5 border border-white/10 text-xs text-zinc-300">
|
||||
<span className="w-2 h-2 rounded-full bg-[#D4AF37]" />
|
||||
API: <span className="font-semibold text-white">localhost:3001</span>
|
||||
</div>
|
||||
<Link
|
||||
href="/"
|
||||
className="text-xs text-zinc-400 hover:text-white transition-colors"
|
||||
>
|
||||
Menulio Web →
|
||||
</Link>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{/* Sayfa İçeriği */}
|
||||
<div className="flex-1 p-6 md:p-8 overflow-y-auto">
|
||||
{children}
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
import Link from "next/link";
|
||||
import { QR_STAND_PRESETS } from "@menulio/shared";
|
||||
|
||||
export default function AdminDashboardPage() {
|
||||
const presetCount = Object.keys(QR_STAND_PRESETS).length;
|
||||
|
||||
return (
|
||||
<div className="max-w-6xl mx-auto space-y-8">
|
||||
{/* Karşılama Başlığı */}
|
||||
<div className="flex flex-col md:flex-row md:items-center md:justify-between gap-4">
|
||||
<div>
|
||||
<h1 className="text-2xl md:text-3xl font-extrabold text-white tracking-tight">
|
||||
Menulio Sistem & Şablon Yönetimi
|
||||
</h1>
|
||||
<p className="text-zinc-400 text-sm mt-1">
|
||||
Masa standı şablonlarını, JSON şablon motorunu ve restoran görünüm ayarlarını yönetin.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<Link
|
||||
href="/admin/templates"
|
||||
className="inline-flex items-center gap-2 bg-gradient-to-r from-[#D4AF37] to-[#F59E0B] text-[#0C0C0E] font-bold px-5 py-2.5 rounded-xl shadow-lg shadow-[#D4AF37]/20 hover:opacity-95 transition-all text-sm self-start"
|
||||
>
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M12 4v16m8-8H4" />
|
||||
</svg>
|
||||
Yeni Şablon Tasarla & Ekle
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{/* İstatistik Kartları */}
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
|
||||
<div className="p-5 rounded-2xl bg-[#121215] border border-white/10 space-y-2">
|
||||
<div className="text-xs font-semibold text-zinc-400 uppercase tracking-wider">
|
||||
Aktif Şablonlar
|
||||
</div>
|
||||
<div className="text-3xl font-black text-white">{presetCount} Adet</div>
|
||||
<div className="text-xs text-emerald-400 font-medium flex items-center gap-1">
|
||||
<span>●</span> Mobil & Web Canlıda
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="p-5 rounded-2xl bg-[#121215] border border-white/10 space-y-2">
|
||||
<div className="text-xs font-semibold text-zinc-400 uppercase tracking-wider">
|
||||
Şablon Motoru Tipi
|
||||
</div>
|
||||
<div className="text-2xl font-black text-[#D4AF37]">JSON Driven</div>
|
||||
<div className="text-xs text-zinc-400 font-medium">
|
||||
0 App Store güncellemesi
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="p-5 rounded-2xl bg-[#121215] border border-white/10 space-y-2">
|
||||
<div className="text-xs font-semibold text-zinc-400 uppercase tracking-wider">
|
||||
Baskı Çıktı Formatı
|
||||
</div>
|
||||
<div className="text-3xl font-black text-white">A5 & 300DPI</div>
|
||||
<div className="text-xs text-zinc-400 font-medium">
|
||||
PDF + Vektörel PNG
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="p-5 rounded-2xl bg-[#121215] border border-white/10 space-y-2">
|
||||
<div className="text-xs font-semibold text-zinc-400 uppercase tracking-wider">
|
||||
API Durumu
|
||||
</div>
|
||||
<div className="text-2xl font-black text-emerald-400">Aktif & Senkron</div>
|
||||
<div className="text-xs text-zinc-400 font-medium">
|
||||
REST API v1
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Nasıl Çalışır / Mimari Rehber Kutusu */}
|
||||
<div className="p-6 rounded-2xl bg-gradient-to-br from-[#18181B] to-[#121215] border border-[#D4AF37]/20 relative overflow-hidden">
|
||||
<div className="relative z-10 space-y-4">
|
||||
<div className="inline-flex items-center gap-2 px-3 py-1 rounded-full bg-[#D4AF37]/15 border border-[#D4AF37]/30 text-xs font-bold text-[#D4AF37]">
|
||||
💡 DİNAMİK ŞABLON MİMARİSİ
|
||||
</div>
|
||||
|
||||
<h2 className="text-xl font-bold text-white">
|
||||
Mobil Uygulamayı Güncellemeden Yeni Şablon Yayınlama
|
||||
</h2>
|
||||
|
||||
<p className="text-sm text-zinc-300 leading-relaxed max-w-3xl">
|
||||
Menulio, QR masa kartı şablonlarını doğrudan kod içine gömmek yerine <strong>JSON şablon spesifikasyonu</strong> ile çalışır.
|
||||
Admin panelinden yeni bir SVG/Görsel arka plan ve varsayılan QR/metin koordinatları tanımladığınızda,
|
||||
tüm mobil kullanıcılar uygulamayı güncellemek zorunda kalmadan yeni şablonu anında görürler.
|
||||
</p>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 pt-2">
|
||||
<div className="p-4 rounded-xl bg-black/40 border border-white/5 space-y-1.5">
|
||||
<div className="text-xs font-bold text-[#D4AF37]">1. Arka Planı Belirle</div>
|
||||
<div className="text-xs text-zinc-400">
|
||||
Figma veya Illustrator'dan SVG/PNG arka planı oluşturup yükleyin veya renk seçin.
|
||||
</div>
|
||||
</div>
|
||||
<div className="p-4 rounded-xl bg-black/40 border border-white/5 space-y-1.5">
|
||||
<div className="text-xs font-bold text-[#D4AF37]">2. QR & Katmanları Ayarla</div>
|
||||
<div className="text-xs text-zinc-400">
|
||||
QR kodun yerini, boyutunu, restoran ismi ve slogan metinlerinin varsayılan yerlerini belirleyin.
|
||||
</div>
|
||||
</div>
|
||||
<div className="p-4 rounded-xl bg-black/40 border border-white/5 space-y-1.5">
|
||||
<div className="text-xs font-bold text-[#D4AF37]">3. Tek Tıkla Yayınla</div>
|
||||
<div className="text-xs text-zinc-400">
|
||||
Kaydet butonuna bastığınız anda API üzerinden tüm restoranların mobil uygulamasına dağıtılır.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Hızlı Şablonlar Listesi */}
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<h3 className="text-lg font-bold text-white">Mevcut Hazır Şablonlar</h3>
|
||||
<Link
|
||||
href="/admin/templates"
|
||||
className="text-xs font-semibold text-[#D4AF37] hover:underline"
|
||||
>
|
||||
Tümünü Yönet & Düzenle →
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
|
||||
{Object.values(QR_STAND_PRESETS).slice(0, 4).map((preset) => (
|
||||
<div
|
||||
key={preset.key}
|
||||
className="p-4 rounded-xl bg-[#121215] border border-white/10 space-y-3 flex flex-col justify-between"
|
||||
>
|
||||
<div>
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<span className="text-[11px] font-mono text-zinc-500">{preset.key}</span>
|
||||
<span className="text-[10px] bg-emerald-500/10 text-emerald-400 border border-emerald-500/20 px-2 py-0.5 rounded-full font-bold">
|
||||
Aktif
|
||||
</span>
|
||||
</div>
|
||||
<div className="font-bold text-white text-sm line-clamp-1">{preset.name}</div>
|
||||
<div className="text-xs text-zinc-400 mt-0.5">{preset.categoryName || "Masa Kartı"}</div>
|
||||
</div>
|
||||
|
||||
<div className="pt-3 border-t border-white/5 flex items-center justify-between text-xs text-zinc-400">
|
||||
<span>Oran: {preset.aspectRatio || "1/1"}</span>
|
||||
<Link
|
||||
href={`/admin/templates?key=${preset.key}`}
|
||||
className="font-bold text-[#D4AF37] hover:underline"
|
||||
>
|
||||
Düzenle
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import type { Metadata } from "next";
|
||||
import { TemplateStudioAdminClient } from "@/components/TemplateStudioAdminClient";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "QR Şablon Stüdyosu | Menulio Admin",
|
||||
description: "Masa standı şablonlarını görsel editör veya JSON ile tasarlayın, canlı önizleyin ve anında yayınlayın.",
|
||||
};
|
||||
|
||||
export default function AdminTemplatesPage() {
|
||||
return (
|
||||
<div className="max-w-7xl mx-auto space-y-6">
|
||||
<div className="flex flex-col md:flex-row md:items-center md:justify-between gap-2 border-b border-white/10 pb-5">
|
||||
<div>
|
||||
<h1 className="text-2xl font-extrabold text-white tracking-tight">
|
||||
QR Masa Standı Şablon Stüdyosu
|
||||
</h1>
|
||||
<p className="text-zinc-400 text-xs mt-1">
|
||||
Uygulama güncellemesi gerektirmeden yeni şablonlar ekleyin, renkleri ve QR koordinatlarını düzenleyin.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<TemplateStudioAdminClient />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import fs from "fs/promises";
|
||||
import path from "path";
|
||||
import { supabase } from "@/lib/supabase";
|
||||
|
||||
const DATA_FILE = path.join(process.cwd(), "src/data/qr-templates.json");
|
||||
|
||||
function mapFromDb(row: any) {
|
||||
return {
|
||||
key: row.key,
|
||||
name: row.name,
|
||||
categoryName: row.category_name,
|
||||
aspectRatio: row.aspect_ratio || "1/1",
|
||||
bgType: row.bg_type || "gradient",
|
||||
bgImageUrl: row.bg_image_url || undefined,
|
||||
bgGradient: row.bg_gradient || undefined,
|
||||
plaqueColor: row.plaque_color || undefined,
|
||||
borderAccent: row.border_accent || undefined,
|
||||
accentText: row.accent_text || undefined,
|
||||
headerText: row.header_text || undefined,
|
||||
subText: row.sub_text || undefined,
|
||||
dividerColor: row.divider_color || undefined,
|
||||
qrConfig: row.qr_config,
|
||||
layers: row.layers,
|
||||
isActive: row.is_active ?? true,
|
||||
createdAt: row.created_at,
|
||||
updatedAt: row.updated_at,
|
||||
};
|
||||
}
|
||||
|
||||
function mapToDb(item: any) {
|
||||
return {
|
||||
key: item.key,
|
||||
name: item.name,
|
||||
category_name: item.categoryName || null,
|
||||
aspect_ratio: item.aspectRatio || "1/1",
|
||||
bg_type: item.bgType || "gradient",
|
||||
bg_image_url: item.bgImageUrl || null,
|
||||
bg_gradient: item.bgGradient || null,
|
||||
plaque_color: item.plaqueColor || null,
|
||||
border_accent: item.borderAccent || null,
|
||||
accent_text: item.accentText || null,
|
||||
header_text: item.headerText || null,
|
||||
sub_text: item.subText || null,
|
||||
divider_color: item.dividerColor || null,
|
||||
qr_config: item.qrConfig || item.defaultQrConfig || null,
|
||||
layers: item.layers || item.defaultLayers || null,
|
||||
is_active: item.isActive !== undefined ? item.isActive : true,
|
||||
updated_at: new Date().toISOString(),
|
||||
};
|
||||
}
|
||||
|
||||
async function readFromDisk(): Promise<any[]> {
|
||||
try {
|
||||
const raw = await fs.readFile(DATA_FILE, "utf-8");
|
||||
return JSON.parse(raw);
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
async function writeToDisk(list: any[]): Promise<void> {
|
||||
try {
|
||||
await fs.writeFile(DATA_FILE, JSON.stringify(list, null, 2), "utf-8");
|
||||
} catch {}
|
||||
}
|
||||
|
||||
export async function GET() {
|
||||
try {
|
||||
const { data, error } = await supabase
|
||||
.from("qr_stand_templates")
|
||||
.select("*")
|
||||
.order("created_at", { ascending: false });
|
||||
|
||||
if (!error && data && data.length > 0) {
|
||||
const mapped = data.map(mapFromDb);
|
||||
await writeToDisk(mapped);
|
||||
return NextResponse.json({ templates: mapped, source: "supabase_db" });
|
||||
}
|
||||
} catch {}
|
||||
|
||||
const diskTemplates = await readFromDisk();
|
||||
return NextResponse.json({ templates: diskTemplates, source: "disk_cache" });
|
||||
}
|
||||
|
||||
export async function POST(req: Request) {
|
||||
try {
|
||||
const body = await req.json();
|
||||
if (!body || !body.key) {
|
||||
return NextResponse.json({ error: "Şablon key zorunludur" }, { status: 400 });
|
||||
}
|
||||
|
||||
// 1. Supabase'e Upsert et
|
||||
try {
|
||||
const dbRow = mapToDb(body);
|
||||
await supabase.from("qr_stand_templates").upsert(dbRow, { onConflict: "key" });
|
||||
} catch (dbErr) {
|
||||
console.warn("Supabase upsert warning:", dbErr);
|
||||
}
|
||||
|
||||
// 2. Diske de yaz (offline / backup)
|
||||
const diskList = await readFromDisk();
|
||||
const existingIdx = diskList.findIndex((t: any) => t.key === body.key);
|
||||
if (existingIdx >= 0) {
|
||||
diskList[existingIdx] = { ...diskList[existingIdx], ...body };
|
||||
} else {
|
||||
diskList.unshift(body);
|
||||
}
|
||||
await writeToDisk(diskList);
|
||||
|
||||
return NextResponse.json({ success: true, template: body });
|
||||
} catch (err: any) {
|
||||
return NextResponse.json({ error: err.message }, { status: 500 });
|
||||
}
|
||||
}
|
||||
|
||||
export async function DELETE(req: Request) {
|
||||
try {
|
||||
const { searchParams } = new URL(req.url);
|
||||
const key = searchParams.get("key");
|
||||
if (!key) {
|
||||
return NextResponse.json({ error: "Şablon key zorunludur" }, { status: 400 });
|
||||
}
|
||||
|
||||
// 1. Supabase'den Sil
|
||||
try {
|
||||
await supabase.from("qr_stand_templates").delete().eq("key", key);
|
||||
} catch (dbErr) {
|
||||
console.warn("Supabase delete warning:", dbErr);
|
||||
}
|
||||
|
||||
// 2. Diskten de Sil
|
||||
const diskList = await readFromDisk();
|
||||
const filtered = diskList.filter((t: any) => t.key !== key);
|
||||
await writeToDisk(filtered);
|
||||
|
||||
return NextResponse.json({ success: true, deletedKey: key });
|
||||
} catch (err: any) {
|
||||
return NextResponse.json({ error: err.message }, { status: 500 });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,16 +20,16 @@ export function QrStandGeneratorClient({
|
||||
slug = "kebapci-sinan-3",
|
||||
categoriesList = [],
|
||||
}: QrStandGeneratorClientProps) {
|
||||
const [selectedKey, setSelectedKey] = useState<string>("urban");
|
||||
const [selectedKey, setSelectedKey] = useState<string>("qr-4");
|
||||
const [downloadingPng, setDownloadingPng] = useState(false);
|
||||
const [downloadingPdf, setDownloadingPdf] = useState(false);
|
||||
const standRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const activePreset: QrStandPreset = (QR_STAND_PRESETS[selectedKey] || QR_STAND_PRESETS.urban)!;
|
||||
const activePreset: QrStandPreset = (QR_STAND_PRESETS[selectedKey] || Object.values(QR_STAND_PRESETS)[0])!;
|
||||
const targetUrl = `https://menul.io/${slug}`;
|
||||
|
||||
const displayCategories =
|
||||
categoriesList.length > 0 ? categoriesList.slice(0, 4) : activePreset.categories;
|
||||
categoriesList.length > 0 ? categoriesList.slice(0, 4) : (activePreset.categories || []);
|
||||
|
||||
async function handleDownloadPng() {
|
||||
if (!standRef.current) return;
|
||||
@@ -139,7 +139,7 @@ export function QrStandGeneratorClient({
|
||||
<div className="flex items-center gap-3">
|
||||
<div
|
||||
className="w-4 h-4 rounded-full border border-white/20 shadow-sm"
|
||||
style={{ background: preset.plaqueBg }}
|
||||
style={{ background: preset.plaqueBg || preset.plaqueColor || "#1A1A1E" }}
|
||||
/>
|
||||
<div>
|
||||
<div className="text-xs font-bold">{preset.name}</div>
|
||||
@@ -208,78 +208,35 @@ export function QrStandGeneratorClient({
|
||||
{activePreset.svgTemplateUrl ? (
|
||||
<div
|
||||
ref={standRef}
|
||||
className="w-[360px] sm:w-[380px] aspect-[672/900] rounded-[2.5rem] relative overflow-hidden shadow-[0_25px_60px_rgba(0,0,0,0.6)] border-2 border-white/10"
|
||||
className="w-[360px] sm:w-[380px] rounded-[2.5rem] relative overflow-hidden shadow-[0_25px_60px_rgba(0,0,0,0.6)] border-2 border-white/10 aspect-[400/650]"
|
||||
>
|
||||
{/* SVG Template Vector Background Graphic */}
|
||||
{/* Exact SVG Template Vector Background Graphic */}
|
||||
<img
|
||||
src={activePreset.svgTemplateUrl}
|
||||
alt={activePreset.name}
|
||||
className="absolute inset-0 w-full h-full object-cover pointer-events-none"
|
||||
className="absolute inset-0 w-full h-full object-contain pointer-events-none"
|
||||
/>
|
||||
|
||||
{/* Dynamic Restaurant Logo & Name Header Overlay */}
|
||||
<div className="absolute top-[13%] left-0 right-0 flex flex-col items-center justify-center px-6 text-center z-10">
|
||||
{logoUrl ? (
|
||||
<img
|
||||
src={logoUrl}
|
||||
alt={restaurantName}
|
||||
className="h-9 w-auto max-w-[130px] object-contain mb-1"
|
||||
/>
|
||||
) : null}
|
||||
<span
|
||||
className="font-black text-lg sm:text-xl tracking-wider uppercase font-['Montserrat'] leading-tight drop-shadow-md"
|
||||
style={{ color: activePreset.accentText }}
|
||||
>
|
||||
{restaurantName}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Dynamic QR Code Canvas Overlay */}
|
||||
<div className="absolute top-[37%] left-1/2 -translate-x-1/2 w-[52%] aspect-square rounded-2xl p-2.5 shadow-2xl flex items-center justify-center bg-[#f8f3eb] z-10 border border-black/10">
|
||||
{/* Dynamic QR Code Canvas Overlay - Fills the QR Code Box Exactly */}
|
||||
<div className="absolute top-[33.08%] left-1/2 -translate-x-1/2 w-[55%] aspect-square rounded-[20px] p-3 shadow-xl flex items-center justify-center bg-white z-10 border border-zinc-200">
|
||||
<QRCodeCanvas
|
||||
value={targetUrl}
|
||||
size={170}
|
||||
bgColor={activePreset.qrBg || "#f8f3eb"}
|
||||
fgColor={activePreset.qrColor || "#a64d2a"}
|
||||
size={220}
|
||||
bgColor="#ffffff"
|
||||
fgColor="#111111"
|
||||
level="H"
|
||||
style={{ width: "100%", height: "100%" }}
|
||||
includeMargin={false}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Dynamic Categories Overlay */}
|
||||
<div className="absolute bottom-[8%] left-0 right-0 flex flex-col items-center justify-center px-6 text-center z-10">
|
||||
<p
|
||||
className="text-[11px] sm:text-xs tracking-wide mb-2 font-semibold"
|
||||
style={{ color: activePreset.subText }}
|
||||
>
|
||||
Telefonunuzun kamerasını doğrultun
|
||||
</p>
|
||||
<div
|
||||
className="flex flex-col gap-1 max-w-[200px] w-full font-['Montserrat'] font-extrabold uppercase tracking-widest text-xs sm:text-sm"
|
||||
style={{ color: activePreset.headerText }}
|
||||
>
|
||||
{displayCategories.map((cat, idx) => (
|
||||
<div key={idx} className="w-full">
|
||||
<div className="py-0.5">{cat}</div>
|
||||
{idx < displayCategories.length - 1 && (
|
||||
<div
|
||||
className="w-full h-[1px] my-0.5"
|
||||
style={{ backgroundColor: activePreset.dividerColor }}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div
|
||||
ref={standRef}
|
||||
className="w-[360px] sm:w-[380px] min-h-[660px] rounded-[2.5rem] p-7 flex flex-col items-center text-center relative shadow-[0_25px_60px_rgba(0,0,0,0.6)] border-2"
|
||||
style={{
|
||||
background: activePreset.plaqueBg,
|
||||
borderColor: activePreset.borderAccent,
|
||||
background: activePreset.plaqueBg || activePreset.plaqueColor || "#1A1A1E",
|
||||
borderColor: activePreset.borderAccent || "#D4AF37",
|
||||
}}
|
||||
>
|
||||
{/* Inner Line Accent */}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
"private": true,
|
||||
"version": "0.0.1",
|
||||
"type": "module",
|
||||
"packageManager": "pnpm@11.22.0",
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
},
|
||||
@@ -12,13 +13,7 @@
|
||||
"dev:mobile": "pnpm --filter @menulio/mobile start",
|
||||
"build": "pnpm -r build",
|
||||
"lint": "pnpm -r lint",
|
||||
"typecheck": "pnpm -r typecheck"
|
||||
},
|
||||
"pnpm": {
|
||||
"onlyBuiltDependencies": [
|
||||
"@swc/core",
|
||||
"esbuild",
|
||||
"sharp"
|
||||
]
|
||||
"typecheck": "pnpm -r typecheck",
|
||||
"clean": "rm -rf node_modules apps/*/node_modules packages/*/node_modules apps/*/.next apps/*/.expo apps/*/dist"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,60 +1,138 @@
|
||||
export interface QrStandPreset {
|
||||
key: string;
|
||||
name: string;
|
||||
categoryName: string;
|
||||
bgGradient: string;
|
||||
plaqueBg: string;
|
||||
plaqueColor: string;
|
||||
borderAccent: string;
|
||||
qrBg: string;
|
||||
qrColor: string;
|
||||
logoBorder: string;
|
||||
logoBg: string;
|
||||
accentText: string;
|
||||
headerText: string;
|
||||
subText: string;
|
||||
dividerColor: string;
|
||||
categories: string[];
|
||||
svgTemplateUrl?: string;
|
||||
categoryName?: string;
|
||||
svgTemplateUrl: string;
|
||||
aspectRatio?: string;
|
||||
bgGradient?: string;
|
||||
plaqueBg?: string;
|
||||
plaqueColor?: string;
|
||||
borderAccent?: string;
|
||||
qrBg?: string;
|
||||
qrColor?: string;
|
||||
logoBorder?: string;
|
||||
logoBg?: string;
|
||||
accentText?: string;
|
||||
headerText?: string;
|
||||
subText?: string;
|
||||
dividerColor?: string;
|
||||
categories?: string[];
|
||||
}
|
||||
|
||||
export const QR_STAND_PRESETS: Record<string, QrStandPreset> = {
|
||||
urban: {
|
||||
key: "urban",
|
||||
name: "The Urban Burger QR Stand 1",
|
||||
categoryName: "Özel Masa Kartı Şablonu 1",
|
||||
bgGradient: "radial-gradient(circle at 50% 50%, #1c3e2b 0%, #0e2016 100%)",
|
||||
plaqueBg: "linear-gradient(135deg, #a64d2a 0%, #8e3f20 100%)",
|
||||
plaqueColor: "#a64d2a",
|
||||
borderAccent: "rgba(255, 255, 255, 0.15)",
|
||||
qrBg: "#f8f3eb",
|
||||
qrColor: "#a64d2a",
|
||||
logoBorder: "#f5be72",
|
||||
logoBg: "rgba(166, 77, 42, 0.8)",
|
||||
accentText: "#f5be72",
|
||||
headerText: "#ffffff",
|
||||
subText: "rgba(255, 255, 255, 0.9)",
|
||||
dividerColor: "rgba(255, 255, 255, 0.25)",
|
||||
categories: ["BURGERS", "FRIES", "SHAKES", "VEGAN OPTIONS"],
|
||||
"qr-1": {
|
||||
key: "qr-1",
|
||||
name: "Lumière Restaurant (QR-1)",
|
||||
categoryName: "Özel Masa Kartı",
|
||||
svgTemplateUrl: "/qr-1.svg",
|
||||
aspectRatio: "1/1",
|
||||
bgGradient: "radial-gradient(circle at 50% 50%, #132034 0%, #0b0d13 100%)",
|
||||
plaqueColor: "#132034",
|
||||
borderAccent: "#d4af37",
|
||||
accentText: "#d4af37",
|
||||
headerText: "#FFFFFF",
|
||||
subText: "#e5c378",
|
||||
dividerColor: "rgba(212, 175, 55, 0.3)",
|
||||
},
|
||||
"custom-svg-2": {
|
||||
key: "custom-svg-2",
|
||||
name: "Classic Bistro QR Stand 2",
|
||||
categoryName: "Özel Masa Kartı Şablonu 2",
|
||||
bgGradient: "radial-gradient(circle at 50% 50%, #18181b 0%, #09090b 100%)",
|
||||
plaqueBg: "linear-gradient(135deg, #1c1917 0%, #0c0a09 100%)",
|
||||
plaqueColor: "#1c1917",
|
||||
borderAccent: "rgba(200, 169, 107, 0.4)",
|
||||
qrBg: "#faf8f5",
|
||||
qrColor: "#1c1917",
|
||||
logoBorder: "#c8a96b",
|
||||
logoBg: "rgba(28, 25, 23, 0.9)",
|
||||
accentText: "#c8a96b",
|
||||
headerText: "#ffffff",
|
||||
subText: "#d6d3d1",
|
||||
dividerColor: "rgba(200, 169, 107, 0.3)",
|
||||
categories: ["BAŞLANGIÇLAR", "ANA YEMEKLER", "TATLILAR", "İÇECEKLER"],
|
||||
"qr-2": {
|
||||
key: "qr-2",
|
||||
name: "The Daily Grind Cafe (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)",
|
||||
categoryName: "Özel Masa Kartı",
|
||||
svgTemplateUrl: "/qr-3.svg",
|
||||
aspectRatio: "400/650",
|
||||
bgGradient: "radial-gradient(circle at 50% 50%, #faf4e6 0%, #f7eed8 100%)",
|
||||
plaqueColor: "#fdfbf5",
|
||||
borderAccent: "#b92b27",
|
||||
accentText: "#b92b27",
|
||||
headerText: "#18181b",
|
||||
subText: "#5d4037",
|
||||
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)",
|
||||
},
|
||||
"qr-5": {
|
||||
key: "qr-5",
|
||||
name: "The Urban Fork (QR-5)",
|
||||
categoryName: "Özel Masa Kartı",
|
||||
svgTemplateUrl: "/qr-5.svg",
|
||||
aspectRatio: "380/500",
|
||||
bgGradient: "radial-gradient(circle at 50% 50%, #e0f2fe 0%, #bae6fd 100%)",
|
||||
plaqueColor: "#003554",
|
||||
borderAccent: "#003554",
|
||||
accentText: "#003554",
|
||||
headerText: "#003554",
|
||||
subText: "#0284c7",
|
||||
dividerColor: "rgba(0, 53, 84, 0.3)",
|
||||
},
|
||||
"qr-6": {
|
||||
key: "qr-6",
|
||||
name: "The Stonehearth Grill (QR-6)",
|
||||
categoryName: "Özel Masa Kartı",
|
||||
svgTemplateUrl: "/qr-6.svg",
|
||||
aspectRatio: "400/580",
|
||||
bgGradient: "radial-gradient(circle at 50% 50%, #434b54 0%, #1a2228 100%)",
|
||||
plaqueColor: "#f6efe3",
|
||||
borderAccent: "#c59b58",
|
||||
accentText: "#a64423",
|
||||
headerText: "#122a42",
|
||||
subText: "#c59b58",
|
||||
dividerColor: "rgba(197, 155, 88, 0.3)",
|
||||
},
|
||||
"qr-8": {
|
||||
key: "qr-8",
|
||||
name: "Emerald & Gold Special (QR-8)",
|
||||
categoryName: "Özel Kare Masa Kartı",
|
||||
svgTemplateUrl: "/qr-8.svg",
|
||||
aspectRatio: "1/1",
|
||||
bgGradient: "radial-gradient(circle at 50% 50%, #0c4443 0%, #062423 100%)",
|
||||
plaqueColor: "#f9f5eb",
|
||||
borderAccent: "#d1a752",
|
||||
accentText: "#d1a752",
|
||||
headerText: "#ffffff",
|
||||
subText: "#d1a752",
|
||||
dividerColor: "rgba(209, 167, 82, 0.4)",
|
||||
},
|
||||
"qr-9": {
|
||||
key: "qr-9",
|
||||
name: "Vintage Table Menu (QR-9)",
|
||||
categoryName: "Özel Kare Masa Kartı",
|
||||
svgTemplateUrl: "/qr-9.svg",
|
||||
aspectRatio: "1/1",
|
||||
bgGradient: "radial-gradient(circle at 50% 50%, #f8f6f0 0%, #e2ded5 100%)",
|
||||
plaqueColor: "#ffffff",
|
||||
borderAccent: "#d5cebe",
|
||||
accentText: "#1e3246",
|
||||
headerText: "#1e3246",
|
||||
subText: "#a88b58",
|
||||
dividerColor: "rgba(30, 50, 70, 0.2)",
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -90,14 +90,14 @@ importers:
|
||||
specifier: ~7.0.5
|
||||
version: 7.0.5(expo@52.0.49(@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))(@expo/metro-runtime@4.0.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@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)(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))(react@18.3.1)(supports-color@8.1.1)
|
||||
expo-print:
|
||||
specifier: ^57.0.1
|
||||
version: 57.0.1(expo@52.0.49(@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))(@expo/metro-runtime@4.0.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@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)(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))
|
||||
specifier: ~14.0.3
|
||||
version: 14.0.3(expo@52.0.49(@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))(@expo/metro-runtime@4.0.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@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)(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))
|
||||
expo-router:
|
||||
specifier: ~4.0.0
|
||||
version: 4.0.22(1482d1318244a89f06fcfc2b6da7d585)
|
||||
expo-sharing:
|
||||
specifier: ^57.0.13
|
||||
version: 57.0.13(expo@52.0.49(@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))(@expo/metro-runtime@4.0.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@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)(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))(react@18.3.1)(supports-color@8.1.1)(typescript@5.9.3)
|
||||
specifier: ~13.0.1
|
||||
version: 13.0.1(expo@52.0.49(@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))(@expo/metro-runtime@4.0.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@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)(supports-color@8.1.1))
|
||||
expo-splash-screen:
|
||||
specifier: ~0.29.24
|
||||
version: 0.29.24(expo@52.0.49(@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))(@expo/metro-runtime@4.0.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@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)(supports-color@8.1.1))(supports-color@8.1.1)
|
||||
@@ -119,12 +119,15 @@ importers:
|
||||
react-native-screens:
|
||||
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)
|
||||
react-native-svg:
|
||||
specifier: 15.8.0
|
||||
version: 15.8.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-url-polyfill:
|
||||
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))
|
||||
react-native-view-shot:
|
||||
specifier: ^5.1.1
|
||||
version: 5.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))(react@18.3.1)
|
||||
specifier: ~4.0.3
|
||||
version: 4.0.3(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)
|
||||
devDependencies:
|
||||
'@babel/core':
|
||||
specifier: ^7.25.2
|
||||
@@ -1271,18 +1274,12 @@ packages:
|
||||
'@expo/code-signing-certificates@0.0.6':
|
||||
resolution: {integrity: sha512-iNe0puxwBNEcuua9gmTGzq+SuMDa0iATai1FlFTMHJ/vUmKvN/V//drXoLJkVb5i5H3iE/n/qIJxyoBnXouD0w==}
|
||||
|
||||
'@expo/config-plugins@57.0.8':
|
||||
resolution: {integrity: sha512-x6lx4s/19i39/+1dMPwb9tFCc4WR843dG5Yi+C64lhKL3YHX+6oKrT2Kv72PCEalnUY+usQDkB3NrLSrYOWtDg==}
|
||||
|
||||
'@expo/config-plugins@9.0.17':
|
||||
resolution: {integrity: sha512-m24F1COquwOm7PBl5wRbkT9P9DviCXe0D7S7nQsolfbhdCWuvMkfXeoWmgjtdhy7sDlOyIgBrAdnB6MfsWKqIg==}
|
||||
|
||||
'@expo/config-types@52.0.5':
|
||||
resolution: {integrity: sha512-AMDeuDLHXXqd8W+0zSjIt7f37vUd/BP8p43k68NHpyAvQO+z8mbQZm3cNQVAMySeayK2XoPigAFB1JF2NFajaA==}
|
||||
|
||||
'@expo/config-types@57.0.2':
|
||||
resolution: {integrity: sha512-ewW08OonrcRIsRKIlFvvcmmafE5zemb1ocu3HkNwtVPyRtj2w42pZCAkMIROYpcVBaPnc3mDT9UZDzwXWC3i6g==}
|
||||
|
||||
'@expo/config@10.0.11':
|
||||
resolution: {integrity: sha512-nociJ4zr/NmbVfMNe9j/+zRlt7wz/siISu7PjdWE4WE+elEGxWWxsGzltdJG0llzrM+khx8qUiFK5aiVcdMBww==}
|
||||
|
||||
@@ -1326,20 +1323,9 @@ packages:
|
||||
'@expo/plist@0.2.2':
|
||||
resolution: {integrity: sha512-ZZGvTO6vEWq02UAPs3LIdja+HRO18+LRI5QuDl6Hs3Ps7KX7xU6Y6kjahWKY37Rx2YjNpX07dGpBFzzC+vKa2g==}
|
||||
|
||||
'@expo/plist@0.8.1':
|
||||
resolution: {integrity: sha512-3gTReGIUm0oRaMClsAJYxBnVPCl6fVpsl8HS+DTVxDhW4GyVyxg9E/Znm3BvcHtUJ51RJJI14pC1wvrNilCRHw==}
|
||||
|
||||
'@expo/prebuild-config@8.2.0':
|
||||
resolution: {integrity: sha512-CxiPpd980s0jyxi7eyN3i/7YKu3XL+8qPjBZUCYtc0+axpGweqIkq2CslyLSKHyqVyH/zlPkbVgWdyiYavFS5Q==}
|
||||
|
||||
'@expo/require-utils@57.0.4':
|
||||
resolution: {integrity: sha512-e7xbg/9BTQcsZE/oErafZXtI7kh5IgfasLJ97J5sFSzX2cA74pDvdlhW1KHVSaDkQyQv6h1LSLhsY7dEeOk7hw==}
|
||||
peerDependencies:
|
||||
typescript: ^5.0.0 || ^5.0.0-0 || ^6.0.0 || ^7.0.0
|
||||
peerDependenciesMeta:
|
||||
typescript:
|
||||
optional: true
|
||||
|
||||
'@expo/rudder-sdk-node@1.1.1':
|
||||
resolution: {integrity: sha512-uy/hS/awclDJ1S88w9UGpc6Nm9XnNUjzOAAib1A3PVAnGQIwebg8DpFqOthFBTlZxeuV/BKbZ5jmTbtNZkp1WQ==}
|
||||
engines: {node: '>=12'}
|
||||
@@ -2081,10 +2067,6 @@ packages:
|
||||
engines: {node: '>=10.0.0'}
|
||||
deprecated: this version has critical issues, please update to the latest version
|
||||
|
||||
'@xmldom/xmldom@0.8.14':
|
||||
resolution: {integrity: sha512-T4EDRUBVZYRldYApjEJiU0e1stYWaRAX7CuSnKzrpwdZKo53zGV8/pqfzV6FfwNl9YThD2OumQYvqtvjvgG7aQ==}
|
||||
engines: {node: '>=10.0.0'}
|
||||
|
||||
'@xmldom/xmldom@0.9.11':
|
||||
resolution: {integrity: sha512-tW8bcK3hsG0/uqSnNz6TK4BkcuZSezoU7DlnYssILmZDktPnSHHuDJJFM0AJv+13gz2r0iGdrj6qqKeUnxXEDg==}
|
||||
engines: {node: '>=14.6'}
|
||||
@@ -2295,10 +2277,6 @@ packages:
|
||||
balanced-match@1.0.2:
|
||||
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
|
||||
|
||||
balanced-match@4.0.4:
|
||||
resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==}
|
||||
engines: {node: 18 || 20 || >=22}
|
||||
|
||||
base64-arraybuffer@1.0.2:
|
||||
resolution: {integrity: sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==}
|
||||
engines: {node: '>= 0.6.0'}
|
||||
@@ -2323,6 +2301,9 @@ packages:
|
||||
resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
boolbase@1.0.0:
|
||||
resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==}
|
||||
|
||||
bplist-creator@0.0.7:
|
||||
resolution: {integrity: sha512-xp/tcaV3T5PCiaY04mXga7o/TE+t95gqeLmADeBI1CvZtdWTbgBt3uLpvh4UWtenKeBhCV6oVxGk38yZr2uYEA==}
|
||||
|
||||
@@ -2343,10 +2324,6 @@ packages:
|
||||
brace-expansion@2.1.4:
|
||||
resolution: {integrity: sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==}
|
||||
|
||||
brace-expansion@5.0.9:
|
||||
resolution: {integrity: sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==}
|
||||
engines: {node: 20 || >=22}
|
||||
|
||||
braces@3.0.3:
|
||||
resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
|
||||
engines: {node: '>=8'}
|
||||
@@ -2601,6 +2578,17 @@ packages:
|
||||
css-line-break@2.1.0:
|
||||
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:
|
||||
resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==}
|
||||
engines: {node: '>=4'}
|
||||
@@ -2703,9 +2691,22 @@ packages:
|
||||
dlv@1.1.3:
|
||||
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:
|
||||
resolution: {integrity: sha512-dVoH9z+MY+C9IilgGCk3YfFqjLi3fChm2OiKJMzh6axrJ5qwxqWaZamgmHrpv22CN/KdbZJuGEGgfQoL00LTdg==}
|
||||
|
||||
domutils@3.2.2:
|
||||
resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==}
|
||||
|
||||
dotenv-expand@11.0.7:
|
||||
resolution: {integrity: sha512-zIHwmZPRshsCdpMDyVsqGmgyP0yT8GAgXUnkdAoJisxvf33k7yO6OuoKmcTGuXPWSsm8Oh88nZicRLA9Y0rUeA==}
|
||||
engines: {node: '>=12'}
|
||||
@@ -2748,6 +2749,10 @@ packages:
|
||||
end-of-stream@1.4.5:
|
||||
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:
|
||||
resolution: {integrity: sha512-ObFo8v4rQJAE59M69QzwloxPZtd33TpYEIjtKD1rrFDcM1Gd7IkDxEBU+HriziN6HSHQnBJi8Dmy+JWkav5HKA==}
|
||||
engines: {node: '>=8'}
|
||||
@@ -2894,8 +2899,8 @@ packages:
|
||||
expo-modules-core@2.2.3:
|
||||
resolution: {integrity: sha512-01QqZzpP/wWlxnNly4G06MsOBUTbMDj02DQigZoXfDh80vd/rk3/uVXqnZgOdLSggTs6DnvOgAUy0H2q30XdUg==}
|
||||
|
||||
expo-print@57.0.1:
|
||||
resolution: {integrity: sha512-cEfxM+sV5suZyqbXsOmDGRHD+UmfAh2GWabbakdkl1s70L4MFiObPtC73ATXNjeUe+mlzxLhudoTxsuyJxk8Mg==}
|
||||
expo-print@14.0.3:
|
||||
resolution: {integrity: sha512-VEGl7SX3q2Z3ZKwvxtOBAlXm9yDX8Jodewr+kouQcg2EaV0ciAH9rqLwRWZhAtEETpVdEG53ow7IAmwJU7jInA==}
|
||||
peerDependencies:
|
||||
expo: '*'
|
||||
react-native: '*'
|
||||
@@ -2919,12 +2924,10 @@ packages:
|
||||
react-native-reanimated:
|
||||
optional: true
|
||||
|
||||
expo-sharing@57.0.13:
|
||||
resolution: {integrity: sha512-8kRUBikuR+c90++PF4PaP8pj+McUNi7AIZkwmn0iXcA8QobkJDSAjSB3fP8qoFm8bzHDhyibHIlODTAsxrMYMw==}
|
||||
expo-sharing@13.0.1:
|
||||
resolution: {integrity: sha512-qych3Nw65wlFcnzE/gRrsdtvmdV0uF4U4qVMZBJYPG90vYyWh2QM9rp1gVu0KWOBc7N8CC2dSVYn4/BXqJy6Xw==}
|
||||
peerDependencies:
|
||||
expo: '*'
|
||||
react: '*'
|
||||
react-native: '*'
|
||||
|
||||
expo-splash-screen@0.29.24:
|
||||
resolution: {integrity: sha512-k2rdjbb3Qeg4g104Sdz6+qXXYba8QgiuZRSxHX8IpsSYiiTU48BmCCGy12sN+O1B+sD1/+WPL4duCa1Fy6+Y4g==}
|
||||
@@ -3152,10 +3155,6 @@ packages:
|
||||
resolution: {integrity: sha512-7yetJWqbS9sbn0vIfliPsFgoXMKn/YMF+Wuiog97x+urnSRRRZ7xB+uVkwGKzRgq9CDFfMQnE9ruL5DHv9c6Xg==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
getenv@2.0.0:
|
||||
resolution: {integrity: sha512-VilgtJj/ALgGY77fiLam5iD336eSWi96Q15JSAG1zi8NRBysm3LXKdGnHb4m5cuyxvOLQQKWpBZAT6ni4FI2iQ==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
glob-parent@5.1.2:
|
||||
resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
|
||||
engines: {node: '>= 6'}
|
||||
@@ -3169,10 +3168,6 @@ packages:
|
||||
deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me
|
||||
hasBin: true
|
||||
|
||||
glob@13.0.6:
|
||||
resolution: {integrity: sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==}
|
||||
engines: {node: 18 || 20 || >=22}
|
||||
|
||||
glob@7.2.3:
|
||||
resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==}
|
||||
deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me
|
||||
@@ -3614,10 +3609,6 @@ packages:
|
||||
lru-cache@10.4.3:
|
||||
resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==}
|
||||
|
||||
lru-cache@11.5.2:
|
||||
resolution: {integrity: sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==}
|
||||
engines: {node: 20 || >=22}
|
||||
|
||||
lru-cache@5.1.1:
|
||||
resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
|
||||
|
||||
@@ -3646,6 +3637,9 @@ packages:
|
||||
md5@2.3.0:
|
||||
resolution: {integrity: sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==}
|
||||
|
||||
mdn-data@2.0.14:
|
||||
resolution: {integrity: sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==}
|
||||
|
||||
memoize-one@5.2.1:
|
||||
resolution: {integrity: sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==}
|
||||
|
||||
@@ -3747,10 +3741,6 @@ packages:
|
||||
resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
minimatch@10.2.6:
|
||||
resolution: {integrity: sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==}
|
||||
engines: {node: 18 || 20 || >=22}
|
||||
|
||||
minimatch@3.1.5:
|
||||
resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==}
|
||||
|
||||
@@ -3896,6 +3886,9 @@ packages:
|
||||
resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
nth-check@2.1.1:
|
||||
resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==}
|
||||
|
||||
nullthrows@1.1.1:
|
||||
resolution: {integrity: sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==}
|
||||
|
||||
@@ -4030,10 +4023,6 @@ packages:
|
||||
resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==}
|
||||
engines: {node: '>=16 || 14 >=14.18'}
|
||||
|
||||
path-scurry@2.0.2:
|
||||
resolution: {integrity: sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==}
|
||||
engines: {node: 18 || 20 || >=22}
|
||||
|
||||
path-type@4.0.0:
|
||||
resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==}
|
||||
engines: {node: '>=8'}
|
||||
@@ -4292,17 +4281,22 @@ packages:
|
||||
react: '*'
|
||||
react-native: '*'
|
||||
|
||||
react-native-svg@15.8.0:
|
||||
resolution: {integrity: sha512-KHJzKpgOjwj1qeZzsBjxNdoIgv2zNCO9fVcoq2TEhTRsVV5DGTZ9JzUZwybd7q4giT/H3RdtqC3u44dWdO0Ffw==}
|
||||
peerDependencies:
|
||||
react: '*'
|
||||
react-native: '*'
|
||||
|
||||
react-native-url-polyfill@2.0.0:
|
||||
resolution: {integrity: sha512-My330Do7/DvKnEvwQc0WdcBnFPploYKp9CYlefDXzIdEaA+PAhDYllkvGeEroEzvc4Kzzj2O4yVdz8v6fjRvhA==}
|
||||
peerDependencies:
|
||||
react-native: '*'
|
||||
|
||||
react-native-view-shot@5.1.1:
|
||||
resolution: {integrity: sha512-jOTLQrMGNBgv/nRA81VR2x2AdiCGoCjc63c9a5P4A0gStCbh9oDeJ6W+GtRxxQmxXXf6LDSrvG+mmWYulIrOkg==}
|
||||
engines: {node: '>=20.19.4', npm: '>=10'}
|
||||
react-native-view-shot@4.0.3:
|
||||
resolution: {integrity: sha512-USNjYmED7C0me02c1DxKA0074Hw+y/nxo+xJKlffMvfUWWzL5ELh/TJA/pTnVqFurIrzthZDPtDM7aBFJuhrHQ==}
|
||||
peerDependencies:
|
||||
react: '>=18.0.0'
|
||||
react-native: '>=0.76.0'
|
||||
react: '*'
|
||||
react-native: '*'
|
||||
|
||||
react-native@0.76.9:
|
||||
resolution: {integrity: sha512-+LRwecWmTDco7OweGsrECIqJu0iyrREd6CTCgC/uLLYipiHvk+MH9nd6drFtCw/6Blz6eoKTcH9YTTJusNtrWg==}
|
||||
@@ -6302,25 +6296,6 @@ snapshots:
|
||||
dependencies:
|
||||
node-forge: 1.4.0
|
||||
|
||||
'@expo/config-plugins@57.0.8(supports-color@8.1.1)(typescript@5.9.3)':
|
||||
dependencies:
|
||||
'@expo/config-types': 57.0.2
|
||||
'@expo/json-file': 11.0.1
|
||||
'@expo/plist': 0.8.1
|
||||
'@expo/require-utils': 57.0.4(supports-color@8.1.1)(typescript@5.9.3)
|
||||
'@expo/sdk-runtime-versions': 1.0.0
|
||||
chalk: 4.1.2
|
||||
debug: 4.4.3(supports-color@8.1.1)
|
||||
getenv: 2.0.0
|
||||
glob: 13.0.6
|
||||
semver: 7.8.5
|
||||
slugify: 1.6.9
|
||||
xcode: 3.0.1
|
||||
xml2js: 0.6.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
- typescript
|
||||
|
||||
'@expo/config-plugins@9.0.17(supports-color@8.1.1)':
|
||||
dependencies:
|
||||
'@expo/config-types': 52.0.5
|
||||
@@ -6342,8 +6317,6 @@ snapshots:
|
||||
|
||||
'@expo/config-types@52.0.5': {}
|
||||
|
||||
'@expo/config-types@57.0.2': {}
|
||||
|
||||
'@expo/config@10.0.11(supports-color@8.1.1)':
|
||||
dependencies:
|
||||
'@babel/code-frame': 7.10.4
|
||||
@@ -6469,12 +6442,6 @@ snapshots:
|
||||
base64-js: 1.5.1
|
||||
xmlbuilder: 14.0.0
|
||||
|
||||
'@expo/plist@0.8.1':
|
||||
dependencies:
|
||||
'@xmldom/xmldom': 0.8.14
|
||||
base64-js: 1.5.1
|
||||
xmlbuilder: 15.1.1
|
||||
|
||||
'@expo/prebuild-config@8.2.0(supports-color@8.1.1)':
|
||||
dependencies:
|
||||
'@expo/config': 10.0.11(supports-color@8.1.1)
|
||||
@@ -6491,16 +6458,6 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@expo/require-utils@57.0.4(supports-color@8.1.1)(typescript@5.9.3)':
|
||||
dependencies:
|
||||
'@babel/code-frame': 7.29.7
|
||||
'@babel/core': 7.29.7(supports-color@8.1.1)
|
||||
'@babel/plugin-transform-modules-commonjs': 7.29.7(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1)
|
||||
optionalDependencies:
|
||||
typescript: 5.9.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@expo/rudder-sdk-node@1.1.1':
|
||||
dependencies:
|
||||
'@expo/bunyan': 4.0.1
|
||||
@@ -7260,8 +7217,6 @@ snapshots:
|
||||
|
||||
'@xmldom/xmldom@0.7.13': {}
|
||||
|
||||
'@xmldom/xmldom@0.8.14': {}
|
||||
|
||||
'@xmldom/xmldom@0.9.11': {}
|
||||
|
||||
abort-controller@3.0.0:
|
||||
@@ -7496,8 +7451,6 @@ snapshots:
|
||||
|
||||
balanced-match@1.0.2: {}
|
||||
|
||||
balanced-match@4.0.4: {}
|
||||
|
||||
base64-arraybuffer@1.0.2: {}
|
||||
|
||||
base64-js@1.5.1: {}
|
||||
@@ -7512,6 +7465,8 @@ snapshots:
|
||||
|
||||
binary-extensions@2.3.0: {}
|
||||
|
||||
boolbase@1.0.0: {}
|
||||
|
||||
bplist-creator@0.0.7:
|
||||
dependencies:
|
||||
stream-buffers: 2.2.0
|
||||
@@ -7537,10 +7492,6 @@ snapshots:
|
||||
dependencies:
|
||||
balanced-match: 1.0.2
|
||||
|
||||
brace-expansion@5.0.9:
|
||||
dependencies:
|
||||
balanced-match: 4.0.4
|
||||
|
||||
braces@3.0.3:
|
||||
dependencies:
|
||||
fill-range: 7.1.1
|
||||
@@ -7832,6 +7783,21 @@ snapshots:
|
||||
dependencies:
|
||||
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: {}
|
||||
|
||||
csstype@3.2.3: {}
|
||||
@@ -7907,11 +7873,29 @@ snapshots:
|
||||
|
||||
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:
|
||||
optionalDependencies:
|
||||
'@types/trusted-types': 2.0.7
|
||||
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:
|
||||
dependencies:
|
||||
dotenv: 16.4.7
|
||||
@@ -7944,6 +7928,8 @@ snapshots:
|
||||
dependencies:
|
||||
once: 1.4.0
|
||||
|
||||
entities@4.5.0: {}
|
||||
|
||||
env-editor@0.4.2: {}
|
||||
|
||||
error-ex@1.3.4:
|
||||
@@ -8149,7 +8135,7 @@ snapshots:
|
||||
dependencies:
|
||||
invariant: 2.2.4
|
||||
|
||||
expo-print@57.0.1(expo@52.0.49(@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))(@expo/metro-runtime@4.0.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@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)(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)):
|
||||
expo-print@14.0.3(expo@52.0.49(@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))(@expo/metro-runtime@4.0.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@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)(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)):
|
||||
dependencies:
|
||||
expo: 52.0.49(@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))(@expo/metro-runtime@4.0.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@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)(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)
|
||||
@@ -8181,17 +8167,9 @@ snapshots:
|
||||
- react-native
|
||||
- supports-color
|
||||
|
||||
expo-sharing@57.0.13(expo@52.0.49(@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))(@expo/metro-runtime@4.0.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@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)(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))(react@18.3.1)(supports-color@8.1.1)(typescript@5.9.3):
|
||||
expo-sharing@13.0.1(expo@52.0.49(@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))(@expo/metro-runtime@4.0.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@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)(supports-color@8.1.1)):
|
||||
dependencies:
|
||||
'@expo/config-plugins': 57.0.8(supports-color@8.1.1)(typescript@5.9.3)
|
||||
'@expo/config-types': 57.0.2
|
||||
'@expo/plist': 0.8.1
|
||||
expo: 52.0.49(@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))(@expo/metro-runtime@4.0.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@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)(supports-color@8.1.1)
|
||||
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)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
- typescript
|
||||
|
||||
expo-splash-screen@0.29.24(expo@52.0.49(@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))(@expo/metro-runtime@4.0.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@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)(supports-color@8.1.1))(supports-color@8.1.1):
|
||||
dependencies:
|
||||
@@ -8481,8 +8459,6 @@ snapshots:
|
||||
|
||||
getenv@1.0.0: {}
|
||||
|
||||
getenv@2.0.0: {}
|
||||
|
||||
glob-parent@5.1.2:
|
||||
dependencies:
|
||||
is-glob: 4.0.3
|
||||
@@ -8500,12 +8476,6 @@ snapshots:
|
||||
package-json-from-dist: 1.0.1
|
||||
path-scurry: 1.11.1
|
||||
|
||||
glob@13.0.6:
|
||||
dependencies:
|
||||
minimatch: 10.2.6
|
||||
minipass: 7.1.3
|
||||
path-scurry: 2.0.2
|
||||
|
||||
glob@7.2.3:
|
||||
dependencies:
|
||||
fs.realpath: 1.0.0
|
||||
@@ -8938,8 +8908,6 @@ snapshots:
|
||||
|
||||
lru-cache@10.4.3: {}
|
||||
|
||||
lru-cache@11.5.2: {}
|
||||
|
||||
lru-cache@5.1.1:
|
||||
dependencies:
|
||||
yallist: 3.1.1
|
||||
@@ -8971,6 +8939,8 @@ snapshots:
|
||||
crypt: 0.0.2
|
||||
is-buffer: 1.1.6
|
||||
|
||||
mdn-data@2.0.14: {}
|
||||
|
||||
memoize-one@5.2.1: {}
|
||||
|
||||
merge-options@3.0.4:
|
||||
@@ -9172,10 +9142,6 @@ snapshots:
|
||||
|
||||
mimic-fn@2.1.0: {}
|
||||
|
||||
minimatch@10.2.6:
|
||||
dependencies:
|
||||
brace-expansion: 5.0.9
|
||||
|
||||
minimatch@3.1.5:
|
||||
dependencies:
|
||||
brace-expansion: 1.1.18
|
||||
@@ -9304,6 +9270,10 @@ snapshots:
|
||||
dependencies:
|
||||
path-key: 3.1.1
|
||||
|
||||
nth-check@2.1.1:
|
||||
dependencies:
|
||||
boolbase: 1.0.0
|
||||
|
||||
nullthrows@1.1.1: {}
|
||||
|
||||
ob1@0.81.5:
|
||||
@@ -9420,11 +9390,6 @@ snapshots:
|
||||
lru-cache: 10.4.3
|
||||
minipass: 7.1.3
|
||||
|
||||
path-scurry@2.0.2:
|
||||
dependencies:
|
||||
lru-cache: 11.5.2
|
||||
minipass: 7.1.3
|
||||
|
||||
path-type@4.0.0: {}
|
||||
|
||||
pathe@2.0.3: {}
|
||||
@@ -9679,12 +9644,20 @@ 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)
|
||||
warn-once: 0.1.1
|
||||
|
||||
react-native-svg@15.8.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):
|
||||
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)
|
||||
warn-once: 0.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:
|
||||
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)
|
||||
whatwg-url-without-unicode: 8.0.0-3
|
||||
|
||||
react-native-view-shot@5.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))(react@18.3.1):
|
||||
react-native-view-shot@4.0.3(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:
|
||||
html2canvas: 1.4.1
|
||||
react: 18.3.1
|
||||
|
||||
@@ -1,7 +1,19 @@
|
||||
packages:
|
||||
- "apps/*"
|
||||
- "packages/*"
|
||||
|
||||
allowBuilds:
|
||||
core-js: set this to true or false
|
||||
core-js: true
|
||||
esbuild: true
|
||||
sharp: true
|
||||
"@esbuild/darwin-arm64": true
|
||||
"@next/swc-darwin-arm64": true
|
||||
"@swc/core": true
|
||||
|
||||
onlyBuiltDependencies:
|
||||
- "core-js"
|
||||
- "@swc/core"
|
||||
- "esbuild"
|
||||
- "sharp"
|
||||
- "@esbuild/darwin-arm64"
|
||||
- "@next/swc-darwin-arm64"
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
-- QR Stand Templates table for dynamic server-driven template engine
|
||||
create table if not exists qr_stand_templates (
|
||||
id uuid primary key default gen_random_uuid(),
|
||||
key text not null unique,
|
||||
name text not null,
|
||||
category_name text,
|
||||
aspect_ratio text default '1/1',
|
||||
bg_type text default 'gradient',
|
||||
bg_image_url text,
|
||||
bg_gradient text,
|
||||
plaque_color text,
|
||||
border_accent text,
|
||||
accent_text text,
|
||||
header_text text,
|
||||
sub_text text,
|
||||
divider_color text,
|
||||
qr_config jsonb,
|
||||
layers jsonb,
|
||||
is_active boolean not null default true,
|
||||
created_at timestamptz not null default now(),
|
||||
updated_at timestamptz not null default now()
|
||||
);
|
||||
|
||||
-- Public read access for mobile and web
|
||||
alter table qr_stand_templates enable row level security;
|
||||
|
||||
create policy "Public read active templates"
|
||||
on qr_stand_templates for select
|
||||
using (is_active = true);
|
||||
|
||||
create policy "Admins can manage templates"
|
||||
on qr_stand_templates for all
|
||||
using (true);
|
||||
@@ -0,0 +1,88 @@
|
||||
<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>
|
||||
|
After Width: | Height: | Size: 4.5 KiB |
@@ -0,0 +1,71 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 400 650" width="400" height="650">
|
||||
<defs>
|
||||
<!-- Define a linear gradient for gold effect -->
|
||||
<linearGradient id="goldGradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" stop-color="#D4AF37"/>
|
||||
<stop offset="100%" stop-color="#FFD700"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<!-- Background Canvas -->
|
||||
<rect id="bg-canvas" fill="#451A03" width="400" height="650"/>
|
||||
<!-- Main Menu Card Panel -->
|
||||
<rect id="main-card" x="20" y="20" width="360" height="610" rx="24" fill="#000000" stroke="url(#goldGradient)" stroke-width="2"/>
|
||||
<!-- Layer for Icons/Graphics -->
|
||||
<g id="layer-icons">
|
||||
<!-- Top decorative line -->
|
||||
<line x1="80" y1="165" x2="320" y2="165" stroke="url(#goldGradient)" stroke-width="1" fill="#D97706"/>
|
||||
<!-- Bottom decorative line -->
|
||||
<line x1="80" y1="535" x2="320" y2="535" stroke="url(#goldGradient)" stroke-width="1" fill="#B45309"/>
|
||||
<!-- Subtle corner accents (L-shapes) -->
|
||||
<path d="M40 40 L40 70 M40 40 L70 40" stroke="url(#goldGradient)" stroke-width="1.5" stroke-linecap="round" fill="#D97706"/>
|
||||
<path d="M360 40 L360 70 M360 40 L330 40" stroke="url(#goldGradient)" stroke-width="1.5" stroke-linecap="round" fill="#B45309"/>
|
||||
<path d="M40 610 L40 580 M40 610 L70 610" stroke="url(#goldGradient)" stroke-width="1.5" stroke-linecap="round" fill="#D97706"/>
|
||||
<path d="M360 610 L360 580 M360 610 L330 610" stroke="url(#goldGradient)" stroke-width="1.5" stroke-linecap="round" fill="#B45309"/>
|
||||
<!-- Minimalist Fork and Knife Line Art -->
|
||||
<g transform="translate(0, 15)">
|
||||
<!-- Fork -->
|
||||
<path d="M185 550 L185 560 M188 550 L188 560 M191 550 L191 560 M188 560 L188 565" stroke="url(#goldGradient)" stroke-width="1.5" stroke-linecap="round" fill="#D97706"/>
|
||||
<!-- Knife -->
|
||||
<path d="M205 550 L205 560 L208 560 L208 552 C208 550 206.5 549 205 549 Z M206.5 560 L206.5 565" stroke="url(#goldGradient)" stroke-width="1.5" stroke-linecap="round" fill="#B45309"/>
|
||||
</g>
|
||||
</g>
|
||||
<!-- QR Placeholder -->
|
||||
<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>
|
||||
<!-- Layer for Texts -->
|
||||
<g id="layer-texts" fill="#c10101">
|
||||
<!-- Table Info badge -->
|
||||
<rect x="140" y="40" width="120" height="30" rx="15" fill="url(#goldGradient)"/>
|
||||
<text x="200" y="60" font-family="Inter, sans-serif" font-size="14" font-weight="600" fill="#1A1A1D" text-anchor="middle">
|
||||
MASA NO: 01
|
||||
</text>
|
||||
<!-- Restaurant Name -->
|
||||
<text x="200" y="120" font-family="Inter, sans-serif" font-size="26" font-weight="700" fill="url(#goldGradient)" text-anchor="middle">
|
||||
Gourmet Bistro
|
||||
</text>
|
||||
<!-- Tagline / Subtitle -->
|
||||
<text x="200" y="145" font-family="Inter, sans-serif" font-size="12" font-weight="400" fill="#E4E4E7" text-anchor="middle">
|
||||
Temassız Dijital Menü
|
||||
</text>
|
||||
<!-- "MENÜYÜ GÖRMEK İÇİN OKUTUN" -->
|
||||
<text x="200" y="480" font-family="Inter, sans-serif" font-size="16" font-weight="700" fill="url(#goldGradient)" text-anchor="middle">
|
||||
MENÜYÜ GÖRMEK İÇİN OKUTUN
|
||||
</text>
|
||||
<!-- Instruction note -->
|
||||
<text x="200" y="505" font-family="Inter, sans-serif" font-size="11" font-weight="400" fill="#A1A1AA" text-anchor="middle">
|
||||
Kameranızı QR Koda Odaklayın
|
||||
</text>
|
||||
<!-- WiFi & Social Media footer info -->
|
||||
<text x="200" y="595" font-family="Inter, sans-serif" font-size="11" font-weight="400" fill="#A1A1AA" text-anchor="middle">
|
||||
Ücretsiz Wi-Fi: GourmetBistro_Guest
|
||||
</text>
|
||||
<text x="200" y="610" font-family="Inter, sans-serif" font-size="11" font-weight="400" fill="#A1A1AA" text-anchor="middle">
|
||||
Bizi Takip Edin: @GourmetBistro
|
||||
</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.0 KiB |
@@ -0,0 +1,285 @@
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 400 650"
|
||||
width="400"
|
||||
height="650"
|
||||
>
|
||||
<g id="layer-background">
|
||||
<rect
|
||||
id="bg-canvas"
|
||||
x="0"
|
||||
y="0"
|
||||
width="400"
|
||||
height="650"
|
||||
fill="#0B0E14"
|
||||
/>
|
||||
</g>
|
||||
|
||||
<g id="layer-card">
|
||||
<!-- Ana Lüks Kart Arka Planı (Gece Yarısı Laciverti & Kömür) -->
|
||||
<rect
|
||||
id="main-card"
|
||||
x="20"
|
||||
y="20"
|
||||
width="360"
|
||||
height="610"
|
||||
rx="24"
|
||||
fill="#121721"
|
||||
stroke="#D4AF37"
|
||||
stroke-width="1.5"
|
||||
/>
|
||||
|
||||
<!-- İç Çift Altın Bordür Hattı -->
|
||||
<rect
|
||||
id="inner-border"
|
||||
x="28"
|
||||
y="28"
|
||||
width="344"
|
||||
height="594"
|
||||
rx="18"
|
||||
fill="none"
|
||||
stroke="#C5A059"
|
||||
stroke-width="0.8"
|
||||
stroke-opacity="0.4"
|
||||
/>
|
||||
<rect
|
||||
x="32"
|
||||
y="32"
|
||||
width="336"
|
||||
height="586"
|
||||
rx="14"
|
||||
fill="none"
|
||||
stroke="#C5A059"
|
||||
stroke-width="0.5"
|
||||
stroke-opacity="0.2"
|
||||
/>
|
||||
</g>
|
||||
|
||||
<g id="layer-icons">
|
||||
<!-- Üst Köşe Geometrik Art-Deco Motifleri -->
|
||||
<path
|
||||
d="M 38 48 L 48 38 M 38 38 L 52 38 M 38 38 L 38 52"
|
||||
stroke="#D4AF37"
|
||||
stroke-width="1"
|
||||
stroke-linecap="round"
|
||||
stroke-opacity="0.6"
|
||||
fill="none"
|
||||
/>
|
||||
<path
|
||||
d="M 362 48 L 352 38 M 362 38 L 348 38 M 362 38 L 362 52"
|
||||
stroke="#D4AF37"
|
||||
stroke-width="1"
|
||||
stroke-linecap="round"
|
||||
stroke-opacity="0.6"
|
||||
fill="none"
|
||||
/>
|
||||
|
||||
<!-- Üst Masa Numarası Kapsülü -->
|
||||
<rect
|
||||
x="148"
|
||||
y="44"
|
||||
width="104"
|
||||
height="22"
|
||||
rx="11"
|
||||
fill="#1A2230"
|
||||
stroke="#D4AF37"
|
||||
stroke-width="0.8"
|
||||
/>
|
||||
<circle cx="158" cy="55" r="1.5" fill="#D4AF37" />
|
||||
<circle cx="242" cy="55" r="1.5" fill="#D4AF37" />
|
||||
|
||||
<!-- Merkezi Gastronomi & Yıldız Amblemi -->
|
||||
<g transform="translate(200, 84)">
|
||||
<circle cx="0" cy="0" r="11" fill="none" stroke="#D4AF37" stroke-width="0.8" stroke-dasharray="2 2" />
|
||||
<polygon points="0,-7 2,-2 7,0 2,2 0,7 -2,2 -7,0 -2,-2" fill="#D4AF37" />
|
||||
</g>
|
||||
|
||||
<!-- Zarif Tipografik Ayırıcı -->
|
||||
<line x1="75" y1="172" x2="175" y2="172" stroke="#D4AF37" stroke-width="0.75" stroke-opacity="0.5" />
|
||||
<polygon points="200,169.5 202.5,172 200,174.5 197.5,172" fill="#D4AF37" />
|
||||
<line x1="225" y1="172" x2="325" y2="172" stroke="#D4AF37" stroke-width="0.75" stroke-opacity="0.5" />
|
||||
|
||||
<!-- QR Alanı Hedef Odak Çerçeveleri -->
|
||||
<path d="M 80 205 L 80 195 L 90 195" fill="none" stroke="#D4AF37" stroke-width="2" stroke-linecap="round" />
|
||||
<path d="M 320 205 L 320 195 L 310 195" fill="none" stroke="#D4AF37" stroke-width="2" stroke-linecap="round" />
|
||||
<path d="M 80 445 L 80 455 L 90 455" fill="none" stroke="#D4AF37" stroke-width="2" stroke-linecap="round" />
|
||||
<path d="M 320 445 L 320 455 L 310 455" fill="none" stroke="#D4AF37" stroke-width="2" stroke-linecap="round" />
|
||||
|
||||
<!-- Alt Bilgi Kartı Paneli -->
|
||||
<rect
|
||||
x="36"
|
||||
y="520"
|
||||
width="328"
|
||||
height="84"
|
||||
rx="14"
|
||||
fill="#171F2C"
|
||||
stroke="#D4AF37"
|
||||
stroke-width="0.8"
|
||||
stroke-opacity="0.4"
|
||||
/>
|
||||
<line x1="200" y1="530" x2="200" y2="594" stroke="#D4AF37" stroke-width="0.5" stroke-opacity="0.25" />
|
||||
|
||||
<!-- Minimal WiFi Vektörü -->
|
||||
<g transform="translate(62, 545)">
|
||||
<path d="M 0 4 A 12 12 0 0 1 18 4" fill="none" stroke="#D4AF37" stroke-width="1.4" stroke-linecap="round" />
|
||||
<path d="M 3.5 8 A 7 7 0 0 1 14.5 8" fill="none" stroke="#D4AF37" stroke-width="1.4" stroke-linecap="round" />
|
||||
<circle cx="9" cy="12" r="1.2" fill="#D4AF37" />
|
||||
</g>
|
||||
|
||||
<!-- Minimal Instagram / Web Vektörü -->
|
||||
<g transform="translate(224, 545)">
|
||||
<rect x="0" y="0" width="15" height="15" rx="4" fill="none" stroke="#D4AF37" stroke-width="1.4" />
|
||||
<circle cx="7.5" cy="7.5" r="3.2" fill="none" stroke="#D4AF37" stroke-width="1.1" />
|
||||
<circle cx="11.5" cy="3.5" r="0.75" fill="#D4AF37" />
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<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, Arial, sans-serif"
|
||||
font-size="12"
|
||||
font-weight="600"
|
||||
fill="#71717A"
|
||||
text-anchor="middle"
|
||||
>QR KOD ALANI</text>
|
||||
</g>
|
||||
|
||||
<g id="layer-texts">
|
||||
<!-- Masa Numarası -->
|
||||
<text
|
||||
x="200"
|
||||
y="59"
|
||||
font-family="Inter, Arial, sans-serif"
|
||||
font-size="9"
|
||||
font-weight="700"
|
||||
letter-spacing="2.5"
|
||||
fill="#E5C378"
|
||||
text-anchor="middle"
|
||||
>TABLE • 01</text>
|
||||
|
||||
<!-- Restoran Logosu & İsmi -->
|
||||
<text
|
||||
x="200"
|
||||
y="126"
|
||||
font-family="'Playfair Display', 'Cinzel', Georgia, serif"
|
||||
font-size="28"
|
||||
font-weight="700"
|
||||
letter-spacing="1.5"
|
||||
fill="#F9F6F0"
|
||||
text-anchor="middle"
|
||||
>L’ATELIER BISTRO</text>
|
||||
|
||||
<!-- Fransızca / Uluslararası Alt Başlık -->
|
||||
<text
|
||||
x="200"
|
||||
y="152"
|
||||
font-family="Inter, Arial, sans-serif"
|
||||
font-size="8.5"
|
||||
font-weight="600"
|
||||
letter-spacing="4"
|
||||
fill="#D4AF37"
|
||||
text-anchor="middle"
|
||||
>FINE DINING & COCKTAILS</text>
|
||||
|
||||
<!-- Çağrı Metni / Talimat -->
|
||||
<text
|
||||
x="200"
|
||||
y="480"
|
||||
font-family="Inter, Arial, sans-serif"
|
||||
font-size="11.5"
|
||||
font-weight="700"
|
||||
letter-spacing="2"
|
||||
fill="#F9F6F0"
|
||||
text-anchor="middle"
|
||||
>DİJİTAL MENÜ İÇİN OKUTUN</text>
|
||||
|
||||
<text
|
||||
x="200"
|
||||
y="498"
|
||||
font-family="'Playfair Display', Georgia, serif"
|
||||
font-size="10.5"
|
||||
font-style="italic"
|
||||
font-weight="400"
|
||||
letter-spacing="0.5"
|
||||
fill="#94A3B8"
|
||||
text-anchor="middle"
|
||||
>Kameranızı QR koda yaklaştırınız</text>
|
||||
|
||||
<!-- Sol Sütun: Yüksek Hızlı WiFi Bilgileri -->
|
||||
<text
|
||||
x="126"
|
||||
y="550"
|
||||
font-family="Inter, Arial, sans-serif"
|
||||
font-size="9"
|
||||
font-weight="700"
|
||||
letter-spacing="1.2"
|
||||
fill="#E5C378"
|
||||
text-anchor="middle"
|
||||
>GUEST WIFI</text>
|
||||
|
||||
<text
|
||||
x="126"
|
||||
y="567"
|
||||
font-family="Inter, Arial, sans-serif"
|
||||
font-size="8.5"
|
||||
font-weight="500"
|
||||
fill="#CBD5E1"
|
||||
text-anchor="middle"
|
||||
>Ağ: Atelier-Guest</text>
|
||||
|
||||
<text
|
||||
x="126"
|
||||
y="582"
|
||||
font-family="Inter, Arial, sans-serif"
|
||||
font-size="8.5"
|
||||
font-weight="500"
|
||||
fill="#CBD5E1"
|
||||
text-anchor="middle"
|
||||
>Şifre: bistro2026</text>
|
||||
|
||||
<!-- Sağ Sütun: Sosyal Medya & Rezervasyon -->
|
||||
<text
|
||||
x="286"
|
||||
y="550"
|
||||
font-family="Inter, Arial, sans-serif"
|
||||
font-size="8.5"
|
||||
font-weight="700"
|
||||
letter-spacing="1.2"
|
||||
fill="#E5C378"
|
||||
text-anchor="middle"
|
||||
>SOSYAL MEDYA</text>
|
||||
|
||||
<text
|
||||
x="286"
|
||||
y="575"
|
||||
font-family="Inter, Arial, sans-serif"
|
||||
font-size="10.5"
|
||||
font-weight="600"
|
||||
letter-spacing="0.5"
|
||||
fill="#F9F6F0"
|
||||
text-anchor="middle"
|
||||
>@latelier.bistro</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 7.1 KiB |
@@ -0,0 +1,275 @@
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 400 650"
|
||||
width="400"
|
||||
height="650"
|
||||
>
|
||||
<g id="layer-background">
|
||||
<!-- Koyu Doku / Kontrast Arka Plan -->
|
||||
<rect
|
||||
id="bg-canvas"
|
||||
x="0"
|
||||
y="0"
|
||||
width="400"
|
||||
height="650"
|
||||
fill="#252C36"
|
||||
/>
|
||||
</g>
|
||||
|
||||
<g id="layer-card">
|
||||
<!-- Sıcak Parşömen / Vintage Krem Ana Kart -->
|
||||
<rect
|
||||
id="main-card"
|
||||
x="20"
|
||||
y="20"
|
||||
width="360"
|
||||
height="610"
|
||||
rx="20"
|
||||
fill="#F6EFE2"
|
||||
stroke="#3A2312"
|
||||
stroke-width="1.5"
|
||||
/>
|
||||
<!-- Çift İnce Vintage Çerçeve -->
|
||||
<rect
|
||||
x="28"
|
||||
y="28"
|
||||
width="344"
|
||||
height="594"
|
||||
rx="14"
|
||||
fill="none"
|
||||
stroke="#8C6239"
|
||||
stroke-width="1"
|
||||
stroke-opacity="0.4"
|
||||
/>
|
||||
<rect
|
||||
x="32"
|
||||
y="32"
|
||||
width="336"
|
||||
height="586"
|
||||
rx="10"
|
||||
fill="none"
|
||||
stroke="#8C6239"
|
||||
stroke-width="0.5"
|
||||
stroke-opacity="0.25"
|
||||
/>
|
||||
</g>
|
||||
|
||||
<g id="layer-icons">
|
||||
<!-- Sol Üst: Botanik Kahve / Çay Dalı -->
|
||||
<g transform="translate(36, 36) scale(0.75)" stroke="#3A2312" stroke-width="1.4" fill="none" stroke-linecap="round">
|
||||
<path d="M 0 0 C 15 15 25 35 30 65" />
|
||||
<path d="M 10 12 C 5 2 20 -2 22 8 C 22 18 10 12 10 12 Z" fill="#EADCC7" />
|
||||
<path d="M 18 27 C 12 20 28 14 32 23 C 32 32 18 27 18 27 Z" fill="#EADCC7" />
|
||||
<path d="M 24 45 C 32 38 45 48 38 56 C 30 62 24 45 24 45 Z" fill="#EADCC7" />
|
||||
</g>
|
||||
|
||||
<!-- Sağ Üst: Kuşbakışı Latte Art Fincanı -->
|
||||
<g transform="translate(340, 52)">
|
||||
<!-- Tabak -->
|
||||
<circle cx="0" cy="0" r="32" fill="#EADCC7" stroke="#3A2312" stroke-width="1.2" />
|
||||
<circle cx="0" cy="0" r="26" fill="#F6EFE2" stroke="#8C6239" stroke-width="0.8" stroke-dasharray="2 2" />
|
||||
<!-- Fincan Kenarı & Kahve -->
|
||||
<circle cx="0" cy="0" r="21" fill="#7A4B26" stroke="#3A2312" stroke-width="1.4" />
|
||||
<!-- Latte Art / Rosetta Yaprakları -->
|
||||
<path d="M 0 10 C -8 4 -4 -6 0 -12 C 4 -6 8 4 0 10 Z" fill="#F6EFE2" />
|
||||
<path d="M 0 4 C -5 0 -2 -5 0 -8 C 2 -5 5 0 0 4 Z" fill="#7A4B26" />
|
||||
<circle cx="0" cy="-14" r="1.8" fill="#F6EFE2" />
|
||||
</g>
|
||||
|
||||
<!-- Sol Orta: Küçük Kahve Fincanı & Tabağı -->
|
||||
<g transform="translate(48, 250)">
|
||||
<ellipse cx="0" cy="0" rx="26" ry="26" fill="#EADCC7" stroke="#3A2312" stroke-width="1" />
|
||||
<circle cx="0" cy="0" r="17" fill="#6A3B18" stroke="#3A2312" stroke-width="1.2" />
|
||||
<!-- Basit Kalp Latte Deseni -->
|
||||
<path d="M -5 -3 C -8 -7 0 -10 0 -4 C 0 -10 8 -7 5 -3 C 2 3 0 7 0 7 C 0 7 -2 3 -5 -3 Z" fill="#F6EFE2" />
|
||||
<!-- Fincan Kulpu -->
|
||||
<path d="M 17 -4 C 23 -4 23 4 17 4" fill="none" stroke="#3A2312" stroke-width="1.5" />
|
||||
</g>
|
||||
|
||||
<!-- Sağ Orta: Dikey Yaprak İllüstrasyonu -->
|
||||
<g transform="translate(345, 230) scale(0.7)" stroke="#3A2312" stroke-width="1.3" fill="none">
|
||||
<path d="M 10 0 C 0 30 -5 70 5 110" />
|
||||
<path d="M 6 25 C 18 15 28 28 14 36 C 6 36 6 25 6 25 Z" fill="#EADCC7" />
|
||||
<path d="M 2 60 C -12 48 -20 62 -8 70 C 2 70 2 60 2 60 Z" fill="#EADCC7" />
|
||||
<path d="M 5 88 C 18 78 24 92 12 100 C 5 100 5 88 5 88 Z" fill="#EADCC7" />
|
||||
</g>
|
||||
|
||||
<!-- Üst Başlık Yan Süsleme Çizgileri -->
|
||||
<path d="M 142 66 C 148 64 154 68 160 66" fill="none" stroke="#8C6239" stroke-width="1.2" stroke-linecap="round" />
|
||||
<path d="M 240 66 C 246 68 252 64 258 66" fill="none" stroke="#8C6239" stroke-width="1.2" stroke-linecap="round" />
|
||||
|
||||
<!-- Sol Alt: Çekirdek ve Yaprak Grubu -->
|
||||
<g transform="translate(42, 530) scale(0.65)" stroke="#3A2312" stroke-width="1.4" fill="none">
|
||||
<path d="M 0 110 C 20 70 15 30 0 0" />
|
||||
<path d="M 8 30 C 24 15 35 30 18 42 Z" fill="#EADCC7" />
|
||||
<path d="M 12 70 C 30 55 40 72 20 84 Z" fill="#EADCC7" />
|
||||
</g>
|
||||
|
||||
<!-- Kahve Çekirdekleri Motifleri -->
|
||||
<!-- Sol Alt Çekirdek 1 -->
|
||||
<g transform="translate(108, 595) rotate(-20) scale(0.8)">
|
||||
<ellipse cx="0" cy="0" rx="9" ry="6.5" fill="#CBB296" stroke="#3A2312" stroke-width="1.2" />
|
||||
<path d="M -7 0 Q 0 3 0 0 Q 0 -3 7 0" fill="none" stroke="#3A2312" stroke-width="1.2" />
|
||||
</g>
|
||||
<!-- Sol Alt Çekirdek 2 -->
|
||||
<g transform="translate(128, 584) rotate(35) scale(0.8)">
|
||||
<ellipse cx="0" cy="0" rx="9" ry="6.5" fill="#CBB296" stroke="#3A2312" stroke-width="1.2" />
|
||||
<path d="M -7 0 Q 0 3 0 0 Q 0 -3 7 0" fill="none" stroke="#3A2312" stroke-width="1.2" />
|
||||
</g>
|
||||
<!-- Sağ Orta Çekirdek -->
|
||||
<g transform="translate(336, 360) rotate(-45) scale(0.7)">
|
||||
<ellipse cx="0" cy="0" rx="9" ry="6.5" fill="#CBB296" stroke="#3A2312" stroke-width="1.2" />
|
||||
<path d="M -7 0 Q 0 3 0 0 Q 0 -3 7 0" fill="none" stroke="#3A2312" stroke-width="1.2" />
|
||||
</g>
|
||||
|
||||
<!-- Sağ Alt: Sıcak Kahve Fincanı İllüstrasyonu -->
|
||||
<g transform="translate(325, 590)">
|
||||
<!-- Tabak -->
|
||||
<ellipse cx="0" cy="18" rx="28" ry="7" fill="#EADCC7" stroke="#3A2312" stroke-width="1.2" />
|
||||
<!-- Fincan Gövdesi -->
|
||||
<path d="M -18 2 C -18 16 18 16 18 2 Z" fill="#F6EFE2" stroke="#3A2312" stroke-width="1.4" />
|
||||
<ellipse cx="0" cy="2" rx="18" ry="4" fill="#6A3B18" stroke="#3A2312" stroke-width="1.2" />
|
||||
<!-- Kulp -->
|
||||
<path d="M 16 4 C 24 4 24 12 16 14" fill="none" stroke="#3A2312" stroke-width="1.3" />
|
||||
<!-- Buhar Çizgileri -->
|
||||
<path d="M -6 -4 C -8 -9 -4 -12 -6 -17" fill="none" stroke="#8C6239" stroke-width="1" stroke-linecap="round" />
|
||||
<path d="M 2 -5 C 0 -10 4 -13 2 -19" fill="none" stroke="#8C6239" stroke-width="1" stroke-linecap="round" />
|
||||
</g>
|
||||
|
||||
<!-- QR Alanı İnce Köşe Süsleri -->
|
||||
<path d="M 80 205 C 80 195 80 195 90 195" fill="none" stroke="#3A2312" stroke-width="2" stroke-linecap="round" />
|
||||
<path d="M 320 205 C 320 195 320 195 310 195" fill="none" stroke="#3A2312" stroke-width="2" stroke-linecap="round" />
|
||||
<path d="M 80 445 C 80 455 80 455 90 455" fill="none" stroke="#3A2312" stroke-width="2" stroke-linecap="round" />
|
||||
<path d="M 320 445 C 320 455 320 455 310 455" fill="none" stroke="#3A2312" stroke-width="2" stroke-linecap="round" />
|
||||
|
||||
<!-- Alt Est. Bölümü Çizgileri -->
|
||||
<line x1="145" y1="608" x2="175" y2="608" stroke="#8C6239" stroke-width="0.8" />
|
||||
<line x1="225" y1="608" x2="255" y2="608" stroke="#8C6239" stroke-width="0.8" />
|
||||
</g>
|
||||
|
||||
<g id="qr-placeholder" transform="translate(90, 215)">
|
||||
<!-- QR Kod Arka Planı (Hafif Sıcak Ton) -->
|
||||
<rect
|
||||
width="220"
|
||||
height="220"
|
||||
rx="20"
|
||||
fill="#FBF8F2"
|
||||
stroke="#D8C7B0"
|
||||
stroke-width="1.5"
|
||||
/>
|
||||
<rect
|
||||
x="10"
|
||||
y="10"
|
||||
width="200"
|
||||
height="200"
|
||||
rx="14"
|
||||
fill="#F5EFE6"
|
||||
stroke="#D8C7B0"
|
||||
stroke-dasharray="4 3"
|
||||
/>
|
||||
<!-- QR Merkezine Kahve Çekirdeği İkonu Rozeti -->
|
||||
<circle cx="110" cy="98" r="18" fill="#FBF8F2" stroke="#8C6239" stroke-width="1" />
|
||||
<g transform="translate(110, 98) scale(0.65)">
|
||||
<ellipse cx="-5" cy="2" rx="7" ry="5" fill="#3A2312" transform="rotate(-30)" />
|
||||
<ellipse cx="5" cy="0" rx="7" ry="5" fill="#3A2312" transform="rotate(40)" />
|
||||
</g>
|
||||
<text
|
||||
x="110"
|
||||
y="132"
|
||||
font-family="Inter, Arial, sans-serif"
|
||||
font-size="11"
|
||||
font-weight="700"
|
||||
letter-spacing="1"
|
||||
fill="#8C6239"
|
||||
text-anchor="middle"
|
||||
>QR KOD ALANI</text>
|
||||
</g>
|
||||
|
||||
<g id="layer-texts">
|
||||
<!-- Üst Başlık Takısı -->
|
||||
<text
|
||||
x="200"
|
||||
y="70"
|
||||
font-family="'Playfair Display', Georgia, serif"
|
||||
font-size="14"
|
||||
font-style="italic"
|
||||
font-weight="700"
|
||||
letter-spacing="4"
|
||||
fill="#3A2312"
|
||||
text-anchor="middle"
|
||||
>THE</text>
|
||||
|
||||
<!-- Ana Tipografi: El Yazısı / Retro Tabela Havası -->
|
||||
<text
|
||||
x="200"
|
||||
y="114"
|
||||
font-family="'Playfair Display', 'Brush Script MT', 'Great Vibes', Georgia, cursive, serif"
|
||||
font-size="34"
|
||||
font-weight="800"
|
||||
letter-spacing="0.5"
|
||||
fill="#2C1A0E"
|
||||
text-anchor="middle"
|
||||
>The Daily Grind</text>
|
||||
|
||||
<!-- Alt Kategori Başlığı -->
|
||||
<text
|
||||
x="200"
|
||||
y="146"
|
||||
font-family="Inter, Arial, sans-serif"
|
||||
font-size="13"
|
||||
font-weight="800"
|
||||
letter-spacing="3"
|
||||
fill="#3A2312"
|
||||
text-anchor="middle"
|
||||
>COFFEE & KITCHEN</text>
|
||||
|
||||
<!-- Büyük ve Belirgin Çağrı Metni -->
|
||||
<text
|
||||
x="200"
|
||||
y="488"
|
||||
font-family="Inter, Arial, sans-serif"
|
||||
font-size="19"
|
||||
font-weight="900"
|
||||
letter-spacing="1.5"
|
||||
fill="#2C1A0E"
|
||||
text-anchor="middle"
|
||||
>SCAN TO VIEW MENU</text>
|
||||
|
||||
<!-- Restoran & WiFi Bilgi Bandı -->
|
||||
<text
|
||||
x="200"
|
||||
y="520"
|
||||
font-family="Inter, Arial, sans-serif"
|
||||
font-size="10.5"
|
||||
font-weight="700"
|
||||
letter-spacing="0.5"
|
||||
fill="#3A2312"
|
||||
text-anchor="middle"
|
||||
>DAILY GRIND CAFE | WIFI: DAILY_GRIND_GUEST</text>
|
||||
|
||||
<!-- Şifre ve Masa Detayı -->
|
||||
<text
|
||||
x="200"
|
||||
y="542"
|
||||
font-family="Inter, Arial, sans-serif"
|
||||
font-size="9"
|
||||
font-weight="600"
|
||||
letter-spacing="1"
|
||||
fill="#8C6239"
|
||||
text-anchor="middle"
|
||||
>ŞİFRE: coffee2026 • MASA NO: 04</text>
|
||||
|
||||
<!-- Kuruluş / Est. Yılı -->
|
||||
<text
|
||||
x="200"
|
||||
y="611"
|
||||
font-family="'Playfair Display', Georgia, serif"
|
||||
font-size="11"
|
||||
font-style="italic"
|
||||
font-weight="700"
|
||||
letter-spacing="2"
|
||||
fill="#3A2312"
|
||||
text-anchor="middle"
|
||||
>EST. 2018</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 9.3 KiB |
|
Before Width: | Height: | Size: 1.6 MiB |
|
Before Width: | Height: | Size: 357 KiB |