@promakeai/cli 0.4.7 → 0.4.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +71 -0
- package/dist/index.js +161 -168
- package/dist/registry/blog-core.json +7 -26
- package/dist/registry/blog-list-page.json +2 -2
- package/dist/registry/blog-section.json +1 -1
- package/dist/registry/cart-drawer.json +1 -1
- package/dist/registry/cart-page.json +1 -1
- package/dist/registry/category-section.json +1 -1
- package/dist/registry/checkout-page.json +1 -1
- package/dist/registry/contact-page-centered.json +1 -1
- package/dist/registry/contact-page-map-overlay.json +1 -1
- package/dist/registry/contact-page-map-split.json +1 -1
- package/dist/registry/contact-page-split.json +1 -1
- package/dist/registry/contact-page.json +1 -1
- package/dist/registry/docs/blog-core.md +12 -13
- package/dist/registry/docs/blog-list-page.md +1 -1
- package/dist/registry/docs/ecommerce-core.md +10 -13
- package/dist/registry/docs/featured-products.md +1 -1
- package/dist/registry/docs/post-detail-page.md +2 -2
- package/dist/registry/docs/product-detail-page.md +2 -2
- package/dist/registry/docs/products-page.md +1 -1
- package/dist/registry/ecommerce-core.json +5 -25
- package/dist/registry/featured-products.json +2 -2
- package/dist/registry/forgot-password-page-split.json +1 -1
- package/dist/registry/forgot-password-page.json +1 -1
- package/dist/registry/header-centered-pill.json +1 -1
- package/dist/registry/header-ecommerce.json +1 -1
- package/dist/registry/index.json +0 -1
- package/dist/registry/login-page-split.json +1 -1
- package/dist/registry/login-page.json +1 -1
- package/dist/registry/newsletter-section.json +1 -1
- package/dist/registry/post-card.json +1 -1
- package/dist/registry/post-detail-block.json +1 -1
- package/dist/registry/post-detail-page.json +3 -3
- package/dist/registry/product-card-detailed.json +1 -1
- package/dist/registry/product-card.json +1 -1
- package/dist/registry/product-detail-block.json +1 -1
- package/dist/registry/product-detail-page.json +3 -3
- package/dist/registry/product-detail-section.json +1 -1
- package/dist/registry/product-quick-view.json +1 -1
- package/dist/registry/products-page.json +2 -2
- package/dist/registry/register-page-split.json +1 -1
- package/dist/registry/register-page.json +1 -1
- package/dist/registry/related-products-block.json +1 -1
- package/dist/registry/reset-password-page-split.json +1 -1
- package/package.json +4 -2
- package/template/README.md +39 -58
- package/template/package.json +4 -3
- package/template/public/data/database.db +0 -0
- package/template/public/data/database.db-shm +0 -0
- package/template/public/data/database.db-wal +0 -0
- package/template/scripts/init-db.ts +13 -126
- package/template/src/App.tsx +8 -5
- package/template/src/components/FormField.tsx +5 -11
- package/template/src/db/index.ts +20 -0
- package/template/src/db/provider.tsx +77 -0
- package/template/src/db/schema.json +259 -0
- package/template/src/db/types.ts +195 -0
- package/template/src/hooks/use-debounced-value.ts +12 -0
- package/dist/registry/db.json +0 -129
- package/template/src/PasswordInput.tsx +0 -61
|
@@ -1,131 +1,18 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { execSync } from "child_process";
|
|
2
2
|
import path from "path";
|
|
3
|
-
import initSqlJs from "sql.js";
|
|
4
3
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
const cwd = process.cwd();
|
|
5
|
+
const schemaPath = path.join(cwd, "src", "db", "schema.json");
|
|
6
|
+
const outputDir = path.join(cwd, "src", "db");
|
|
7
|
+
const dbPath = path.join(cwd, "public", "data", "database.db");
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
// BLOG SISTEMI
|
|
11
|
-
// ============================================
|
|
9
|
+
const cmd = `dbcli generate --schema "${schemaPath}" --database "${dbPath}" --output "${outputDir}"`;
|
|
12
10
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
21
|
-
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
22
|
-
);
|
|
23
|
-
`);
|
|
24
|
-
|
|
25
|
-
db.exec(`
|
|
26
|
-
CREATE TABLE posts (
|
|
27
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
28
|
-
title TEXT NOT NULL,
|
|
29
|
-
slug TEXT UNIQUE NOT NULL,
|
|
30
|
-
content TEXT NOT NULL,
|
|
31
|
-
excerpt TEXT,
|
|
32
|
-
featured_image TEXT,
|
|
33
|
-
images TEXT,
|
|
34
|
-
author TEXT,
|
|
35
|
-
author_avatar TEXT,
|
|
36
|
-
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
37
|
-
published_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
38
|
-
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
39
|
-
tags TEXT,
|
|
40
|
-
read_time INTEGER DEFAULT 0,
|
|
41
|
-
view_count INTEGER DEFAULT 0,
|
|
42
|
-
featured INTEGER DEFAULT 0,
|
|
43
|
-
published INTEGER DEFAULT 1,
|
|
44
|
-
meta_description TEXT,
|
|
45
|
-
meta_keywords TEXT
|
|
46
|
-
);
|
|
47
|
-
`);
|
|
48
|
-
|
|
49
|
-
db.exec(`
|
|
50
|
-
CREATE TABLE post_categories (
|
|
51
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
52
|
-
post_id INTEGER NOT NULL,
|
|
53
|
-
category_id INTEGER NOT NULL,
|
|
54
|
-
is_primary INTEGER DEFAULT 0,
|
|
55
|
-
FOREIGN KEY (post_id) REFERENCES posts(id) ON DELETE CASCADE,
|
|
56
|
-
FOREIGN KEY (category_id) REFERENCES blog_categories(id) ON DELETE CASCADE
|
|
57
|
-
);
|
|
58
|
-
`);
|
|
59
|
-
|
|
60
|
-
// ============================================
|
|
61
|
-
// E-COMMERCE SISTEMI
|
|
62
|
-
// ============================================
|
|
63
|
-
|
|
64
|
-
db.exec(`
|
|
65
|
-
CREATE TABLE product_categories (
|
|
66
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
67
|
-
name TEXT NOT NULL,
|
|
68
|
-
slug TEXT UNIQUE NOT NULL,
|
|
69
|
-
description TEXT,
|
|
70
|
-
image TEXT,
|
|
71
|
-
parent_id INTEGER,
|
|
72
|
-
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
73
|
-
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
74
|
-
FOREIGN KEY (parent_id) REFERENCES product_categories(id) ON DELETE SET NULL
|
|
75
|
-
);
|
|
76
|
-
`);
|
|
77
|
-
|
|
78
|
-
db.exec(`
|
|
79
|
-
CREATE TABLE products (
|
|
80
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
81
|
-
name TEXT NOT NULL,
|
|
82
|
-
slug TEXT UNIQUE NOT NULL,
|
|
83
|
-
description TEXT,
|
|
84
|
-
price REAL NOT NULL,
|
|
85
|
-
sale_price REAL,
|
|
86
|
-
on_sale INTEGER DEFAULT 0,
|
|
87
|
-
images TEXT,
|
|
88
|
-
brand TEXT,
|
|
89
|
-
sku TEXT,
|
|
90
|
-
stock INTEGER DEFAULT 0,
|
|
91
|
-
tags TEXT,
|
|
92
|
-
rating REAL DEFAULT 0,
|
|
93
|
-
review_count INTEGER DEFAULT 0,
|
|
94
|
-
featured INTEGER DEFAULT 0,
|
|
95
|
-
is_new INTEGER DEFAULT 0,
|
|
96
|
-
published INTEGER DEFAULT 1,
|
|
97
|
-
specifications TEXT,
|
|
98
|
-
variants TEXT,
|
|
99
|
-
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
100
|
-
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
101
|
-
meta_description TEXT,
|
|
102
|
-
meta_keywords TEXT
|
|
103
|
-
);
|
|
104
|
-
`);
|
|
105
|
-
|
|
106
|
-
db.exec(`
|
|
107
|
-
CREATE TABLE product_category_relations (
|
|
108
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
109
|
-
product_id INTEGER NOT NULL,
|
|
110
|
-
category_id INTEGER NOT NULL,
|
|
111
|
-
is_primary INTEGER DEFAULT 0,
|
|
112
|
-
FOREIGN KEY (product_id) REFERENCES products(id) ON DELETE CASCADE,
|
|
113
|
-
FOREIGN KEY (category_id) REFERENCES product_categories(id) ON DELETE CASCADE
|
|
114
|
-
);
|
|
115
|
-
`);
|
|
116
|
-
|
|
117
|
-
// Database'i dosyaya kaydet
|
|
118
|
-
const data = db.export();
|
|
119
|
-
const buffer = Buffer.from(data);
|
|
120
|
-
|
|
121
|
-
const outputPath = path.join(process.cwd(), "public/data/database.db");
|
|
122
|
-
fs.mkdirSync(path.dirname(outputPath), { recursive: true });
|
|
123
|
-
fs.writeFileSync(outputPath, buffer);
|
|
124
|
-
|
|
125
|
-
console.log(`Database olusturuldu: ${outputPath}`);
|
|
126
|
-
console.log("Tablolar (6):");
|
|
127
|
-
console.log(" - blog_categories, posts, post_categories");
|
|
128
|
-
console.log(" - product_categories, products, product_category_relations");
|
|
11
|
+
try {
|
|
12
|
+
console.log("Generating database with dbcli...");
|
|
13
|
+
execSync(cmd, { stdio: "inherit" });
|
|
14
|
+
console.log(`Database created: ${dbPath}`);
|
|
15
|
+
} catch (err) {
|
|
16
|
+
console.error("Failed to generate database. Ensure dbcli is installed globally.");
|
|
17
|
+
throw err;
|
|
129
18
|
}
|
|
130
|
-
|
|
131
|
-
createDatabase().catch(console.error);
|
package/template/src/App.tsx
CHANGED
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
import { TooltipProvider } from "@/components/ui/tooltip";
|
|
2
2
|
import { GoogleAnalytics } from "@/components/GoogleAnalytics";
|
|
3
3
|
import { ScriptInjector } from "@/components/ScriptInjector";
|
|
4
|
+
import { AppDbProvider } from "@/db";
|
|
4
5
|
import { Router } from "./router";
|
|
5
6
|
|
|
6
7
|
const App = () => {
|
|
7
8
|
return (
|
|
8
|
-
<
|
|
9
|
-
<
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
<AppDbProvider>
|
|
10
|
+
<TooltipProvider>
|
|
11
|
+
<GoogleAnalytics />
|
|
12
|
+
<ScriptInjector />
|
|
13
|
+
<Router />
|
|
14
|
+
</TooltipProvider>
|
|
15
|
+
</AppDbProvider>
|
|
13
16
|
);
|
|
14
17
|
};
|
|
15
18
|
|
|
@@ -1,17 +1,15 @@
|
|
|
1
1
|
import { Stack } from "./Stack";
|
|
2
2
|
import { Label } from "./ui/label";
|
|
3
3
|
|
|
4
|
-
// FormField.tsx
|
|
5
4
|
export interface FormFieldProps {
|
|
6
|
-
label:
|
|
5
|
+
label: string;
|
|
7
6
|
htmlFor?: string;
|
|
8
7
|
error?: string;
|
|
9
8
|
required?: boolean;
|
|
10
9
|
description?: string;
|
|
11
10
|
children: React.ReactNode;
|
|
12
|
-
gap?: 0 | 1 | 2 | 3 | 4 | 6 | 8 | 10 | 12;
|
|
11
|
+
gap?: 0 | 1 | 2 | 3 | 4 | 6 | 8 | 10 | 12; // Stack gap override
|
|
13
12
|
className?: string;
|
|
14
|
-
labelAction?: React.ReactNode; // YENİ
|
|
15
13
|
}
|
|
16
14
|
|
|
17
15
|
export function FormField({
|
|
@@ -23,16 +21,12 @@ export function FormField({
|
|
|
23
21
|
description,
|
|
24
22
|
gap = 2,
|
|
25
23
|
className,
|
|
26
|
-
labelAction, // YENİ
|
|
27
24
|
}: FormFieldProps) {
|
|
28
25
|
return (
|
|
29
26
|
<Stack gap={gap} className={className}>
|
|
30
|
-
<
|
|
31
|
-
<
|
|
32
|
-
|
|
33
|
-
</Label>
|
|
34
|
-
{labelAction}
|
|
35
|
-
</div>
|
|
27
|
+
<Label htmlFor={htmlFor}>
|
|
28
|
+
{label} {required && <span className="text-destructive">*</span>}
|
|
29
|
+
</Label>
|
|
36
30
|
{children}
|
|
37
31
|
{description && (
|
|
38
32
|
<p className="text-sm text-muted-foreground">
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export { AppDbProvider } from "./provider";
|
|
2
|
+
|
|
3
|
+
import { parseJSONSchema } from "@promakeai/dbreact";
|
|
4
|
+
import schemaJson from "./schema.json";
|
|
5
|
+
export const schema = parseJSONSchema(schemaJson as any);
|
|
6
|
+
|
|
7
|
+
export {
|
|
8
|
+
useDb,
|
|
9
|
+
useAdapter,
|
|
10
|
+
useDbLang,
|
|
11
|
+
useDbList,
|
|
12
|
+
useDbGet,
|
|
13
|
+
useDbCreate,
|
|
14
|
+
useDbUpdate,
|
|
15
|
+
useDbDelete,
|
|
16
|
+
SqliteAdapter,
|
|
17
|
+
parseJSONSchema,
|
|
18
|
+
} from "@promakeai/dbreact";
|
|
19
|
+
|
|
20
|
+
export * from "./types";
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { useEffect, useState, type ReactNode } from "react";
|
|
2
|
+
import { useTranslation } from "react-i18next";
|
|
3
|
+
import { DbProvider, SqliteAdapter, parseJSONSchema } from "@promakeai/dbreact";
|
|
4
|
+
import constants from "@/constants/constants.json";
|
|
5
|
+
import schemaJson from "./schema.json";
|
|
6
|
+
|
|
7
|
+
const schema = parseJSONSchema(schemaJson as any);
|
|
8
|
+
|
|
9
|
+
interface AppDbProviderProps {
|
|
10
|
+
children: ReactNode;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const DEFAULT_LANG = constants?.site?.defaultLanguage || "en";
|
|
14
|
+
|
|
15
|
+
export function AppDbProvider({ children }: AppDbProviderProps) {
|
|
16
|
+
const { i18n } = useTranslation();
|
|
17
|
+
const [adapter, setAdapter] = useState<SqliteAdapter | null>(null);
|
|
18
|
+
const [error, setError] = useState<Error | null>(null);
|
|
19
|
+
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
let cancelled = false;
|
|
22
|
+
|
|
23
|
+
async function loadDb() {
|
|
24
|
+
try {
|
|
25
|
+
const response = await fetch("/data/database.db");
|
|
26
|
+
if (!response.ok) {
|
|
27
|
+
throw new Error(
|
|
28
|
+
`Failed to load database: ${response.status} ${response.statusText}`
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const buffer = await response.arrayBuffer();
|
|
33
|
+
if (cancelled) return;
|
|
34
|
+
|
|
35
|
+
const dbAdapter = new SqliteAdapter({
|
|
36
|
+
schema,
|
|
37
|
+
defaultLang: DEFAULT_LANG,
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
await dbAdapter.connect();
|
|
41
|
+
await dbAdapter.import(new Uint8Array(buffer));
|
|
42
|
+
|
|
43
|
+
if (!cancelled) {
|
|
44
|
+
setAdapter(dbAdapter);
|
|
45
|
+
}
|
|
46
|
+
} catch (err) {
|
|
47
|
+
if (!cancelled) {
|
|
48
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
loadDb();
|
|
54
|
+
|
|
55
|
+
return () => {
|
|
56
|
+
cancelled = true;
|
|
57
|
+
};
|
|
58
|
+
}, []);
|
|
59
|
+
|
|
60
|
+
if (error) {
|
|
61
|
+
console.error(error);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (!adapter) {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return (
|
|
69
|
+
<DbProvider
|
|
70
|
+
adapter={adapter}
|
|
71
|
+
lang={i18n?.language || DEFAULT_LANG}
|
|
72
|
+
fallbackLang={DEFAULT_LANG}
|
|
73
|
+
>
|
|
74
|
+
{children}
|
|
75
|
+
</DbProvider>
|
|
76
|
+
);
|
|
77
|
+
}
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "promake",
|
|
3
|
+
"languages": [
|
|
4
|
+
"en",
|
|
5
|
+
"tr"
|
|
6
|
+
],
|
|
7
|
+
"defaultLanguage": "en",
|
|
8
|
+
"tables": {
|
|
9
|
+
"blog_categories": {
|
|
10
|
+
"id": {
|
|
11
|
+
"type": "id"
|
|
12
|
+
},
|
|
13
|
+
"name": {
|
|
14
|
+
"type": "string",
|
|
15
|
+
"nullable": false,
|
|
16
|
+
"translatable": true
|
|
17
|
+
},
|
|
18
|
+
"slug": {
|
|
19
|
+
"type": "string",
|
|
20
|
+
"nullable": false,
|
|
21
|
+
"unique": true
|
|
22
|
+
},
|
|
23
|
+
"description": {
|
|
24
|
+
"type": "text",
|
|
25
|
+
"translatable": true
|
|
26
|
+
},
|
|
27
|
+
"image": {
|
|
28
|
+
"type": "text"
|
|
29
|
+
},
|
|
30
|
+
"created_at": {
|
|
31
|
+
"type": "timestamp"
|
|
32
|
+
},
|
|
33
|
+
"updated_at": {
|
|
34
|
+
"type": "timestamp"
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
"posts": {
|
|
38
|
+
"id": {
|
|
39
|
+
"type": "id"
|
|
40
|
+
},
|
|
41
|
+
"title": {
|
|
42
|
+
"type": "string",
|
|
43
|
+
"nullable": false,
|
|
44
|
+
"translatable": true
|
|
45
|
+
},
|
|
46
|
+
"slug": {
|
|
47
|
+
"type": "string",
|
|
48
|
+
"nullable": false,
|
|
49
|
+
"unique": true
|
|
50
|
+
},
|
|
51
|
+
"content": {
|
|
52
|
+
"type": "text",
|
|
53
|
+
"nullable": false,
|
|
54
|
+
"translatable": true
|
|
55
|
+
},
|
|
56
|
+
"excerpt": {
|
|
57
|
+
"type": "text",
|
|
58
|
+
"translatable": true
|
|
59
|
+
},
|
|
60
|
+
"featured_image": {
|
|
61
|
+
"type": "text"
|
|
62
|
+
},
|
|
63
|
+
"images": {
|
|
64
|
+
"type": [
|
|
65
|
+
"string"
|
|
66
|
+
]
|
|
67
|
+
},
|
|
68
|
+
"author": {
|
|
69
|
+
"type": "string",
|
|
70
|
+
"translatable": true
|
|
71
|
+
},
|
|
72
|
+
"author_avatar": {
|
|
73
|
+
"type": "text"
|
|
74
|
+
},
|
|
75
|
+
"created_at": {
|
|
76
|
+
"type": "timestamp"
|
|
77
|
+
},
|
|
78
|
+
"published_at": {
|
|
79
|
+
"type": "timestamp"
|
|
80
|
+
},
|
|
81
|
+
"updated_at": {
|
|
82
|
+
"type": "timestamp"
|
|
83
|
+
},
|
|
84
|
+
"tags": {
|
|
85
|
+
"type": [
|
|
86
|
+
"string"
|
|
87
|
+
]
|
|
88
|
+
},
|
|
89
|
+
"categories": {
|
|
90
|
+
"type": [
|
|
91
|
+
"number"
|
|
92
|
+
],
|
|
93
|
+
"ref": "blog_categories"
|
|
94
|
+
},
|
|
95
|
+
"read_time": {
|
|
96
|
+
"type": "int",
|
|
97
|
+
"default": 0
|
|
98
|
+
},
|
|
99
|
+
"view_count": {
|
|
100
|
+
"type": "int",
|
|
101
|
+
"default": 0
|
|
102
|
+
},
|
|
103
|
+
"featured": {
|
|
104
|
+
"type": "bool",
|
|
105
|
+
"default": false
|
|
106
|
+
},
|
|
107
|
+
"published": {
|
|
108
|
+
"type": "bool",
|
|
109
|
+
"default": true
|
|
110
|
+
},
|
|
111
|
+
"meta_description": {
|
|
112
|
+
"type": "text",
|
|
113
|
+
"translatable": true
|
|
114
|
+
},
|
|
115
|
+
"meta_keywords": {
|
|
116
|
+
"type": "text",
|
|
117
|
+
"translatable": true
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
"product_categories": {
|
|
121
|
+
"id": {
|
|
122
|
+
"type": "id"
|
|
123
|
+
},
|
|
124
|
+
"name": {
|
|
125
|
+
"type": "string",
|
|
126
|
+
"nullable": false,
|
|
127
|
+
"translatable": true
|
|
128
|
+
},
|
|
129
|
+
"slug": {
|
|
130
|
+
"type": "string",
|
|
131
|
+
"nullable": false,
|
|
132
|
+
"unique": true
|
|
133
|
+
},
|
|
134
|
+
"description": {
|
|
135
|
+
"type": "text",
|
|
136
|
+
"translatable": true
|
|
137
|
+
},
|
|
138
|
+
"image": {
|
|
139
|
+
"type": "text"
|
|
140
|
+
},
|
|
141
|
+
"parent_id": {
|
|
142
|
+
"type": "int",
|
|
143
|
+
"ref": "product_categories"
|
|
144
|
+
},
|
|
145
|
+
"created_at": {
|
|
146
|
+
"type": "timestamp"
|
|
147
|
+
},
|
|
148
|
+
"updated_at": {
|
|
149
|
+
"type": "timestamp"
|
|
150
|
+
}
|
|
151
|
+
},
|
|
152
|
+
"products": {
|
|
153
|
+
"id": {
|
|
154
|
+
"type": "id"
|
|
155
|
+
},
|
|
156
|
+
"name": {
|
|
157
|
+
"type": "string",
|
|
158
|
+
"nullable": false,
|
|
159
|
+
"translatable": true
|
|
160
|
+
},
|
|
161
|
+
"slug": {
|
|
162
|
+
"type": "string",
|
|
163
|
+
"nullable": false,
|
|
164
|
+
"unique": true
|
|
165
|
+
},
|
|
166
|
+
"description": {
|
|
167
|
+
"type": "text",
|
|
168
|
+
"translatable": true
|
|
169
|
+
},
|
|
170
|
+
"price": {
|
|
171
|
+
"type": "decimal",
|
|
172
|
+
"nullable": false
|
|
173
|
+
},
|
|
174
|
+
"sale_price": {
|
|
175
|
+
"type": "decimal"
|
|
176
|
+
},
|
|
177
|
+
"on_sale": {
|
|
178
|
+
"type": "bool",
|
|
179
|
+
"default": false
|
|
180
|
+
},
|
|
181
|
+
"images": {
|
|
182
|
+
"type": [
|
|
183
|
+
"string"
|
|
184
|
+
]
|
|
185
|
+
},
|
|
186
|
+
"brand": {
|
|
187
|
+
"type": "string",
|
|
188
|
+
"translatable": true
|
|
189
|
+
},
|
|
190
|
+
"sku": {
|
|
191
|
+
"type": "string"
|
|
192
|
+
},
|
|
193
|
+
"stock": {
|
|
194
|
+
"type": "int",
|
|
195
|
+
"default": 0
|
|
196
|
+
},
|
|
197
|
+
"tags": {
|
|
198
|
+
"type": [
|
|
199
|
+
"string"
|
|
200
|
+
]
|
|
201
|
+
},
|
|
202
|
+
"categories": {
|
|
203
|
+
"type": [
|
|
204
|
+
"number"
|
|
205
|
+
],
|
|
206
|
+
"ref": "product_categories"
|
|
207
|
+
},
|
|
208
|
+
"rating": {
|
|
209
|
+
"type": "decimal",
|
|
210
|
+
"default": 0
|
|
211
|
+
},
|
|
212
|
+
"review_count": {
|
|
213
|
+
"type": "int",
|
|
214
|
+
"default": 0
|
|
215
|
+
},
|
|
216
|
+
"featured": {
|
|
217
|
+
"type": "bool",
|
|
218
|
+
"default": false
|
|
219
|
+
},
|
|
220
|
+
"is_new": {
|
|
221
|
+
"type": "bool",
|
|
222
|
+
"default": false
|
|
223
|
+
},
|
|
224
|
+
"published": {
|
|
225
|
+
"type": "bool",
|
|
226
|
+
"default": true
|
|
227
|
+
},
|
|
228
|
+
"specifications": {
|
|
229
|
+
"type": "json"
|
|
230
|
+
},
|
|
231
|
+
"variants": {
|
|
232
|
+
"type": [
|
|
233
|
+
{
|
|
234
|
+
"id": "string",
|
|
235
|
+
"name": "string",
|
|
236
|
+
"value": "string",
|
|
237
|
+
"price?": "number",
|
|
238
|
+
"image?": "string",
|
|
239
|
+
"stockQuantity": "number"
|
|
240
|
+
}
|
|
241
|
+
]
|
|
242
|
+
},
|
|
243
|
+
"created_at": {
|
|
244
|
+
"type": "timestamp"
|
|
245
|
+
},
|
|
246
|
+
"updated_at": {
|
|
247
|
+
"type": "timestamp"
|
|
248
|
+
},
|
|
249
|
+
"meta_description": {
|
|
250
|
+
"type": "text",
|
|
251
|
+
"translatable": true
|
|
252
|
+
},
|
|
253
|
+
"meta_keywords": {
|
|
254
|
+
"type": "text",
|
|
255
|
+
"translatable": true
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|