@promakeai/cli 0.4.9 → 0.4.10
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/dist/index.js +183 -176
- package/dist/registry/blog-core.json +26 -7
- 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/db.json +129 -0
- package/dist/registry/docs/blog-core.md +13 -12
- package/dist/registry/docs/blog-list-page.md +1 -1
- package/dist/registry/docs/ecommerce-core.md +13 -10
- 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 +25 -5
- 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 +1 -0
- 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 +2 -4
- package/template/README.md +58 -39
- package/template/eslint.config.js +37 -37
- package/template/package.json +3 -4
- package/template/public/data/database.db +0 -0
- package/template/scripts/init-db.ts +126 -13
- package/template/src/App.tsx +5 -8
- package/template/src/PasswordInput.tsx +61 -0
- package/template/src/components/FormField.tsx +11 -5
- package/template/src/lang/index.ts +86 -86
- package/README.md +0 -71
- package/template/public/data/database.db-shm +0 -0
- package/template/public/data/database.db-wal +0 -0
- package/template/src/db/index.ts +0 -20
- package/template/src/db/provider.tsx +0 -77
- package/template/src/db/schema.json +0 -259
- package/template/src/db/types.ts +0 -195
- package/template/src/hooks/use-debounced-value.ts +0 -12
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
import { Stack } from "./Stack";
|
|
2
2
|
import { Label } from "./ui/label";
|
|
3
3
|
|
|
4
|
+
// FormField.tsx
|
|
4
5
|
export interface FormFieldProps {
|
|
5
|
-
label:
|
|
6
|
+
label: React.ReactNode;
|
|
6
7
|
htmlFor?: string;
|
|
7
8
|
error?: string;
|
|
8
9
|
required?: boolean;
|
|
9
10
|
description?: string;
|
|
10
11
|
children: React.ReactNode;
|
|
11
|
-
gap?: 0 | 1 | 2 | 3 | 4 | 6 | 8 | 10 | 12;
|
|
12
|
+
gap?: 0 | 1 | 2 | 3 | 4 | 6 | 8 | 10 | 12;
|
|
12
13
|
className?: string;
|
|
14
|
+
labelAction?: React.ReactNode; // YENİ
|
|
13
15
|
}
|
|
14
16
|
|
|
15
17
|
export function FormField({
|
|
@@ -21,12 +23,16 @@ export function FormField({
|
|
|
21
23
|
description,
|
|
22
24
|
gap = 2,
|
|
23
25
|
className,
|
|
26
|
+
labelAction, // YENİ
|
|
24
27
|
}: FormFieldProps) {
|
|
25
28
|
return (
|
|
26
29
|
<Stack gap={gap} className={className}>
|
|
27
|
-
<
|
|
28
|
-
|
|
29
|
-
|
|
30
|
+
<div className="flex items-center justify-between">
|
|
31
|
+
<Label htmlFor={htmlFor}>
|
|
32
|
+
{label} {required && <span className="text-destructive">*</span>}
|
|
33
|
+
</Label>
|
|
34
|
+
{labelAction}
|
|
35
|
+
</div>
|
|
30
36
|
{children}
|
|
31
37
|
{description && (
|
|
32
38
|
<p className="text-sm text-muted-foreground">
|
|
@@ -1,86 +1,86 @@
|
|
|
1
|
-
import i18n from "i18next";
|
|
2
|
-
import { initReactI18next } from "react-i18next";
|
|
3
|
-
import LanguageDetector from "i18next-browser-languagedetector";
|
|
4
|
-
|
|
5
|
-
import constants from "@/constants/constants.json";
|
|
6
|
-
|
|
7
|
-
// Auto-import translations from two locations:
|
|
8
|
-
// 1. Core: @/lang/{lang}/{namespace}.json (e.g., lang/en/header.json)
|
|
9
|
-
// 2. Modules: @/modules/{namespace}/lang/{lang}.json (e.g., modules/about-page/lang/en.json)
|
|
10
|
-
const coreLangs = import.meta.glob("@/lang/*/*.json", { eager: true });
|
|
11
|
-
const moduleLangs = import.meta.glob("@/modules/*/lang/*.json", { eager: true });
|
|
12
|
-
|
|
13
|
-
// Get available languages from config
|
|
14
|
-
const availableLanguages =
|
|
15
|
-
Object.keys(constants?.site?.availableLanguages || {}) || [];
|
|
16
|
-
|
|
17
|
-
// Build resources object: { en: { header: {...}, about-page: {...} }, tr: {...} }
|
|
18
|
-
const resources: Record<string, Record<string, any>> = {};
|
|
19
|
-
|
|
20
|
-
// Process core translations: /lang/{lang}/{namespace}.json
|
|
21
|
-
Object.entries(coreLangs).forEach(([path, module]) => {
|
|
22
|
-
const match = path.match(/\/lang\/([^/]+)\/([^/]+)\.json$/);
|
|
23
|
-
if (match) {
|
|
24
|
-
const [, lang, namespace] = match;
|
|
25
|
-
if (!availableLanguages.includes(lang)) return;
|
|
26
|
-
if (!resources[lang]) resources[lang] = {};
|
|
27
|
-
resources[lang][namespace] = (module as any).default || module;
|
|
28
|
-
}
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
// Process module translations: /modules/{namespace}/lang/{lang}.json
|
|
32
|
-
Object.entries(moduleLangs).forEach(([path, module]) => {
|
|
33
|
-
const match = path.match(/\/modules\/([^/]+)\/lang\/([^/]+)\.json$/);
|
|
34
|
-
if (match) {
|
|
35
|
-
const [, namespace, lang] = match;
|
|
36
|
-
if (!availableLanguages.includes(lang)) return;
|
|
37
|
-
if (!resources[lang]) resources[lang] = {};
|
|
38
|
-
resources[lang][namespace] = (module as any).default || module;
|
|
39
|
-
}
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
// Custom detector for cached settings
|
|
43
|
-
const settingsDetector = {
|
|
44
|
-
name: "settingsDetector",
|
|
45
|
-
lookup() {
|
|
46
|
-
if (constants.site.overrideBrowserLanguage) {
|
|
47
|
-
return constants?.site?.defaultLanguage;
|
|
48
|
-
}
|
|
49
|
-
return undefined;
|
|
50
|
-
},
|
|
51
|
-
cacheUserLanguage() {
|
|
52
|
-
// Don't cache - settings detector is read-only
|
|
53
|
-
},
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
// Create detector instance and add custom detector
|
|
57
|
-
const languageDetector = new LanguageDetector();
|
|
58
|
-
languageDetector.addDetector(settingsDetector);
|
|
59
|
-
|
|
60
|
-
i18n
|
|
61
|
-
.use(languageDetector)
|
|
62
|
-
.use(initReactI18next)
|
|
63
|
-
.init({
|
|
64
|
-
resources,
|
|
65
|
-
fallbackLng: constants?.site?.defaultLanguage || "en",
|
|
66
|
-
supportedLngs: availableLanguages,
|
|
67
|
-
detection: {
|
|
68
|
-
// Priority: localStorage > settings > browser
|
|
69
|
-
order: ["localStorage", "settingsDetector", "navigator"],
|
|
70
|
-
lookupLocalStorage: "i18nextLng",
|
|
71
|
-
caches: ["localStorage"],
|
|
72
|
-
},
|
|
73
|
-
interpolation: {
|
|
74
|
-
escapeValue: false,
|
|
75
|
-
},
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
// Helper to change language and persist to localStorage
|
|
79
|
-
export const changeLanguage = (lang: string) => {
|
|
80
|
-
if (availableLanguages.includes(lang)) {
|
|
81
|
-
i18n.changeLanguage(lang);
|
|
82
|
-
}
|
|
83
|
-
};
|
|
84
|
-
|
|
85
|
-
export { availableLanguages };
|
|
86
|
-
export default i18n;
|
|
1
|
+
import i18n from "i18next";
|
|
2
|
+
import { initReactI18next } from "react-i18next";
|
|
3
|
+
import LanguageDetector from "i18next-browser-languagedetector";
|
|
4
|
+
|
|
5
|
+
import constants from "@/constants/constants.json";
|
|
6
|
+
|
|
7
|
+
// Auto-import translations from two locations:
|
|
8
|
+
// 1. Core: @/lang/{lang}/{namespace}.json (e.g., lang/en/header.json)
|
|
9
|
+
// 2. Modules: @/modules/{namespace}/lang/{lang}.json (e.g., modules/about-page/lang/en.json)
|
|
10
|
+
const coreLangs = import.meta.glob("@/lang/*/*.json", { eager: true });
|
|
11
|
+
const moduleLangs = import.meta.glob("@/modules/*/lang/*.json", { eager: true });
|
|
12
|
+
|
|
13
|
+
// Get available languages from config
|
|
14
|
+
const availableLanguages =
|
|
15
|
+
Object.keys(constants?.site?.availableLanguages || {}) || [];
|
|
16
|
+
|
|
17
|
+
// Build resources object: { en: { header: {...}, about-page: {...} }, tr: {...} }
|
|
18
|
+
const resources: Record<string, Record<string, any>> = {};
|
|
19
|
+
|
|
20
|
+
// Process core translations: /lang/{lang}/{namespace}.json
|
|
21
|
+
Object.entries(coreLangs).forEach(([path, module]) => {
|
|
22
|
+
const match = path.match(/\/lang\/([^/]+)\/([^/]+)\.json$/);
|
|
23
|
+
if (match) {
|
|
24
|
+
const [, lang, namespace] = match;
|
|
25
|
+
if (!availableLanguages.includes(lang)) return;
|
|
26
|
+
if (!resources[lang]) resources[lang] = {};
|
|
27
|
+
resources[lang][namespace] = (module as any).default || module;
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
// Process module translations: /modules/{namespace}/lang/{lang}.json
|
|
32
|
+
Object.entries(moduleLangs).forEach(([path, module]) => {
|
|
33
|
+
const match = path.match(/\/modules\/([^/]+)\/lang\/([^/]+)\.json$/);
|
|
34
|
+
if (match) {
|
|
35
|
+
const [, namespace, lang] = match;
|
|
36
|
+
if (!availableLanguages.includes(lang)) return;
|
|
37
|
+
if (!resources[lang]) resources[lang] = {};
|
|
38
|
+
resources[lang][namespace] = (module as any).default || module;
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
// Custom detector for cached settings
|
|
43
|
+
const settingsDetector = {
|
|
44
|
+
name: "settingsDetector",
|
|
45
|
+
lookup() {
|
|
46
|
+
if (constants.site.overrideBrowserLanguage) {
|
|
47
|
+
return constants?.site?.defaultLanguage;
|
|
48
|
+
}
|
|
49
|
+
return undefined;
|
|
50
|
+
},
|
|
51
|
+
cacheUserLanguage() {
|
|
52
|
+
// Don't cache - settings detector is read-only
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
// Create detector instance and add custom detector
|
|
57
|
+
const languageDetector = new LanguageDetector();
|
|
58
|
+
languageDetector.addDetector(settingsDetector);
|
|
59
|
+
|
|
60
|
+
i18n
|
|
61
|
+
.use(languageDetector)
|
|
62
|
+
.use(initReactI18next)
|
|
63
|
+
.init({
|
|
64
|
+
resources,
|
|
65
|
+
fallbackLng: constants?.site?.defaultLanguage || "en",
|
|
66
|
+
supportedLngs: availableLanguages,
|
|
67
|
+
detection: {
|
|
68
|
+
// Priority: localStorage > settings > browser
|
|
69
|
+
order: ["localStorage", "settingsDetector", "navigator"],
|
|
70
|
+
lookupLocalStorage: "i18nextLng",
|
|
71
|
+
caches: ["localStorage"],
|
|
72
|
+
},
|
|
73
|
+
interpolation: {
|
|
74
|
+
escapeValue: false,
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
// Helper to change language and persist to localStorage
|
|
79
|
+
export const changeLanguage = (lang: string) => {
|
|
80
|
+
if (availableLanguages.includes(lang)) {
|
|
81
|
+
i18n.changeLanguage(lang);
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
export { availableLanguages };
|
|
86
|
+
export default i18n;
|
package/README.md
DELETED
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
# @promakeai/cli
|
|
2
|
-
|
|
3
|
-
Modular React template CLI (like shadcn/ui). Create React projects and add feature modules incrementally. Copies code directly into projects rather than installing as dependencies.
|
|
4
|
-
|
|
5
|
-
## Quick Start
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
# Install globally
|
|
9
|
-
npm install -g @promakeai/cli
|
|
10
|
-
|
|
11
|
-
# Create a new project
|
|
12
|
-
promake create my-app --preset ecommerce --pm bun
|
|
13
|
-
|
|
14
|
-
# Add modules to existing project
|
|
15
|
-
promake add hero product-card
|
|
16
|
-
|
|
17
|
-
# Theme customization
|
|
18
|
-
promake theme --preset blue --radius medium
|
|
19
|
-
|
|
20
|
-
# Health check
|
|
21
|
-
promake doctor
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
## Commands
|
|
25
|
-
|
|
26
|
-
| Command | Description |
|
|
27
|
-
|---------|-------------|
|
|
28
|
-
| `promake create <name>` | Scaffold a new React project |
|
|
29
|
-
| `promake add <items...>` | Add modules, components, or pages |
|
|
30
|
-
| `promake remove <modules...>` | Remove installed modules |
|
|
31
|
-
| `promake sync` | Install missing modules from promake.json |
|
|
32
|
-
| `promake theme` | Configure theme (colors, radius, fonts) |
|
|
33
|
-
| `promake list` | Browse available modules |
|
|
34
|
-
| `promake doctor` | Analyze project for issues |
|
|
35
|
-
| `promake init` | Initialize promake.json |
|
|
36
|
-
| `promake zip / unzip` | Archive utilities |
|
|
37
|
-
|
|
38
|
-
## Documentation
|
|
39
|
-
|
|
40
|
-
- **[Docs Index](./docs/index.md)** - Full documentation hub
|
|
41
|
-
- **[Workflow Guide](./docs/workflow.md)** - Branching, versioning & publish workflow
|
|
42
|
-
- **[AI Agent Guide](./docs/ai-agent-guide.md)** - Integrating modules with DB + i18n
|
|
43
|
-
|
|
44
|
-
## Data Layer
|
|
45
|
-
|
|
46
|
-
Projects generated by Promake include `@promakeai/dbreact` with a JSON schema
|
|
47
|
-
(`src/db/schema.json`) and a prebuilt SQLite database (`public/data/database.db`).
|
|
48
|
-
Use `@/db` hooks (`useDbList`, `useDbGet`, etc.) for data access.
|
|
49
|
-
|
|
50
|
-
## Development
|
|
51
|
-
|
|
52
|
-
```bash
|
|
53
|
-
# Install dependencies
|
|
54
|
-
bun install
|
|
55
|
-
|
|
56
|
-
# Run from source
|
|
57
|
-
bun run dev
|
|
58
|
-
|
|
59
|
-
# Build
|
|
60
|
-
bun run build
|
|
61
|
-
|
|
62
|
-
# Test
|
|
63
|
-
bun test
|
|
64
|
-
|
|
65
|
-
# Type check
|
|
66
|
-
bun run typecheck
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
## License
|
|
70
|
-
|
|
71
|
-
MIT
|
|
Binary file
|
|
File without changes
|
package/template/src/db/index.ts
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
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";
|
|
@@ -1,77 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,259 +0,0 @@
|
|
|
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
|
-
}
|