first commit

This commit is contained in:
AyrisAI
2026-08-20 01:51:59 +03:00
commit 97b83c7fd4
109 changed files with 21215 additions and 0 deletions
+17
View File
@@ -0,0 +1,17 @@
{
"name": "@menulio/shared",
"private": true,
"version": "0.0.1",
"type": "module",
"main": "./src/index.ts",
"types": "./src/index.ts",
"scripts": {
"typecheck": "tsc --noEmit"
},
"dependencies": {
"zod": "^3.23.8"
},
"devDependencies": {
"typescript": "^5.6.3"
}
}
+3
View File
@@ -0,0 +1,3 @@
export * from "./slug.js";
export * from "./theme.js";
export * from "./types.js";
+30
View File
@@ -0,0 +1,30 @@
// Slug rules per PRD §10: lowercase, Turkish characters normalized, spaces -> "-", safe charset.
const TR_MAP: Record<string, string> = {
ç: "c",
ğ: "g",
ı: "i",
ö: "o",
ş: "s",
ü: "u",
Ç: "c",
Ğ: "g",
İ: "i",
Ö: "o",
Ş: "s",
Ü: "u",
};
export function slugify(input: string): string {
const normalized = input
.split("")
.map((char) => TR_MAP[char] ?? char)
.join("");
return normalized
.toLowerCase()
.normalize("NFD")
.replace(/[̀-ͯ]/g, "")
.replace(/[^a-z0-9]+/g, "-")
.replace(/^-+|-+$/g, "")
.slice(0, 60);
}
+12
View File
@@ -0,0 +1,12 @@
import { z } from "zod";
// Theme config contract shared by the theme engine (PRD §8): mobile writes it,
// the public web renderer consumes it. Changing a theme must never touch menu data.
export const themeConfigSchema = z.object({
primaryColor: z.string(),
background: z.string(),
productLayout: z.enum(["card", "list"]),
categoryLayout: z.enum(["accordion", "tabs", "flat"]),
});
export type ThemeConfig = z.infer<typeof themeConfigSchema>;
+133
View File
@@ -0,0 +1,133 @@
// Mirrors supabase/migrations/20260819000000_initial_schema.sql.
// Kept hand-written for V1; consider generating via `supabase gen types` once
// the schema stabilizes.
export type SubscriptionStatus = "active" | "grace_period" | "suspended" | "deleted";
export type AiImportStatus = "pending" | "processing" | "completed" | "failed";
export type DomainStatus = "pending" | "verified" | "failed";
export interface Restaurant {
id: string;
name: string;
slug: string;
logoUrl: string | null;
phone: string | null;
address: string | null;
createdAt: string;
}
export interface Location {
id: string;
restaurantId: string;
name: string;
address: string | null;
phone: string | null;
}
export interface Menu {
id: string;
locationId: string;
name: string;
isPublished: boolean;
publishedAt: string | null;
}
export interface MenuCategory {
id: string;
menuId: string;
name: string;
description: string | null;
sortOrder: number;
isActive: boolean;
}
export interface MenuItem {
id: string;
categoryId: string;
name: string;
description: string | null;
price: number;
imageUrl: string | null;
sortOrder: number;
isActive: boolean;
}
export interface MenuItemOption {
id: string;
menuItemId: string;
groupName: string;
label: string;
priceDelta: number;
sortOrder: number;
}
export interface AiImport {
id: string;
restaurantId: string;
sourceImage: string;
status: AiImportStatus;
model: string | null;
error: string | null;
processedAt: string | null;
}
export interface AiImportItem {
id: string;
aiImportId: string;
categoryName: string | null;
itemName: string;
description: string | null;
price: number | null;
confidence: number | null;
}
export interface AiExtractedItem {
id: string;
name: string;
description?: string | null;
price: number;
confidence: number;
}
export interface AiExtractedCategory {
id: string;
name: string;
items: AiExtractedItem[];
}
export interface AiImportResponse {
importId: string;
status: AiImportStatus;
model: string | null;
categories: AiExtractedCategory[];
totalItems: number;
lowConfidenceCount: number;
}
export interface AiImportApplyPayload {
categories: {
name: string;
items: {
name: string;
description?: string | null;
price: number;
}[];
}[];
}
export interface Subscription {
id: string;
restaurantId: string;
status: SubscriptionStatus;
productId: string | null;
trialEndsAt: string | null;
currentPeriodEndsAt: string | null;
}
export interface Domain {
id: string;
restaurantId: string;
hostname: string;
isCustom: boolean;
status: DomainStatus;
}
+9
View File
@@ -0,0 +1,9 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"module": "ESNext",
"moduleResolution": "Bundler",
"noEmit": true
},
"include": ["src"]
}