inicontent 1.0.0

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.
Files changed (205) hide show
  1. package/.github/pull_request.yml +18 -0
  2. package/CONTRIBUTING.md +67 -0
  3. package/LICENSE +21 -0
  4. package/README.md +142 -0
  5. package/app/app.vue +106 -0
  6. package/app/assets/main.css +974 -0
  7. package/app/components/Api/EndpointCard.vue +169 -0
  8. package/app/components/Api/SchemaDocsTable.vue +208 -0
  9. package/app/components/Asset/Card.vue +1363 -0
  10. package/app/components/Asset/Grid.vue +295 -0
  11. package/app/components/Asset/Icon.vue +38 -0
  12. package/app/components/Asset/Preview.vue +486 -0
  13. package/app/components/Asset/Scanner.vue +700 -0
  14. package/app/components/Asset/Thumb.vue +708 -0
  15. package/app/components/Auth/Reset.vue +99 -0
  16. package/app/components/Auth/index.vue +346 -0
  17. package/app/components/ChatInterface.vue +2326 -0
  18. package/app/components/ChatPagePreview.vue +34 -0
  19. package/app/components/Column/Array.vue +29 -0
  20. package/app/components/Column/Asset.vue +21 -0
  21. package/app/components/Column/Boolean.vue +12 -0
  22. package/app/components/Column/Color.vue +9 -0
  23. package/app/components/Column/Date.vue +57 -0
  24. package/app/components/Column/Edit.vue +215 -0
  25. package/app/components/Column/Email.vue +9 -0
  26. package/app/components/Column/Html.vue +27 -0
  27. package/app/components/Column/Id.vue +20 -0
  28. package/app/components/Column/Locale.vue +32 -0
  29. package/app/components/Column/Object.vue +18 -0
  30. package/app/components/Column/Password.vue +7 -0
  31. package/app/components/Column/Role.vue +15 -0
  32. package/app/components/Column/S.vue +14 -0
  33. package/app/components/Column/Table/Single.vue +49 -0
  34. package/app/components/Column/Table/index.vue +42 -0
  35. package/app/components/Column/Tags.vue +31 -0
  36. package/app/components/Column/Text.vue +9 -0
  37. package/app/components/Column/Url.vue +9 -0
  38. package/app/components/Column/index.vue +41 -0
  39. package/app/components/Dashboard/Editor.vue +221 -0
  40. package/app/components/Dashboard/Grid.vue +270 -0
  41. package/app/components/Dashboard/View.vue +131 -0
  42. package/app/components/Dashboard/Widget/BarChart.vue +73 -0
  43. package/app/components/Dashboard/Widget/Counter.vue +51 -0
  44. package/app/components/Dashboard/Widget/LineChart.vue +76 -0
  45. package/app/components/Dashboard/Widget/PieChart.vue +75 -0
  46. package/app/components/Dashboard/Widget/Table.vue +94 -0
  47. package/app/components/Dashboard/WidgetEditor.vue +274 -0
  48. package/app/components/Data/Array.vue +102 -0
  49. package/app/components/Data/Html.vue +9 -0
  50. package/app/components/Data/Icon.vue +9 -0
  51. package/app/components/Data/Object.vue +10 -0
  52. package/app/components/Data/S.vue +16 -0
  53. package/app/components/Data/Table.vue +53 -0
  54. package/app/components/Data/Value.vue +34 -0
  55. package/app/components/Data/index.vue +14 -0
  56. package/app/components/Field/Array.vue +324 -0
  57. package/app/components/Field/Asset/Actions.vue +97 -0
  58. package/app/components/Field/Asset/index.vue +639 -0
  59. package/app/components/Field/Boolean.vue +26 -0
  60. package/app/components/Field/Checkbox.vue +48 -0
  61. package/app/components/Field/Color.vue +27 -0
  62. package/app/components/Field/Date.vue +30 -0
  63. package/app/components/Field/Email.vue +51 -0
  64. package/app/components/Field/EmailTemplate.vue +420 -0
  65. package/app/components/Field/Html/ColorPicker.vue +127 -0
  66. package/app/components/Field/Html/Editor.vue +544 -0
  67. package/app/components/Field/Html/index.vue +25 -0
  68. package/app/components/Field/Icon.vue +114 -0
  69. package/app/components/Field/JSON.vue +113 -0
  70. package/app/components/Field/Mention.vue +36 -0
  71. package/app/components/Field/Number.vue +27 -0
  72. package/app/components/Field/Object.vue +92 -0
  73. package/app/components/Field/Password.vue +55 -0
  74. package/app/components/Field/Radio.vue +47 -0
  75. package/app/components/Field/S.vue +44 -0
  76. package/app/components/Field/Select.vue +49 -0
  77. package/app/components/Field/Table.vue +348 -0
  78. package/app/components/Field/Tags.vue +113 -0
  79. package/app/components/Field/Text.vue +31 -0
  80. package/app/components/Field/Textarea.vue +33 -0
  81. package/app/components/Field/Url.vue +40 -0
  82. package/app/components/Field/Wrapper.vue +128 -0
  83. package/app/components/Field/index.vue +68 -0
  84. package/app/components/FloatingChatbot.vue +73 -0
  85. package/app/components/Form/Card.vue +151 -0
  86. package/app/components/Form/Drawer.vue +195 -0
  87. package/app/components/Form/index.vue +621 -0
  88. package/app/components/Header.vue +328 -0
  89. package/app/components/Offline/ConflictModal.vue +200 -0
  90. package/app/components/Offline/SyncStatus.vue +249 -0
  91. package/app/components/PlatformFeatureLanding.vue +65 -0
  92. package/app/components/Table/Backups.vue +638 -0
  93. package/app/components/Table/Flows.vue +814 -0
  94. package/app/components/Table/Grid.vue +296 -0
  95. package/app/components/Table/Icon.vue +36 -0
  96. package/app/components/Table/Logs.vue +181 -0
  97. package/app/components/Table/Schedules.vue +722 -0
  98. package/app/components/Table/Search/Button.vue +256 -0
  99. package/app/components/Table/Search/Items.vue +286 -0
  100. package/app/components/Table/Search/index.vue +87 -0
  101. package/app/components/Table/Settings/Schema.vue +801 -0
  102. package/app/components/Table/Settings/index.vue +631 -0
  103. package/app/components/Table/TranslateDrawer.vue +526 -0
  104. package/app/components/Table/Views/Kanban.vue +289 -0
  105. package/app/components/Table/Views/Table.vue +1273 -0
  106. package/app/components/Table/index.vue +890 -0
  107. package/app/composables/Translation/define.ts +41 -0
  108. package/app/composables/Translation/fetch.ts +107 -0
  109. package/app/composables/Translation/languages.ts +1 -0
  110. package/app/composables/Translation/loadCoreTranslations.ts +38 -0
  111. package/app/composables/Translation/translate.ts +250 -0
  112. package/app/composables/databaseCookies.ts +79 -0
  113. package/app/composables/fieldValidator.ts +72 -0
  114. package/app/composables/fieldsList.ts +301 -0
  115. package/app/composables/formatDatabase.ts +101 -0
  116. package/app/composables/index.ts +444 -0
  117. package/app/composables/openDrawer.ts +118 -0
  118. package/app/composables/optimizeFile.ts +287 -0
  119. package/app/composables/renderLabel.ts +88 -0
  120. package/app/composables/schemaClipboard.ts +95 -0
  121. package/app/composables/search.ts +353 -0
  122. package/app/composables/setThemeConfig.ts +48 -0
  123. package/app/composables/translationValue.ts +169 -0
  124. package/app/composables/useAssetPreview.ts +84 -0
  125. package/app/composables/useAssetUploader.ts +56 -0
  126. package/app/composables/useDashboardData.ts +310 -0
  127. package/app/composables/useDocumentThumbnail.ts +351 -0
  128. package/app/composables/useEcharts.ts +33 -0
  129. package/app/composables/useLocalDatabaseConfig.ts +51 -0
  130. package/app/composables/useOcr.ts +300 -0
  131. package/app/composables/useOfflineFetch.ts +194 -0
  132. package/app/composables/useOfflineItem.ts +107 -0
  133. package/app/composables/useOfflineQueue.ts +302 -0
  134. package/app/composables/useOfflineSync.ts +235 -0
  135. package/app/composables/usePasskeyAuth.ts +156 -0
  136. package/app/composables/usePdfCompressor.ts +180 -0
  137. package/app/composables/usePlatformRedirect.ts +72 -0
  138. package/app/composables/useRealtimeSync.ts +233 -0
  139. package/app/composables/useReferencedItems.ts +180 -0
  140. package/app/composables/useScanner.ts +664 -0
  141. package/app/composables/useSubscription.ts +650 -0
  142. package/app/composables/useThumbnailCache.ts +136 -0
  143. package/app/composables/useVideoCompressor.ts +194 -0
  144. package/app/error.vue +112 -0
  145. package/app/layouts/api.vue +188 -0
  146. package/app/layouts/dashboard.vue +91 -0
  147. package/app/layouts/default.vue +228 -0
  148. package/app/layouts/table.vue +471 -0
  149. package/app/locales/ar.ts +996 -0
  150. package/app/locales/en.ts +655 -0
  151. package/app/locales/es.ts +996 -0
  152. package/app/locales/fr.ts +997 -0
  153. package/app/middleware/dashboard.ts +33 -0
  154. package/app/middleware/database.ts +83 -0
  155. package/app/middleware/global.ts +1 -0
  156. package/app/middleware/sid-ingest.global.ts +29 -0
  157. package/app/middleware/table.ts +54 -0
  158. package/app/middleware/user.ts +26 -0
  159. package/app/pages/[[database]]/admin/api/auth/index.vue +306 -0
  160. package/app/pages/[[database]]/admin/api/index.vue +18 -0
  161. package/app/pages/[[database]]/admin/api/tables/[table]/index.vue +497 -0
  162. package/app/pages/[[database]]/admin/api/tables/index.vue +87 -0
  163. package/app/pages/[[database]]/admin/billing/index.vue +16 -0
  164. package/app/pages/[[database]]/admin/dashboards/[id]/edit.vue +66 -0
  165. package/app/pages/[[database]]/admin/dashboards/[id]/index.vue +117 -0
  166. package/app/pages/[[database]]/admin/dashboards/index.vue +20 -0
  167. package/app/pages/[[database]]/admin/index.vue +28 -0
  168. package/app/pages/[[database]]/admin/settings.vue +526 -0
  169. package/app/pages/[[database]]/admin/tables/[table]/[id]/edit.vue +143 -0
  170. package/app/pages/[[database]]/admin/tables/[table]/[id]/index.vue +146 -0
  171. package/app/pages/[[database]]/admin/tables/[table]/flows.vue +10 -0
  172. package/app/pages/[[database]]/admin/tables/[table]/index.vue +10 -0
  173. package/app/pages/[[database]]/admin/tables/[table]/new.vue +12 -0
  174. package/app/pages/[[database]]/admin/tables/[table]/schedules.vue +10 -0
  175. package/app/pages/[[database]]/admin/tables/[table]/settings.vue +30 -0
  176. package/app/pages/[[database]]/admin/tables/assets/[...path].vue +24 -0
  177. package/app/pages/[[database]]/admin/tables/assets/flows.vue +10 -0
  178. package/app/pages/[[database]]/admin/tables/backups/index.vue +10 -0
  179. package/app/pages/[[database]]/admin/tables/blocks/index.vue +16 -0
  180. package/app/pages/[[database]]/admin/tables/index.vue +20 -0
  181. package/app/pages/[[database]]/admin/tables/pages/index.vue +16 -0
  182. package/app/pages/[[database]]/admin/tables/templates/index.vue +16 -0
  183. package/app/pages/[[database]]/api/index.vue +120 -0
  184. package/app/pages/[[database]]/auth/index.vue +12 -0
  185. package/app/pages/[[database]]/auth/reset.vue +10 -0
  186. package/app/pages/[[database]]/index.vue +10 -0
  187. package/app/plugins/offline-warmup.client.ts +53 -0
  188. package/app/plugins/template-globals.ts +9 -0
  189. package/app/spa-loading-template.html +93 -0
  190. package/biome.json +53 -0
  191. package/index.d.ts +380 -0
  192. package/modules/naiveui.ts +41 -0
  193. package/nuxt.config.ts +269 -0
  194. package/package.json +56 -0
  195. package/pnpm-workspace.yaml +5 -0
  196. package/public/apple-touch-icon.png +0 -0
  197. package/public/favicon-16x16.png +0 -0
  198. package/public/favicon-32x32.png +0 -0
  199. package/public/favicon.ico +0 -0
  200. package/public/pwa-192x192.png +0 -0
  201. package/public/pwa-512x512.png +0 -0
  202. package/public/pwa-maskable-192x192.png +0 -0
  203. package/public/pwa-maskable-512x512.png +0 -0
  204. package/public/vendor/opencv.js +48 -0
  205. package/tsconfig.json +9 -0
@@ -0,0 +1,497 @@
1
+ <template>
2
+ <main v-if="currentTable" class="api-table-doc-page">
3
+ <section class="api-table-doc-page__hero">
4
+ <NFlex wrap justify="space-between" align="center" :size="24">
5
+ <NFlex align="center" :size="16">
6
+ <NIconWrapper :size="64" :border-radius="50">
7
+ <LazyTableIcon size="34" :table="currentTable" />
8
+ </NIconWrapper>
9
+ <div>
10
+ <NH2 style="margin: 0">{{ t(currentTable.slug) }}</NH2>
11
+ <NText depth="3">{{ t('table') }} &middot; {{ currentTable.slug }}</NText>
12
+ </div>
13
+ </NFlex>
14
+ <div class="api-table-doc-page__methods">
15
+ <NText strong>{{ t('apiDocs.availableMethods') }}</NText>
16
+ <NFlex wrap :size="8">
17
+ <NTag v-for="method in availableMethods" :key="method.key" :type="method.type" size="large" round>
18
+ {{ method.label.toUpperCase() }}
19
+ </NTag>
20
+ </NFlex>
21
+ </div>
22
+ </NFlex>
23
+ </section>
24
+
25
+ <section class="api-table-doc-page__session">
26
+ <NCard :bordered="false">
27
+ <template #header>
28
+ <NFlex align="center" :size="8">
29
+ <Icon name="tabler:shield-lock" size="18" />
30
+ <span>{{ t('apiDocs.sessionNoticeTitle') }}</span>
31
+ </NFlex>
32
+ </template>
33
+ <NFlex vertical :size="12">
34
+ <NP>
35
+ {{ t('apiDocs.sessionNoticeDescription', { param: sessionParamName }) }}
36
+ </NP>
37
+ <NFlex wrap :size="12">
38
+ <NTag type="info" size="large">{{ sessionParamName }}</NTag>
39
+ <NText>{{ t('apiDocs.sessionAsQuery') }}</NText>
40
+ <NCode :code="`?${sessionParamName}=SESSION_ID`" language="bash" />
41
+ </NFlex>
42
+ <NP>{{ t('apiDocs.sessionAsCookie') }}</NP>
43
+ <NAlert type="success" :show-icon="false">
44
+ <span>
45
+ {{ t('apiDocs.sessionAuthLinkPrefix') }}
46
+ <NuxtLink :to="authDocsUrl" class="api-table-doc-page__link">
47
+ {{ t('apiDocs.viewAuthDocs') }}
48
+ </NuxtLink>
49
+ .
50
+ </span>
51
+ </NAlert>
52
+ </NFlex>
53
+ </NCard>
54
+ </section>
55
+
56
+ <section class="api-table-doc-page__endpoints">
57
+ <ApiEndpointCard
58
+ v-if="hasMethod('r')"
59
+ :endpoint="listEndpoint"
60
+ method="GET"
61
+ :description="t('apiDocs.tableEndpoints.listDescription', { table: currentTable.slug })"
62
+ :response-example="listResponse"
63
+ :params="listParams"
64
+ />
65
+
66
+ <ApiEndpointCard
67
+ v-if="hasMethod('r')"
68
+ :endpoint="singleEndpoint"
69
+ method="GET"
70
+ :description="t('apiDocs.tableEndpoints.singleDescription', { table: currentTable.slug })"
71
+ :response-example="singleResponse"
72
+ />
73
+
74
+ <ApiEndpointCard
75
+ v-if="hasMethod('c')"
76
+ :endpoint="createEndpoint"
77
+ method="POST"
78
+ :description="t('apiDocs.tableEndpoints.createDescription', { table: currentTable.slug })"
79
+ :request-example="createRequest"
80
+ :response-example="createResponse"
81
+ />
82
+
83
+ <ApiEndpointCard
84
+ v-if="hasMethod('u')"
85
+ :endpoint="updateEndpoint"
86
+ method="PUT"
87
+ :description="t('apiDocs.tableEndpoints.updateDescription', { table: currentTable.slug })"
88
+ :request-example="updateRequest"
89
+ :response-example="updateResponse"
90
+ />
91
+
92
+ <ApiEndpointCard
93
+ v-if="hasMethod('d')"
94
+ :endpoint="deleteEndpoint"
95
+ method="DELETE"
96
+ :description="t('apiDocs.tableEndpoints.deleteDescription', { table: currentTable.slug })"
97
+ :response-example="deleteResponse"
98
+ />
99
+ </section>
100
+
101
+ <section class="api-table-doc-page__schema">
102
+ <NCard :bordered="false">
103
+ <template #header>
104
+ <NFlex align="center" :size="8">
105
+ <Icon name="tabler:list-details" size="18" />
106
+ <NH3 style="margin: 0">{{ t('apiDocs.schemaTitle') }}</NH3>
107
+ </NFlex>
108
+ </template>
109
+ <ApiSchemaDocsTable :schema="currentTable.schema" size="large" />
110
+ </NCard>
111
+ </section>
112
+
113
+ <section v-if="showAssetsUploadDoc" class="api-table-doc-page__assets">
114
+ <NCard :bordered="false">
115
+ <template #header>
116
+ <NFlex align="center" :size="8">
117
+ <Icon name="tabler:upload" size="18" />
118
+ <NH3 style="margin: 0">{{ t('apiDocs.assetsUploadTitle') }}</NH3>
119
+ </NFlex>
120
+ </template>
121
+ <div class="api-table-doc-page__assets-doc">
122
+ <NP>{{ t('apiDocs.assetsUploadIntro') }}</NP>
123
+ <NCode :code="assetsUploadSnippet" language="json" word-wrap />
124
+ <NList>
125
+ <NListItem>{{ t('apiDocs.assetsUploadStep1') }}</NListItem>
126
+ <NListItem>{{ t('apiDocs.assetsUploadStep2') }}</NListItem>
127
+ <NListItem>{{ t('apiDocs.assetsUploadStep3') }}</NListItem>
128
+ </NList>
129
+ <NAlert type="warning" :show-icon="false">
130
+ {{ t('apiDocs.assetsUploadCustomEndpoint') }}
131
+ </NAlert>
132
+ </div>
133
+ </NCard>
134
+ </section>
135
+ </main>
136
+
137
+ <NResult v-else status="404" :title="t('apiDocs.tableNotFoundTitle')" :description="t('apiDocs.tableNotFoundDescription')">
138
+ <template #footer>
139
+ <NuxtLink :to="tablesListUrl" class="api-table-doc-page__link">
140
+ {{ t('apiDocs.backToTables') }}
141
+ </NuxtLink>
142
+ </template>
143
+ </NResult>
144
+ </template>
145
+
146
+ <script setup lang="ts">
147
+ import { Icon, LazyTableIcon, NuxtLink } from "#components";
148
+
149
+ definePageMeta({
150
+ layout: "dashboard",
151
+ middleware: ["database", "user", "dashboard", "global"],
152
+ });
153
+
154
+ const route = useRoute();
155
+ const database = useState<Database>("database");
156
+ const config = useRuntimeConfig();
157
+
158
+ const basePath = computed(() =>
159
+ route.params.database ? `/${route.params.database}` : "",
160
+ );
161
+ const tablesListUrl = computed(() => `${basePath.value}/admin/api/tables`);
162
+
163
+ const tableSlug = computed(() => route.params.table as string);
164
+ const currentTable = computed(() =>
165
+ database.value?.tables?.find(
166
+ (tableItem) => tableItem.slug === tableSlug.value,
167
+ ),
168
+ );
169
+
170
+ const methodMeta = {
171
+ c: { key: "post", type: "success" as const, label: t("post") },
172
+ r: { key: "get", type: "info" as const, label: t("get") },
173
+ u: { key: "put", type: "warning" as const, label: t("put") },
174
+ d: { key: "delete", type: "error" as const, label: t("delete") },
175
+ } as const;
176
+
177
+ type MethodOption = (typeof methodMeta)[keyof typeof methodMeta];
178
+
179
+ const availableMethods = computed<MethodOption[]>(() => {
180
+ if (!currentTable.value?.allowedMethods) return [];
181
+ return (
182
+ currentTable.value.allowedMethods.split("") as (keyof typeof methodMeta)[]
183
+ )
184
+ .map((methodKey) => methodMeta[methodKey])
185
+ .filter((method): method is MethodOption => Boolean(method));
186
+ });
187
+
188
+ function hasMethod(key: string): boolean {
189
+ return !!currentTable.value?.allowedMethods?.includes(key);
190
+ }
191
+
192
+ const sessionParamName = computed(() => {
193
+ const slug =
194
+ (route.params.database as string | undefined) ||
195
+ database.value?.slug ||
196
+ "inicontent";
197
+ return `${slug}_sid`;
198
+ });
199
+
200
+ const dbSlug = computed(
201
+ () =>
202
+ (route.params.database as string | undefined) ||
203
+ database.value?.slug ||
204
+ "inicontent",
205
+ );
206
+
207
+ const authDocsUrl = computed(() => `${basePath.value}/admin/api/auth`);
208
+
209
+ const listEndpoint = computed(
210
+ () => `/${dbSlug.value}/${currentTable.value?.slug}`,
211
+ );
212
+ const singleEndpoint = computed(
213
+ () => `/${dbSlug.value}/${currentTable.value?.slug}/{id}`,
214
+ );
215
+ const createEndpoint = computed(
216
+ () => `/${dbSlug.value}/${currentTable.value?.slug}`,
217
+ );
218
+ const updateEndpoint = computed(
219
+ () => `/${dbSlug.value}/${currentTable.value?.slug}/{id}`,
220
+ );
221
+ const deleteEndpoint = computed(
222
+ () => `/${dbSlug.value}/${currentTable.value?.slug}/{id}`,
223
+ );
224
+
225
+ const listParams = computed(() => [
226
+ {
227
+ name: "page",
228
+ type: "number",
229
+ required: false,
230
+ description: t("apiDocs.pageParam"),
231
+ },
232
+ {
233
+ name: "limit",
234
+ type: "number",
235
+ required: false,
236
+ description: t("apiDocs.limitParam"),
237
+ },
238
+ {
239
+ name: "locale",
240
+ type: "string",
241
+ required: false,
242
+ description: t("apiDocs.localeParam"),
243
+ },
244
+ {
245
+ name: "search",
246
+ type: "string",
247
+ required: false,
248
+ description: t("apiDocs.searchParam"),
249
+ },
250
+ {
251
+ name: "columns",
252
+ type: "string",
253
+ required: false,
254
+ description: t("apiDocs.columnsParam"),
255
+ },
256
+ ]);
257
+
258
+ const listResponse = computed(
259
+ () => `{
260
+ "result": [
261
+ {
262
+ "id": "abc123",
263
+ ...fields
264
+ }
265
+ ],
266
+ "message": "",
267
+ "options": {
268
+ "page": 1,
269
+ "limit": 25,
270
+ "total": 100,
271
+ "totalPages": 4
272
+ },
273
+ "code": 200
274
+ }`,
275
+ );
276
+
277
+ const singleResponse = computed(
278
+ () => `{
279
+ "result": {
280
+ "id": "abc123",
281
+ "createdBy": {
282
+ "id": "user123",
283
+ "username": "admin"
284
+ },
285
+ ...fields
286
+ },
287
+ "message": "",
288
+ "options": {},
289
+ "code": 200
290
+ }`,
291
+ );
292
+
293
+ const createRequest = computed(() => {
294
+ const schema = currentTable.value?.schema;
295
+ if (!schema?.length) return `{\n ...fields\n}`;
296
+ const lines = schema.map(
297
+ (field) => ` "${field.key}": ${getExampleValue(field)}`,
298
+ );
299
+ return `{\n${lines.join(",\n")}\n}`;
300
+ });
301
+
302
+ const createResponse = computed(
303
+ () => `{
304
+ "result": {
305
+ "id": "new_id_abc123",
306
+ "createdBy": "current_user_id",
307
+ ...fields
308
+ },
309
+ "message": "Item created successfully",
310
+ "options": {},
311
+ "code": 200
312
+ }`,
313
+ );
314
+
315
+ const updateRequest = computed(() => {
316
+ const schema = currentTable.value?.schema;
317
+ if (!schema?.length) return `{\n ...fields\n}`;
318
+ const fields = schema
319
+ .slice(0, 3)
320
+ .map((field) => ` "${field.key}": ${getExampleValue(field)}`);
321
+ return `{\n${fields.join(",\n")}\n}`;
322
+ });
323
+
324
+ const updateResponse = computed(
325
+ () => `{
326
+ "result": {
327
+ "id": "abc123",
328
+ ...updatedFields
329
+ },
330
+ "message": "Item updated successfully",
331
+ "options": {},
332
+ "code": 200
333
+ }`,
334
+ );
335
+
336
+ const deleteResponse = computed(
337
+ () => `{
338
+ "result": true,
339
+ "message": "Item deleted successfully",
340
+ "options": {},
341
+ "code": 200
342
+ }`,
343
+ );
344
+
345
+ function getExampleValue(field: Field): string {
346
+ const t = field.type as string;
347
+ if (t === "string" || t === "text" || t === "textarea" || t === "html")
348
+ return `"example"`;
349
+ if (t === "number") return "0";
350
+ if (t === "boolean") return "true";
351
+ if (t === "date") return `"2025-01-01"`;
352
+ if (t === "email") return `"user@example.com"`;
353
+ if (t === "url") return `"https://example.com"`;
354
+ if (t === "json") return "{}";
355
+ if (t === "array") return "[]";
356
+ if (t === "object") return "{}";
357
+ if (t === "select" || t === "radio") return `"option"`;
358
+ if (t === "tags") return `["tag"]`;
359
+ return `"value"`;
360
+ }
361
+
362
+ const showAssetsUploadDoc = computed(() => {
363
+ if (!currentTable.value?.schema) return false;
364
+ if (currentTable.value.slug === "assets") return true;
365
+ return currentTable.value.schema.some(
366
+ (field: Field) =>
367
+ field.type === "table" && "table" in field && field.table === "assets",
368
+ );
369
+ });
370
+
371
+ const assetsUploadSnippet = `POST /assets
372
+ [
373
+ {
374
+ "name": "logo.png",
375
+ "size": 204800,
376
+ "type": "image/png",
377
+ "extension": "png"
378
+ }
379
+ ]`;
380
+
381
+ const jsonLd = computed(() => ({
382
+ "@context": "https://schema.org",
383
+ "@type": "WebAPI",
384
+ name: `${currentTable.value?.slug ?? ""} API`,
385
+ description: `CRUD API endpoints for the ${currentTable.value?.slug ?? ""} table`,
386
+ baseUrl: `${config.public.apiBase}${dbSlug.value}/${currentTable.value?.slug ?? ""}`,
387
+ endpoints: [
388
+ ...(hasMethod("r")
389
+ ? [
390
+ {
391
+ "@type": "EntryPoint",
392
+ name: "List items",
393
+ httpMethod: "GET",
394
+ urlTemplate: `${config.public.apiBase}${dbSlug.value}/${currentTable.value?.slug}`,
395
+ },
396
+ {
397
+ "@type": "EntryPoint",
398
+ name: "Get item",
399
+ httpMethod: "GET",
400
+ urlTemplate: `${config.public.apiBase}${dbSlug.value}/${currentTable.value?.slug}/{id}`,
401
+ },
402
+ ]
403
+ : []),
404
+ ...(hasMethod("c")
405
+ ? [
406
+ {
407
+ "@type": "EntryPoint",
408
+ name: "Create item",
409
+ httpMethod: "POST",
410
+ urlTemplate: `${config.public.apiBase}${dbSlug.value}/${currentTable.value?.slug}`,
411
+ },
412
+ ]
413
+ : []),
414
+ ...(hasMethod("u")
415
+ ? [
416
+ {
417
+ "@type": "EntryPoint",
418
+ name: "Update item",
419
+ httpMethod: "PUT",
420
+ urlTemplate: `${config.public.apiBase}${dbSlug.value}/${currentTable.value?.slug}/{id}`,
421
+ },
422
+ ]
423
+ : []),
424
+ ...(hasMethod("d")
425
+ ? [
426
+ {
427
+ "@type": "EntryPoint",
428
+ name: "Delete item",
429
+ httpMethod: "DELETE",
430
+ urlTemplate: `${config.public.apiBase}${dbSlug.value}/${currentTable.value?.slug}/{id}`,
431
+ },
432
+ ]
433
+ : []),
434
+ ],
435
+ }));
436
+
437
+ useHead(() => ({
438
+ title: currentTable.value
439
+ ? `${t(currentTable.value.slug)} | ${t("apiDocumentation")}`
440
+ : `${t("apiDocs.tableNotFoundTitle")} | ${t("apiDocumentation")}`,
441
+ meta: [
442
+ {
443
+ name: "description",
444
+ content: currentTable.value
445
+ ? `API documentation for the ${currentTable.value.slug} table. Available methods: ${currentTable.value.allowedMethods?.toUpperCase() ?? ""}`
446
+ : t("apiDocs.tableNotFoundDescription"),
447
+ },
448
+ ],
449
+ script: [
450
+ ...(currentTable.value
451
+ ? [
452
+ {
453
+ type: "application/ld+json",
454
+ innerHTML: JSON.stringify(jsonLd.value),
455
+ },
456
+ ]
457
+ : []),
458
+ ],
459
+ }));
460
+ </script>
461
+
462
+ <style scoped>
463
+ .api-table-doc-page {
464
+ display: flex;
465
+ flex-direction: column;
466
+ gap: 24px;
467
+ }
468
+
469
+ .api-table-doc-page__hero {
470
+ background: var(--n-card-color-modal);
471
+ border-radius: 8px;
472
+ padding: 20px;
473
+ }
474
+
475
+ .api-table-doc-page__methods {
476
+ display: flex;
477
+ flex-direction: column;
478
+ gap: 8px;
479
+ }
480
+
481
+ .api-table-doc-page__endpoints {
482
+ display: flex;
483
+ flex-direction: column;
484
+ gap: 16px;
485
+ }
486
+
487
+ .api-table-doc-page__assets-doc {
488
+ display: flex;
489
+ flex-direction: column;
490
+ gap: 16px;
491
+ }
492
+
493
+ .api-table-doc-page__link {
494
+ color: var(--n-primary-color);
495
+ font-weight: 600;
496
+ }
497
+ </style>
@@ -0,0 +1,87 @@
1
+ <template>
2
+ <main>
3
+ <NCard style="background:none" :bordered="false">
4
+ <template #header>
5
+ <NH1 style="margin: 0">{{ t('apiDocumentation') }}</NH1>
6
+ </template>
7
+ <template #header-extra>
8
+ <NuxtLink :to="authDocsUrl">{{ t('apiDocs.viewAuthDocs') }}</NuxtLink>
9
+ </template>
10
+ <NGrid :x-gap="12" :y-gap="12" cols="1 500:2 800:3">
11
+ <NGridItem v-for="tableOption in documentedTables" :key="tableOption.slug">
12
+ <NCard hoverable>
13
+ <template #header>
14
+ <NuxtLink :to="tableOption.url">
15
+ <NFlex align="center">
16
+ <NIconWrapper :border-radius="50" style="font-style: normal">
17
+ <LazyTableIcon :table="tableOption" />
18
+ </NIconWrapper>
19
+ <NH4 style="margin: 0">{{ t(tableOption.slug) }}</NH4>
20
+ </NFlex>
21
+ </NuxtLink>
22
+ </template>
23
+ <NFlex vertical size="small">
24
+ <NFlex wrap :size="6">
25
+ <NTag v-for="method in tableOption.methods" :key="method.key" :type="method.type" round>
26
+ {{ method.label }}
27
+ </NTag>
28
+ </NFlex>
29
+ </NFlex>
30
+ </NCard>
31
+ </NGridItem>
32
+ </NGrid>
33
+ </NCard>
34
+ </main>
35
+ </template>
36
+
37
+ <script setup lang="ts">
38
+ import { LazyTableIcon, NuxtLink } from "#components";
39
+
40
+ definePageMeta({
41
+ layout: "dashboard",
42
+ middleware: ["database", "user", "dashboard", "global"],
43
+ });
44
+
45
+ const route = useRoute();
46
+ const database = useState<Database>("database");
47
+ const basePath = computed(() =>
48
+ route.params.database ? `/${route.params.database}` : "",
49
+ );
50
+ const authDocsUrl = computed(() => `${basePath.value}/admin/api/auth`);
51
+
52
+ const methodMeta = {
53
+ c: { key: "post", type: "success", label: t("post") },
54
+ r: { key: "get", type: "info", label: t("get") },
55
+ u: { key: "put", type: "warning", label: t("put") },
56
+ d: { key: "delete", type: "error", label: t("delete") },
57
+ } as const;
58
+
59
+ const documentedTables = computed(() =>
60
+ (database.value?.tables ?? [])
61
+ .map((tableItem) => {
62
+ const methods = tableItem.allowedMethods
63
+ ? tableItem.allowedMethods
64
+ .split("")
65
+ .map(
66
+ (methodKey) => methodMeta[methodKey as keyof typeof methodMeta],
67
+ )
68
+ : [];
69
+ return {
70
+ ...tableItem,
71
+ methods,
72
+ url: `${route.params.database ? `/${route.params.database}` : ""}/admin/api/tables/${tableItem.slug}`,
73
+ };
74
+ })
75
+ .filter(({ methods }) => methods.length > 0),
76
+ );
77
+
78
+ useHead({
79
+ title: `${t(database.value?.slug ?? "inicontent")} | ${t("apiDocumentation")}`,
80
+ meta: [
81
+ {
82
+ name: "description",
83
+ content: `API documentation for ${database.value?.slug ?? "inicontent"}. Browse table endpoints, authentication, and schema definitions.`,
84
+ },
85
+ ],
86
+ });
87
+ </script>
@@ -0,0 +1,16 @@
1
+ <template>
2
+ <PlatformFeatureLanding feature="billing" />
3
+ </template>
4
+
5
+ <script lang="ts" setup>
6
+ definePageMeta({
7
+ layout: "table",
8
+ middleware: ["database", "user", "dashboard", "global"],
9
+ });
10
+
11
+ const database = useState<Database>("database");
12
+
13
+ useHead({
14
+ title: `${t("billing")} | ${t(database.value.slug)} | ${t("dashboard")}`,
15
+ });
16
+ </script>
@@ -0,0 +1,66 @@
1
+ <template>
2
+ <NCard :title="t('editDashboard')" style="background:none" :bordered="false">
3
+ <NSpin :show="loading">
4
+ <DashboardEditor
5
+ v-if="dashboard"
6
+ :dashboard="dashboard"
7
+ @saved="onSaved"
8
+ />
9
+ </NSpin>
10
+ </NCard>
11
+ </template>
12
+
13
+ <script setup lang="ts">
14
+ definePageMeta({
15
+ layout: "table",
16
+ middleware: ["database", "user", "dashboard", "global"],
17
+ });
18
+
19
+ const route = useRoute();
20
+ const router = useRouter();
21
+ const config = useRuntimeConfig();
22
+ const database = useState<Database>("database");
23
+
24
+ const Language = useScopedCookie<LanguagesType>("language", database.value?.slug);
25
+ const sessionID = useScopedCookie<string>("sid", database.value?.slug);
26
+
27
+ const dashboard = ref<Dashboard | null>(null);
28
+ const currentDashboard = useState<Dashboard | null>("currentDashboard", () => null);
29
+ const loading = ref(true);
30
+
31
+ onMounted(async () => {
32
+ try {
33
+ const res = await $fetch<apiResponse<Dashboard>>(
34
+ `${config.public.apiBase}${database.value.slug}/dashboards/${route.params.id}`,
35
+ {
36
+ params: {
37
+ locale: Language.value,
38
+ [`${database.value.slug}_sid`]: sessionID.value,
39
+ },
40
+ credentials: "include",
41
+ },
42
+ );
43
+ dashboard.value = res.result;
44
+ currentDashboard.value = res.result;
45
+ if (dashboard.value) {
46
+ if (typeof dashboard.value.widgets === "string") {
47
+ try {
48
+ dashboard.value.widgets = JSON.parse(dashboard.value.widgets);
49
+ } catch {}
50
+ }
51
+ }
52
+ } catch {
53
+ window.$message.error(t("error"));
54
+ }
55
+ loading.value = false;
56
+ });
57
+
58
+ function onSaved(saved: Dashboard) {
59
+ const base = route.params.database ? `/${route.params.database}` : "";
60
+ router.push(`${base}/admin/dashboards/${saved.id}`);
61
+ }
62
+
63
+ useHead({
64
+ title: computed(() => `${t("editDashboard")} | ${dashboard.value?.name ?? ""}`),
65
+ });
66
+ </script>