@oktis-works/admin 0.8.8 → 0.10.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.
- package/dist/client/_astro/ActivityWidget.ChQWOeJC.js +16 -0
- package/dist/client/_astro/{AdminBar.5-l0PBZz.js → AdminBar.Dt8_nO1o.js} +2 -2
- package/dist/client/_astro/{AtGlanceWidget.B5ytZ8mp.js → AtGlanceWidget.NFubs9y9.js} +3 -3
- package/dist/client/_astro/BaseLayout.DAkzjMTq.css +1 -0
- package/dist/client/_astro/ContentEditor.CV3dhbjt.js +1 -0
- package/dist/client/_astro/{ContentEditor.CQ2OYPHn.js → ContentEditor.DfL_lpMd.js} +2 -2
- package/dist/client/_astro/{ContentTypeWorkspace.CdsUQQfa.js → ContentTypeWorkspace.BitHXXxQ.js} +1 -1
- package/dist/client/_astro/{DashboardHome.BYOWl94l.js → DashboardHome.Bc3UoI13.js} +1 -1
- package/dist/client/_astro/DashboardLayout.DM3iZCrw.css +1 -0
- package/dist/client/_astro/DashboardLayout.astro_astro_type_script_index_0_lang.CzpgSZHu.js +1 -0
- package/dist/client/_astro/FbIcon.CUNS6rpP.js +1 -0
- package/dist/client/_astro/FieldGroupEditor.DZI2aKIV.js +69 -0
- package/dist/client/_astro/FieldGroupList.Jz2ScLbc.js +1 -0
- package/dist/client/_astro/FloatingPanel.BeCEt_JP.js +1 -0
- package/dist/client/_astro/{Footer.CnuW-fOP.js → Footer.CoQHbtro.js} +2 -2
- package/dist/client/_astro/GeneralSettings.CE_IKxIn.js +1 -0
- package/dist/client/_astro/LogsManager.BQUduS0g.js +1 -0
- package/dist/client/_astro/MediaLibrary.Bt-NcHbG.js +1 -0
- package/dist/client/_astro/{MenuManager.Bl_gpt5T.js → MenuManager.D4NmoaFw.js} +1 -1
- package/dist/client/_astro/{PluginManager.ClpQA_a-.js → PluginManager.BlNWs5RU.js} +2 -2
- package/dist/client/_astro/PostTypesManager.BKcoZ1gF.js +1 -0
- package/dist/client/_astro/{QuickDraftWidget.Gjzwimmf.js → QuickDraftWidget.CRQ7FmTm.js} +2 -2
- package/dist/client/_astro/ResourceHubs.Dz0u_gL5.js +1 -0
- package/dist/client/_astro/TaxonomiesManager.BZZ626v_.js +1 -0
- package/dist/client/_astro/TaxonomyTermsManager.CxXNNf5j.js +1 -0
- package/dist/client/_astro/ThemeManager.DzbQaPu4.js +176 -0
- package/dist/client/_astro/UserCreateForm.DL04jrgd.js +1 -0
- package/dist/client/_astro/{UsersManager.GrlsCN-n.js → UsersManager.DGQJqlbN.js} +1 -1
- package/dist/client/_astro/WebhooksManager.DACLOiX1.js +1 -0
- package/dist/client/_astro/api.CNp-a9Z3.js +1 -0
- package/dist/client/_astro/{ar.9BcNkJMI.js → ar.BeEmOTlL.js} +1 -1
- package/dist/client/_astro/client.DU9Dc2oe.js +1 -0
- package/dist/client/_astro/{es-ES.DgqDFCmG.js → es-ES.C0DI0CK6.js} +1 -1
- package/dist/client/_astro/extensions.BWDpAUyI.js +1 -0
- package/dist/client/_astro/{he.11dIJHQw.js → he.RF4swhtn.js} +1 -1
- package/dist/client/_astro/i18n.DCpVFQAZ.js +1 -0
- package/dist/client/_astro/{it.yCdgk3Xj.js → it.DhCR9YsW.js} +1 -1
- package/dist/client/_astro/{ja.BIm7Hwe_.js → ja.DPrxHQvU.js} +1 -1
- package/dist/client/_astro/{login.astro_astro_type_script_index_0_lang.D7pjn1zG.js → login.astro_astro_type_script_index_0_lang.Dni6a5dD.js} +1 -1
- package/dist/client/_astro/{pl.dUwrcITB.js → pl.6VCoPa7y.js} +1 -1
- package/dist/client/_astro/pt-BR.BpKbXMEZ.js +1 -0
- package/dist/client/_astro/{ru.lmDiQaSu.js → ru.Cv0Z6X2y.js} +1 -1
- package/dist/client/_astro/solid.D49dPDCT.js +1 -0
- package/dist/client/_astro/{src.BJaHBCQK.js → src.DXVWCoHp.js} +1 -1
- package/dist/client/_astro/{sw._qHdoFbp.js → sw.BBMIgY-Y.js} +1 -1
- package/dist/client/_astro/{tr.Do-lr98T.js → tr.BWEmWhCE.js} +1 -1
- package/dist/client/_astro/{uk.DTUX-N1c.js → uk.DDXJXOmn.js} +1 -1
- package/dist/client/_astro/web.Bqbl_1YR.js +1 -0
- package/dist/client/_astro/{yo.BIYG0J7I.js → yo.DNoEBLJd.js} +1 -1
- package/dist/client/_astro/{zh-CN.C7Iod1E1.js → zh-CN.CVKBB6Ky.js} +1 -1
- package/dist/server/chunks/404_CfM6kYWd.mjs +56 -0
- package/dist/server/chunks/{BaseLayout_DAvxa2_1.mjs → BaseLayout_DTOkhEmH.mjs} +1 -1
- package/dist/server/chunks/{ContentEditor_GY92E-dL.mjs → ContentEditor_D3N5qDZK.mjs} +2 -2
- package/dist/server/chunks/{DashboardLayout_U7BSt4BY.mjs → DashboardLayout_CidaIIqh.mjs} +12 -8
- package/dist/server/chunks/FloatingPanel_DI2CdUZK.mjs +57 -0
- package/dist/server/chunks/{MediaLibrary_B-7kFx0j.mjs → MediaLibrary_DZqdhYpt.mjs} +2 -2
- package/dist/server/chunks/{_id__CrGKzk8c.mjs → _id__9FIehtyR.mjs} +21 -10
- package/dist/server/chunks/{_slug__BFkT7tf-.mjs → _slug__Izz0oLah.mjs} +1 -1
- package/dist/server/chunks/access_BSltT-j0.mjs +417 -0
- package/dist/server/chunks/{ar_DlCzOUGe.mjs → ar_DObFFdrO.mjs} +10 -1
- package/dist/server/chunks/{custom-fields_C21soZ6X.mjs → custom-fields_ChGoFU6v.mjs} +2 -2
- package/dist/server/chunks/{edit_BX_SHtDf.mjs → edit_BDJsuJht.mjs} +2 -2
- package/dist/server/chunks/{es-ES_DD40B9Zr.mjs → es-ES_D6jd6T7H.mjs} +10 -1
- package/dist/server/chunks/{general_4pi2Xyk_.mjs → general_J5JruaEJ.mjs} +31 -27
- package/dist/server/chunks/{he_8WSAyM_S.mjs → he_DwGJwonL.mjs} +10 -1
- package/dist/server/chunks/{i18n_k88p-B6j.mjs → i18n_Cr9pc66i.mjs} +229 -527
- package/dist/server/chunks/{index_J14tJb39.mjs → index_BZk89gnA.mjs} +2 -2
- package/dist/server/chunks/{index_BrpYs70y.mjs → index_DEkpVnI-.mjs} +2 -2
- package/dist/server/chunks/{index_BwnKp1u1.mjs → index__TzLfz1Y.mjs} +2 -2
- package/dist/server/chunks/{index_DlZPZgHm.mjs → index_goZUoZn8.mjs} +3 -3
- package/dist/server/chunks/{index_DfzW3lzT.mjs → index_ph0CQvPI.mjs} +1 -1
- package/dist/server/chunks/{it_DEDC6yxf.mjs → it_CMLpQyoj.mjs} +10 -1
- package/dist/server/chunks/{ja_BRQi6rW3.mjs → ja_CYRz0pHv.mjs} +10 -1
- package/dist/server/chunks/{login_CnlPFzuR.mjs → login_D1vntlDR.mjs} +1 -1
- package/dist/server/chunks/{logs_ClikIoCW.mjs → logs_BQ-8tURM.mjs} +2 -2
- package/dist/server/chunks/{menus_UezEKuJz.mjs → menus_CxwSYjnB.mjs} +2 -2
- package/dist/server/chunks/{new_Cgxb7rz2.mjs → new_CZGMo6CG.mjs} +2 -2
- package/dist/server/chunks/{new_BNlbLJSd.mjs → new_DoFj-hVB.mjs} +2 -2
- package/dist/server/chunks/{pl_CKanAk0F.mjs → pl_BuXVPvU1.mjs} +10 -1
- package/dist/server/chunks/{plugins_Cdlj5IY8.mjs → plugins_CM-vRDfx.mjs} +2 -2
- package/dist/server/chunks/post-types_9YdRCAhN.mjs +437 -0
- package/dist/server/chunks/{pt-BR_X0hfCrsD.mjs → pt-BR_CLa-GGIZ.mjs} +48 -5
- package/dist/server/chunks/{ru_CuNXt2MB.mjs → ru_C56ltukG.mjs} +10 -1
- package/dist/server/chunks/{sw_RLMWPkos.mjs → sw_Dhw0GAgD.mjs} +10 -1
- package/dist/server/chunks/{taxonomies_DprUiQYq.mjs → taxonomies_C2yLbsVU.mjs} +2 -2
- package/dist/server/chunks/{taxonomy_ECx2rh8K.mjs → taxonomy_BAqqS2TS.mjs} +2 -2
- package/dist/server/chunks/{themes_B8BHsKmD.mjs → themes_zV_FXCDj.mjs} +2 -2
- package/dist/server/chunks/{tr_Dz8pqyTZ.mjs → tr_zELRy7A-.mjs} +10 -1
- package/dist/server/chunks/{uk_BjwqlweA.mjs → uk_DVOqp1x6.mjs} +10 -1
- package/dist/server/chunks/{upload_FUWLBTTZ.mjs → upload_Bba7badi.mjs} +2 -2
- package/dist/server/chunks/{webhooks_BH1Qrnee.mjs → webhooks_LTF3h9-l.mjs} +2 -2
- package/dist/server/chunks/{yo_Dv14KWVR.mjs → yo_BqgZcoxs.mjs} +10 -1
- package/dist/server/chunks/{zh-CN_DjIZCWzG.mjs → zh-CN_Dz2D1XUc.mjs} +10 -1
- package/dist/server/entry.mjs +74 -47
- package/dist/server/virtual_astro_middleware.mjs +87 -17
- package/package.json +1 -1
- package/dist/client/_astro/ActivityWidget.DFfbrs-K.js +0 -16
- package/dist/client/_astro/BaseLayout.3eIEVCjB.css +0 -1
- package/dist/client/_astro/ContentEditor.DANKVwCI.js +0 -1
- package/dist/client/_astro/DashboardLayout.C1hUraeX.css +0 -1
- package/dist/client/_astro/DashboardLayout.astro_astro_type_script_index_0_lang.DP7AKyOy.js +0 -1
- package/dist/client/_astro/FbIcon.uhYN8ybK.js +0 -1
- package/dist/client/_astro/FieldGroupEditor.5iB_mZUP.js +0 -69
- package/dist/client/_astro/FieldGroupList.CN3KGhw5.js +0 -1
- package/dist/client/_astro/GeneralSettings.Dzv_7xWf.js +0 -1
- package/dist/client/_astro/LogsManager.C3XW2Gze.js +0 -1
- package/dist/client/_astro/MediaLibrary.bx1yD6aC.js +0 -1
- package/dist/client/_astro/PostTypesManager.BJNKEOXY.js +0 -1
- package/dist/client/_astro/ResourceHubs.D11v_J8D.js +0 -1
- package/dist/client/_astro/TaxonomiesManager.DAC1s3aS.js +0 -1
- package/dist/client/_astro/TaxonomyTermsManager.RsO79xRU.js +0 -1
- package/dist/client/_astro/ThemeManager.BO-12WST.js +0 -176
- package/dist/client/_astro/UserCreateForm.CSYbfU4r.js +0 -1
- package/dist/client/_astro/WebhooksManager.BNaGM-w5.js +0 -1
- package/dist/client/_astro/api.D7bs6UOp.js +0 -1
- package/dist/client/_astro/client.DWJHz38V.js +0 -1
- package/dist/client/_astro/extensions.CpcwMaUF.js +0 -1
- package/dist/client/_astro/i18n.CjQozXLx.js +0 -1
- package/dist/client/_astro/pt-BR.BnllmbjP.js +0 -1
- package/dist/client/_astro/solid.8gBSxpeo.js +0 -1
- package/dist/client/_astro/web.DwNv8rLL.js +0 -1
- package/dist/server/chunks/access_C2K6rVdx.mjs +0 -138
- package/dist/server/chunks/post-types_BzDitU7n.mjs +0 -185
|
@@ -83,20 +83,20 @@ async function loadLocale(locale) {
|
|
|
83
83
|
if (localeCache.has(locale)) return localeCache.get(locale);
|
|
84
84
|
try {
|
|
85
85
|
const module = await _rolldown_dynamic_import_helper_default(/* #__PURE__ */ Object.assign({
|
|
86
|
-
"./locales/ar.json": () => import("./
|
|
86
|
+
"./locales/ar.json": () => import("./ar_DObFFdrO.mjs"),
|
|
87
87
|
"./locales/en.json": () => Promise.resolve().then(() => en_exports),
|
|
88
|
-
"./locales/es-ES.json": () => import("./es-
|
|
89
|
-
"./locales/he.json": () => import("./
|
|
90
|
-
"./locales/it.json": () => import("./
|
|
91
|
-
"./locales/ja.json": () => import("./
|
|
92
|
-
"./locales/pl.json": () => import("./
|
|
93
|
-
"./locales/pt-BR.json": () => import("./pt-
|
|
94
|
-
"./locales/ru.json": () => import("./
|
|
95
|
-
"./locales/sw.json": () => import("./
|
|
96
|
-
"./locales/tr.json": () => import("./
|
|
97
|
-
"./locales/uk.json": () => import("./
|
|
98
|
-
"./locales/yo.json": () => import("./
|
|
99
|
-
"./locales/zh-CN.json": () => import("./zh-
|
|
88
|
+
"./locales/es-ES.json": () => import("./es-ES_D6jd6T7H.mjs"),
|
|
89
|
+
"./locales/he.json": () => import("./he_DwGJwonL.mjs"),
|
|
90
|
+
"./locales/it.json": () => import("./it_CMLpQyoj.mjs"),
|
|
91
|
+
"./locales/ja.json": () => import("./ja_CYRz0pHv.mjs"),
|
|
92
|
+
"./locales/pl.json": () => import("./pl_BuXVPvU1.mjs"),
|
|
93
|
+
"./locales/pt-BR.json": () => import("./pt-BR_CLa-GGIZ.mjs"),
|
|
94
|
+
"./locales/ru.json": () => import("./ru_C56ltukG.mjs"),
|
|
95
|
+
"./locales/sw.json": () => import("./sw_Dhw0GAgD.mjs"),
|
|
96
|
+
"./locales/tr.json": () => import("./tr_zELRy7A-.mjs"),
|
|
97
|
+
"./locales/uk.json": () => import("./uk_DVOqp1x6.mjs"),
|
|
98
|
+
"./locales/yo.json": () => import("./yo_BqgZcoxs.mjs"),
|
|
99
|
+
"./locales/zh-CN.json": () => import("./zh-CN_Dz2D1XUc.mjs")
|
|
100
100
|
}), `./locales/${locale}.json`, 3);
|
|
101
101
|
const translations = module.default ?? module;
|
|
102
102
|
localeCache.set(locale, translations);
|
|
@@ -124,6 +124,37 @@ function joinApiUrl(base, path) {
|
|
|
124
124
|
}
|
|
125
125
|
//#endregion
|
|
126
126
|
//#region src/lib/api.ts
|
|
127
|
+
var ApiError = class ApiError extends Error {
|
|
128
|
+
status;
|
|
129
|
+
code;
|
|
130
|
+
/** Segundos do header Retry-After (presente em respostas 429). */
|
|
131
|
+
retryAfter;
|
|
132
|
+
constructor(status, code, message, retryAfter) {
|
|
133
|
+
super(message ?? code);
|
|
134
|
+
this.name = "ApiError";
|
|
135
|
+
this.status = status;
|
|
136
|
+
this.code = code;
|
|
137
|
+
this.retryAfter = retryAfter;
|
|
138
|
+
}
|
|
139
|
+
static async fromResponse(response) {
|
|
140
|
+
const rawRetryAfter = response.headers?.get?.("Retry-After") ?? "";
|
|
141
|
+
const retryAfter = Number(rawRetryAfter);
|
|
142
|
+
const body = await response.json().catch(() => null);
|
|
143
|
+
return new ApiError(response.status, body?.error ?? "Request failed", body?.message ?? (body?.error ? void 0 : `Request failed: ${response.status}`), rawRetryAfter && Number.isFinite(retryAfter) ? retryAfter : void 0);
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
var RATE_LIMITED_EVENT = "okcms:rate-limited";
|
|
147
|
+
var MAX_RATE_LIMIT_WAIT_MS = 2e3;
|
|
148
|
+
var SESSION_REFRESH_MARGIN_MS = 3e5;
|
|
149
|
+
var CSRF_TTL_MS = 6e4;
|
|
150
|
+
var REFRESH_LOCK_TTL_MS = 3e3;
|
|
151
|
+
function emitRateLimited(retryAfter) {
|
|
152
|
+
if (typeof window === "undefined" || typeof CustomEvent === "undefined") return;
|
|
153
|
+
window.dispatchEvent(new CustomEvent(RATE_LIMITED_EVENT, { detail: { retryAfter: retryAfter ?? null } }));
|
|
154
|
+
}
|
|
155
|
+
function sleep(ms) {
|
|
156
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
157
|
+
}
|
|
127
158
|
function resolveApiBase() {
|
|
128
159
|
if (typeof document !== "undefined") {
|
|
129
160
|
const runtimeUrl = document.querySelector("meta[name=\"okcms-api-url\"]")?.getAttribute("content")?.trim();
|
|
@@ -139,6 +170,8 @@ var ApiClient = class {
|
|
|
139
170
|
baseUrl;
|
|
140
171
|
csrfToken = null;
|
|
141
172
|
refreshPromise = null;
|
|
173
|
+
proactiveRefreshBound = false;
|
|
174
|
+
csrfFetchedAt = 0;
|
|
142
175
|
refreshOwner = typeof globalThis.crypto?.randomUUID === "function" ? globalThis.crypto.randomUUID() : Math.random().toString(36).slice(2);
|
|
143
176
|
refreshLockKey = "okcms:auth:refresh-lock";
|
|
144
177
|
constructor(baseUrl = API_BASE) {
|
|
@@ -160,14 +193,53 @@ var ApiClient = class {
|
|
|
160
193
|
await this.ensureCsrfToken();
|
|
161
194
|
} catch {}
|
|
162
195
|
}
|
|
196
|
+
/** Sessão em três estados — o status HTTP decide, nunca a string do erro. */
|
|
197
|
+
async checkSession() {
|
|
198
|
+
try {
|
|
199
|
+
await this.request("GET", "/api/v1/auth/me", void 0, void 0, { skipRateLimitRetry: true });
|
|
200
|
+
return "authenticated";
|
|
201
|
+
} catch (error) {
|
|
202
|
+
if (error instanceof ApiError && error.status === 401) return "unauthenticated";
|
|
203
|
+
return "unavailable";
|
|
204
|
+
}
|
|
205
|
+
}
|
|
163
206
|
/** Sessão válida — verifica via /me (usa cookies HttpOnly automaticamente) */
|
|
164
207
|
async isLoggedIn() {
|
|
208
|
+
return await this.checkSession() === "authenticated";
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Refresh PROATIVO do access token.
|
|
212
|
+
*
|
|
213
|
+
* O access token vence em 15min e só era renovado DEPOIS do primeiro 401.
|
|
214
|
+
* O retorno à aba após esse tempo disparava ~20 requisições simultâneas,
|
|
215
|
+
* todas com 401, cada uma reenviando: ~40 chamadas em <1s — quase metade
|
|
216
|
+
* de um budget de rate limit gasto num único gesto.
|
|
217
|
+
*
|
|
218
|
+
* O cookie `session_expires_at` (emitido por /auth/login e /auth/refresh)
|
|
219
|
+
* permite renovar ANTES do vencimento. Cookie ausente (API antiga) mantém
|
|
220
|
+
* o comportamento legado: nada é renovado proativamente.
|
|
221
|
+
*/
|
|
222
|
+
async ensureFreshSession() {
|
|
223
|
+
const expiresAt = this.readSessionExpiry();
|
|
224
|
+
if (!expiresAt || expiresAt - Date.now() > SESSION_REFRESH_MARGIN_MS) return;
|
|
165
225
|
try {
|
|
166
|
-
await this.
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
226
|
+
await this.refreshTokens();
|
|
227
|
+
} catch {}
|
|
228
|
+
}
|
|
229
|
+
/** Instala o refresh proativo uma vez por aba: boot + aba voltando ao foco. */
|
|
230
|
+
installProactiveRefresh() {
|
|
231
|
+
if (typeof document === "undefined" || this.proactiveRefreshBound) return;
|
|
232
|
+
this.proactiveRefreshBound = true;
|
|
233
|
+
document.addEventListener("visibilitychange", () => {
|
|
234
|
+
if (document.visibilityState === "visible") this.ensureFreshSession();
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
readSessionExpiry() {
|
|
238
|
+
if (typeof document === "undefined") return null;
|
|
239
|
+
const match = document.cookie.match(/(?:^|;\s*)session_expires_at=([^;]+)/);
|
|
240
|
+
if (!match?.[1]) return null;
|
|
241
|
+
const parsed = Date.parse(decodeURIComponent(match[1]));
|
|
242
|
+
return Number.isNaN(parsed) ? null : parsed;
|
|
171
243
|
}
|
|
172
244
|
/** Obtém tenantId do backend via /me */
|
|
173
245
|
async getTenantId() {
|
|
@@ -233,58 +305,88 @@ var ApiClient = class {
|
|
|
233
305
|
await this.request("POST", "/api/v1/auth/refresh", {}, void 0, { skipRefresh: true });
|
|
234
306
|
return;
|
|
235
307
|
}
|
|
236
|
-
const
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
current = JSON.parse(localStorage.getItem(this.refreshLockKey) ?? "null");
|
|
240
|
-
} catch {
|
|
241
|
-
await this.request("POST", "/api/v1/auth/refresh", {}, void 0, { skipRefresh: true });
|
|
308
|
+
const current = this.readRefreshLock();
|
|
309
|
+
if (current?.owner && current.owner !== this.refreshOwner && Number(current.expiresAt) > Date.now()) {
|
|
310
|
+
await this.waitForOtherTabRefresh(Number(current.expiresAt));
|
|
242
311
|
return;
|
|
243
312
|
}
|
|
244
|
-
if (
|
|
245
|
-
await this.waitForOtherTabRefresh(
|
|
313
|
+
if (!this.acquireRefreshLock()) {
|
|
314
|
+
await this.waitForOtherTabRefresh(Date.now() + REFRESH_LOCK_TTL_MS);
|
|
246
315
|
return;
|
|
247
316
|
}
|
|
317
|
+
try {
|
|
318
|
+
await this.request("POST", "/api/v1/auth/refresh", {}, void 0, { skipRefresh: true });
|
|
319
|
+
} finally {
|
|
320
|
+
this.releaseRefreshLock();
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
readRefreshLock() {
|
|
324
|
+
try {
|
|
325
|
+
return JSON.parse(localStorage.getItem(this.refreshLockKey) ?? "null");
|
|
326
|
+
} catch {
|
|
327
|
+
return null;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
acquireRefreshLock() {
|
|
248
331
|
try {
|
|
249
332
|
localStorage.setItem(this.refreshLockKey, JSON.stringify({
|
|
250
333
|
owner: this.refreshOwner,
|
|
251
|
-
expiresAt: now +
|
|
334
|
+
expiresAt: Date.now() + REFRESH_LOCK_TTL_MS
|
|
252
335
|
}));
|
|
336
|
+
return true;
|
|
253
337
|
} catch {
|
|
254
|
-
|
|
255
|
-
return;
|
|
338
|
+
return false;
|
|
256
339
|
}
|
|
340
|
+
}
|
|
341
|
+
releaseRefreshLock() {
|
|
257
342
|
try {
|
|
258
|
-
|
|
259
|
-
}
|
|
260
|
-
try {
|
|
261
|
-
if (JSON.parse(localStorage.getItem(this.refreshLockKey) ?? "null")?.owner === this.refreshOwner) localStorage.removeItem(this.refreshLockKey);
|
|
262
|
-
} catch {}
|
|
263
|
-
}
|
|
343
|
+
if (this.readRefreshLock()?.owner === this.refreshOwner) localStorage.removeItem(this.refreshLockKey);
|
|
344
|
+
} catch {}
|
|
264
345
|
}
|
|
346
|
+
/**
|
|
347
|
+
* Espera a outra aba concluir o refresh.
|
|
348
|
+
*
|
|
349
|
+
* O `storage` event acorda esta aba na hora em que a dona libera a chave.
|
|
350
|
+
* O polling anterior (100ms, TTL de 10s) fazia uma aba órfã travar todas
|
|
351
|
+
* as demais pelo tempo inteiro, e o deadline era herdado do lock vencido.
|
|
352
|
+
*/
|
|
265
353
|
async waitForOtherTabRefresh(expiresAt) {
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
if (
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
354
|
+
const deadline = Math.max(0, Math.min(expiresAt, Date.now() + REFRESH_LOCK_TTL_MS) - Date.now());
|
|
355
|
+
await new Promise((resolve) => {
|
|
356
|
+
let settled = false;
|
|
357
|
+
const finish = () => {
|
|
358
|
+
if (settled) return;
|
|
359
|
+
settled = true;
|
|
360
|
+
window.removeEventListener("storage", onStorage);
|
|
361
|
+
clearTimeout(timer);
|
|
362
|
+
resolve();
|
|
363
|
+
};
|
|
364
|
+
const onStorage = (event) => {
|
|
365
|
+
if (event.key === this.refreshLockKey) finish();
|
|
366
|
+
};
|
|
367
|
+
window.addEventListener("storage", onStorage);
|
|
368
|
+
const timer = setTimeout(finish, deadline);
|
|
369
|
+
});
|
|
275
370
|
}
|
|
276
|
-
/**
|
|
277
|
-
*
|
|
278
|
-
*
|
|
279
|
-
* -
|
|
371
|
+
/**
|
|
372
|
+
* Requisição com as recuperações de sessão/segurança centralizadas:
|
|
373
|
+
*
|
|
374
|
+
* - auto-refresh em 401 (exceto /refresh e /logout);
|
|
375
|
+
* - rebootstrap do CSRF quando outra aba renovou o token (403);
|
|
376
|
+
* - retry de leitura em 429 respeitando o `Retry-After` curto.
|
|
377
|
+
*
|
|
378
|
+
* Um 429 SEMPRE significa "nada aconteceu" (o limiter responde antes do
|
|
379
|
+
* handler), então repetir a chamada é seguro até para escritas.
|
|
380
|
+
*
|
|
381
|
+
* `request()` e `requestRaw()` compartilham este caminho — antes a lógica
|
|
382
|
+
* estava duplicada, e qualquer correção de resiliência teria que ser feita
|
|
383
|
+
* em dois lugares (o que explica por que não havia tratamento de 429).
|
|
280
384
|
*/
|
|
281
|
-
async
|
|
385
|
+
async sendWithRecovery(method, path, body, headers, options) {
|
|
282
386
|
const url = joinApiUrl(this.baseUrl, path);
|
|
283
387
|
const send = async () => {
|
|
284
|
-
const requestHeaders = {
|
|
285
|
-
|
|
286
|
-
...headers
|
|
287
|
-
};
|
|
388
|
+
const requestHeaders = { ...headers };
|
|
389
|
+
if (options?.bodyInit !== void 0 || body !== void 0) requestHeaders["Content-Type"] = "application/json";
|
|
288
390
|
if (![
|
|
289
391
|
"GET",
|
|
290
392
|
"HEAD",
|
|
@@ -297,7 +399,7 @@ var ApiClient = class {
|
|
|
297
399
|
method,
|
|
298
400
|
headers: requestHeaders,
|
|
299
401
|
credentials: "include",
|
|
300
|
-
body: body ? JSON.stringify(body) : void 0
|
|
402
|
+
body: options?.bodyInit ?? (body !== void 0 ? JSON.stringify(body) : void 0)
|
|
301
403
|
});
|
|
302
404
|
};
|
|
303
405
|
let response = await send();
|
|
@@ -315,51 +417,27 @@ var ApiClient = class {
|
|
|
315
417
|
response = await send();
|
|
316
418
|
}
|
|
317
419
|
}
|
|
318
|
-
if (
|
|
319
|
-
const
|
|
320
|
-
|
|
420
|
+
if (response.status === 429 && !options?.skipRateLimitRetry) {
|
|
421
|
+
const retryAfterHeader = response.headers?.get?.("Retry-After") ?? "";
|
|
422
|
+
const retryAfter = Number(retryAfterHeader);
|
|
423
|
+
const waitMs = (Number.isFinite(retryAfter) && retryAfter > 0 ? retryAfter : 1) * 1e3;
|
|
424
|
+
emitRateLimited(Number.isFinite(retryAfter) && retryAfter > 0 ? retryAfter : 1);
|
|
425
|
+
if (waitMs <= MAX_RATE_LIMIT_WAIT_MS) {
|
|
426
|
+
await sleep(waitMs + 50);
|
|
427
|
+
response = await send();
|
|
428
|
+
}
|
|
321
429
|
}
|
|
430
|
+
return response;
|
|
431
|
+
}
|
|
432
|
+
/** Requisição JSON: dispara ApiError com status quando a resposta falha. */
|
|
433
|
+
async request(method, path, body, headers, options) {
|
|
434
|
+
const response = await this.sendWithRecovery(method, path, body, headers, options);
|
|
435
|
+
if (!response.ok) throw await ApiError.fromResponse(response);
|
|
322
436
|
return response.json();
|
|
323
437
|
}
|
|
324
438
|
/** Requisição genérica que retorna o Response bruto (para blobs, downloads, etc.). */
|
|
325
439
|
async requestRaw(method, path, body, headers, options) {
|
|
326
|
-
|
|
327
|
-
const send = async () => {
|
|
328
|
-
const requestHeaders = {
|
|
329
|
-
"Content-Type": "application/json",
|
|
330
|
-
...headers
|
|
331
|
-
};
|
|
332
|
-
if (![
|
|
333
|
-
"GET",
|
|
334
|
-
"HEAD",
|
|
335
|
-
"OPTIONS"
|
|
336
|
-
].includes(method.toUpperCase())) {
|
|
337
|
-
const csrf = await this.ensureCsrfToken();
|
|
338
|
-
if (csrf) requestHeaders["x-csrf-token"] = csrf;
|
|
339
|
-
}
|
|
340
|
-
return fetch(url, {
|
|
341
|
-
method,
|
|
342
|
-
headers: requestHeaders,
|
|
343
|
-
credentials: "include",
|
|
344
|
-
body: body ? JSON.stringify(body) : void 0
|
|
345
|
-
});
|
|
346
|
-
};
|
|
347
|
-
let response = await send();
|
|
348
|
-
if (response.status === 401 && !options?.skipRefresh && !path.includes("/auth/refresh") && !path.includes("/auth/logout")) {
|
|
349
|
-
await this.refreshTokens();
|
|
350
|
-
response = await send();
|
|
351
|
-
}
|
|
352
|
-
if (response.status === 403 && !options?.skipCsrfRecovery) {
|
|
353
|
-
let errorCode = "";
|
|
354
|
-
try {
|
|
355
|
-
errorCode = (await (typeof response.clone === "function" ? response.clone() : response).json()).error ?? "";
|
|
356
|
-
} catch {}
|
|
357
|
-
if (errorCode === "CSRF_TOKEN_INVALID") {
|
|
358
|
-
await this.ensureCsrfToken(true);
|
|
359
|
-
response = await send();
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
return response;
|
|
440
|
+
return this.sendWithRecovery(method, path, body, headers, options);
|
|
363
441
|
}
|
|
364
442
|
/** Garante um token CSRF sincronizado com o cookie do browser. */
|
|
365
443
|
async ensureCsrfToken(force = false) {
|
|
@@ -368,7 +446,7 @@ var ApiClient = class {
|
|
|
368
446
|
this.csrfToken = cookieToken;
|
|
369
447
|
return cookieToken;
|
|
370
448
|
}
|
|
371
|
-
if (!force && this.csrfToken) return this.csrfToken;
|
|
449
|
+
if (!force && this.csrfToken && Date.now() - this.csrfFetchedAt < CSRF_TTL_MS) return this.csrfToken;
|
|
372
450
|
try {
|
|
373
451
|
const response = await fetch(joinApiUrl(this.baseUrl, "/api/v1/auth/csrf"), {
|
|
374
452
|
credentials: "include",
|
|
@@ -377,6 +455,7 @@ var ApiClient = class {
|
|
|
377
455
|
if (!response.ok) return this.csrfToken;
|
|
378
456
|
const data = await response.json();
|
|
379
457
|
this.csrfToken = data.csrfToken ?? this.readCsrfCookie();
|
|
458
|
+
this.csrfFetchedAt = Date.now();
|
|
380
459
|
} catch {}
|
|
381
460
|
return this.csrfToken;
|
|
382
461
|
}
|
|
@@ -394,6 +473,7 @@ var ApiClient = class {
|
|
|
394
473
|
if (options?.type) params.set("type", options.type);
|
|
395
474
|
if (options?.status) params.set("status", options.status);
|
|
396
475
|
if (options?.search) params.set("search", options.search);
|
|
476
|
+
if (options?.includeAuthor) params.set("include", "author");
|
|
397
477
|
return this.request("GET", `/api/v1/content?${params.toString()}`);
|
|
398
478
|
}
|
|
399
479
|
async getContentById(id) {
|
|
@@ -565,37 +645,8 @@ var ApiClient = class {
|
|
|
565
645
|
formData.append("file", file);
|
|
566
646
|
if (options?.alt) formData.append("alt", options.alt);
|
|
567
647
|
if (options?.caption) formData.append("caption", options.caption);
|
|
568
|
-
const
|
|
569
|
-
|
|
570
|
-
const csrf = await this.ensureCsrfToken();
|
|
571
|
-
const requestHeaders = {};
|
|
572
|
-
if (csrf) requestHeaders["x-csrf-token"] = csrf;
|
|
573
|
-
return fetch(url, {
|
|
574
|
-
method: "POST",
|
|
575
|
-
headers: requestHeaders,
|
|
576
|
-
credentials: "include",
|
|
577
|
-
body: formData
|
|
578
|
-
});
|
|
579
|
-
};
|
|
580
|
-
let response = await send();
|
|
581
|
-
if (response.status === 401) {
|
|
582
|
-
await this.refreshTokens();
|
|
583
|
-
response = await send();
|
|
584
|
-
}
|
|
585
|
-
if (response.status === 403) {
|
|
586
|
-
let errorCode = "";
|
|
587
|
-
try {
|
|
588
|
-
errorCode = (await response.clone().json()).error ?? "";
|
|
589
|
-
} catch {}
|
|
590
|
-
if (errorCode === "CSRF_TOKEN_INVALID") {
|
|
591
|
-
await this.ensureCsrfToken(true);
|
|
592
|
-
response = await send();
|
|
593
|
-
}
|
|
594
|
-
}
|
|
595
|
-
if (!response.ok) {
|
|
596
|
-
const error = await response.json().catch(() => ({ error: `Upload failed: ${response.status}` }));
|
|
597
|
-
throw new Error(error.error ?? `Upload failed: ${response.status}`);
|
|
598
|
-
}
|
|
648
|
+
const response = await this.sendWithRecovery("POST", "/api/v1/media/upload", void 0, {}, { bodyInit: formData });
|
|
649
|
+
if (!response.ok) throw await ApiError.fromResponse(response);
|
|
599
650
|
return response.json();
|
|
600
651
|
}
|
|
601
652
|
async updateMedia(id, data) {
|
|
@@ -604,33 +655,8 @@ var ApiClient = class {
|
|
|
604
655
|
async replaceMediaFile(id, file) {
|
|
605
656
|
const formData = new FormData();
|
|
606
657
|
formData.append("file", file);
|
|
607
|
-
const
|
|
608
|
-
|
|
609
|
-
const csrf = await this.ensureCsrfToken();
|
|
610
|
-
const headers = {};
|
|
611
|
-
if (csrf) headers["x-csrf-token"] = csrf;
|
|
612
|
-
return fetch(url, {
|
|
613
|
-
method: "PUT",
|
|
614
|
-
headers,
|
|
615
|
-
credentials: "include",
|
|
616
|
-
body: formData
|
|
617
|
-
});
|
|
618
|
-
};
|
|
619
|
-
let response = await send();
|
|
620
|
-
if (response.status === 401) {
|
|
621
|
-
await this.refreshTokens();
|
|
622
|
-
response = await send();
|
|
623
|
-
}
|
|
624
|
-
if (response.status === 403) {
|
|
625
|
-
if ((await response.clone().json().catch(() => ({}))).error === "CSRF_TOKEN_INVALID") {
|
|
626
|
-
await this.ensureCsrfToken(true);
|
|
627
|
-
response = await send();
|
|
628
|
-
}
|
|
629
|
-
}
|
|
630
|
-
if (!response.ok) {
|
|
631
|
-
const error = await response.json().catch(() => ({ error: `Replace failed: ${response.status}` }));
|
|
632
|
-
throw new Error(error.error ?? `Replace failed: ${response.status}`);
|
|
633
|
-
}
|
|
658
|
+
const response = await this.sendWithRecovery("PUT", `/api/v1/media/${id}/file`, void 0, {}, { bodyInit: formData });
|
|
659
|
+
if (!response.ok) throw await ApiError.fromResponse(response);
|
|
634
660
|
return response.json();
|
|
635
661
|
}
|
|
636
662
|
async deleteMedia(id) {
|
|
@@ -833,7 +859,9 @@ var en_exports = /* @__PURE__ */ __exportAll({
|
|
|
833
859
|
logs: () => logs,
|
|
834
860
|
media: () => media,
|
|
835
861
|
menus: () => menus,
|
|
862
|
+
notFound: () => notFound,
|
|
836
863
|
plugins: () => plugins,
|
|
864
|
+
rateLimit: () => rateLimit,
|
|
837
865
|
resources: () => resources,
|
|
838
866
|
screenOptions: () => screenOptions,
|
|
839
867
|
settings: () => settings,
|
|
@@ -1107,376 +1135,7 @@ var users = {
|
|
|
1107
1135
|
"confirmDelete": "Delete user {email}?"
|
|
1108
1136
|
}
|
|
1109
1137
|
};
|
|
1110
|
-
var settings = {
|
|
1111
|
-
"general": {
|
|
1112
|
-
"title": "General",
|
|
1113
|
-
"description": "Basic information displayed on the site.",
|
|
1114
|
-
"back": "← All settings",
|
|
1115
|
-
"fields": {
|
|
1116
|
-
"siteTitle": "Site Title",
|
|
1117
|
-
"siteTitlePlaceholder": "My Site",
|
|
1118
|
-
"siteDescription": "Description",
|
|
1119
|
-
"siteDescriptionPlaceholder": "A new site built with OkCMS",
|
|
1120
|
-
"language": "Language",
|
|
1121
|
-
"timezone": "Timezone",
|
|
1122
|
-
"timezonePlaceholder": "America/Sao_Paulo"
|
|
1123
|
-
},
|
|
1124
|
-
"saved": "Settings saved ✓",
|
|
1125
|
-
"loading": "Loading…",
|
|
1126
|
-
"save": "Save"
|
|
1127
|
-
},
|
|
1128
|
-
"postTypes": {
|
|
1129
|
-
"title": "Content Types",
|
|
1130
|
-
"description": "Create custom content types (CPTs) beyond Posts and Pages.",
|
|
1131
|
-
"form": {
|
|
1132
|
-
"title": "Create Content Type",
|
|
1133
|
-
"slug": "Slug (singular, e.g.: portfolio)",
|
|
1134
|
-
"slugHint": "Only lowercase letters, numbers and underscore",
|
|
1135
|
-
"singular": "Singular name",
|
|
1136
|
-
"plural": "Plural name",
|
|
1137
|
-
"supports": "Supports",
|
|
1138
|
-
"supportsOptions": {
|
|
1139
|
-
"title": "Title",
|
|
1140
|
-
"editor": "Editor",
|
|
1141
|
-
"thumbnail": "Featured Image",
|
|
1142
|
-
"excerpt": "Excerpt",
|
|
1143
|
-
"revisions": "Revisions"
|
|
1144
|
-
},
|
|
1145
|
-
"hasArchive": "Has public archive (/slug)",
|
|
1146
|
-
"singleton": "Singleton (only one content entry)",
|
|
1147
|
-
"icon": "Sidebar icon (Lucide name)",
|
|
1148
|
-
"iconHint": "Examples: box, package, shopping-bag, file-text",
|
|
1149
|
-
"editTitle": "Edit Content Type",
|
|
1150
|
-
"update": "Update",
|
|
1151
|
-
"cancel": "Cancel",
|
|
1152
|
-
"create": "Create"
|
|
1153
|
-
},
|
|
1154
|
-
"table": {
|
|
1155
|
-
"slug": "Slug",
|
|
1156
|
-
"singular": "Singular",
|
|
1157
|
-
"plural": "Plural",
|
|
1158
|
-
"source": "Source",
|
|
1159
|
-
"supports": "Supports",
|
|
1160
|
-
"archive": "Archive",
|
|
1161
|
-
"mode": "Mode",
|
|
1162
|
-
"singleton": "Singleton",
|
|
1163
|
-
"collection": "Collection",
|
|
1164
|
-
"icon": "Icon",
|
|
1165
|
-
"actions": {
|
|
1166
|
-
"edit": "Edit",
|
|
1167
|
-
"remove": "Remove"
|
|
1168
|
-
}
|
|
1169
|
-
},
|
|
1170
|
-
"sources": {
|
|
1171
|
-
"admin": "Admin",
|
|
1172
|
-
"core": "Core"
|
|
1173
|
-
}
|
|
1174
|
-
},
|
|
1175
|
-
"taxonomies": {
|
|
1176
|
-
"title": "Taxonomies",
|
|
1177
|
-
"description": "Create custom taxonomies and link them to content types.",
|
|
1178
|
-
"form": {
|
|
1179
|
-
"title": "Create Taxonomy",
|
|
1180
|
-
"name": "Name (e.g.: Genre)",
|
|
1181
|
-
"slug": "Slug (optional, generated from name)",
|
|
1182
|
-
"slugHint": "Only lowercase letters, numbers and underscore",
|
|
1183
|
-
"hierarchical": "Hierarchical (allows parent/child terms)",
|
|
1184
|
-
"attachTo": "Link to content types",
|
|
1185
|
-
"create": "Create"
|
|
1186
|
-
},
|
|
1187
|
-
"table": {
|
|
1188
|
-
"taxonomy": "Taxonomy",
|
|
1189
|
-
"hierarchical": "Hierarchical",
|
|
1190
|
-
"attachedTypes": "Linked content types",
|
|
1191
|
-
"actions": { "remove": "Remove" }
|
|
1192
|
-
}
|
|
1193
|
-
},
|
|
1194
|
-
"customFields": {
|
|
1195
|
-
"title": "Custom Fields",
|
|
1196
|
-
"description": "Create field groups, arrange fields and choose which standard fields are visible for each content type.",
|
|
1197
|
-
"contentType": "Content type",
|
|
1198
|
-
"location": {
|
|
1199
|
-
"title": "Location rules",
|
|
1200
|
-
"description": "Choose where this field group appears. Rules in the same group use AND; groups use OR.",
|
|
1201
|
-
"parameter": "Location parameter",
|
|
1202
|
-
"operator": "Operator",
|
|
1203
|
-
"chooseValue": "Choose a value",
|
|
1204
|
-
"valuePlaceholder": "Enter a value or slug",
|
|
1205
|
-
"showIf": "Show if",
|
|
1206
|
-
"removeRule": "Remove rule",
|
|
1207
|
-
"required": "Add a value to every location rule.",
|
|
1208
|
-
"addRule": "Add condition",
|
|
1209
|
-
"addGroup": "Add OR group",
|
|
1210
|
-
"remove": "Remove",
|
|
1211
|
-
"and": "AND",
|
|
1212
|
-
"or": "OR",
|
|
1213
|
-
"params": {
|
|
1214
|
-
"content_type": "Post type",
|
|
1215
|
-
"content_slug": "Post slug",
|
|
1216
|
-
"taxonomy": "Taxonomy",
|
|
1217
|
-
"term": "Term slug",
|
|
1218
|
-
"user_role": "User role",
|
|
1219
|
-
"page_template": "Page template",
|
|
1220
|
-
"post_status": "Post status"
|
|
1221
|
-
},
|
|
1222
|
-
"operators": {
|
|
1223
|
-
"eq": "is",
|
|
1224
|
-
"neq": "is not"
|
|
1225
|
-
}
|
|
1226
|
-
},
|
|
1227
|
-
"standard": {
|
|
1228
|
-
"title": "Standard fields",
|
|
1229
|
-
"description": "Title and slug are required. The other standard fields can be enabled or disabled.",
|
|
1230
|
-
"enabled": "Enabled",
|
|
1231
|
-
"locked": "Required",
|
|
1232
|
-
"fields": {
|
|
1233
|
-
"title": "Title",
|
|
1234
|
-
"slug": "Slug",
|
|
1235
|
-
"content": "Content",
|
|
1236
|
-
"excerpt": "Excerpt",
|
|
1237
|
-
"featured_image": "Featured image",
|
|
1238
|
-
"seo_title": "SEO title",
|
|
1239
|
-
"seo_description": "SEO description",
|
|
1240
|
-
"status": "Status",
|
|
1241
|
-
"author": "Author"
|
|
1242
|
-
}
|
|
1243
|
-
},
|
|
1244
|
-
"groups": {
|
|
1245
|
-
"title": "Field groups",
|
|
1246
|
-
"new": "New field group",
|
|
1247
|
-
"empty": "No custom field groups yet.",
|
|
1248
|
-
"emptyHint": "Create your first group to start modelling your content.",
|
|
1249
|
-
"search": "Search groups…",
|
|
1250
|
-
"groupName": "Group",
|
|
1251
|
-
"status": "Status",
|
|
1252
|
-
"fields": "fields",
|
|
1253
|
-
"appliesTo": "Applies to",
|
|
1254
|
-
"allContentTypes": "All content types",
|
|
1255
|
-
"edit": "Edit",
|
|
1256
|
-
"active": "Active",
|
|
1257
|
-
"inactive": "Inactive"
|
|
1258
|
-
},
|
|
1259
|
-
"form": {
|
|
1260
|
-
"createTitle": "Create field group",
|
|
1261
|
-
"editTitle": "Edit field group",
|
|
1262
|
-
"title": "Group name",
|
|
1263
|
-
"titlePlaceholder": "Example: Product details",
|
|
1264
|
-
"displayTitle": "Display title",
|
|
1265
|
-
"active": "Active",
|
|
1266
|
-
"backToGroups": "Back to groups",
|
|
1267
|
-
"editorEyebrow": "Field builder",
|
|
1268
|
-
"groupTitleHint": "This name identifies the field set inside the content editor.",
|
|
1269
|
-
"fieldsTitle": "Fields",
|
|
1270
|
-
"fieldsDescription": "Add, configure and order fields. Use the six-dot handle to drag without losing their position.",
|
|
1271
|
-
"noFields": "This group has no fields yet.",
|
|
1272
|
-
"settingsTitle": "Group settings",
|
|
1273
|
-
"settingsDescription": "Presentation and panel visibility options for this panel.",
|
|
1274
|
-
"description": "Description",
|
|
1275
|
-
"labelPlacement": "Label placement",
|
|
1276
|
-
"instructionPlacement": "Instruction placement",
|
|
1277
|
-
"menuOrder": "Menu order",
|
|
1278
|
-
"hideOnScreen": "Hide on edit screen",
|
|
1279
|
-
"hide": {
|
|
1280
|
-
"permalink": "Permalink",
|
|
1281
|
-
"content": "Content editor",
|
|
1282
|
-
"excerpt": "Excerpt",
|
|
1283
|
-
"featured_image": "Featured image",
|
|
1284
|
-
"author": "Author",
|
|
1285
|
-
"revisions": "Revisions",
|
|
1286
|
-
"comments": "Comments"
|
|
1287
|
-
},
|
|
1288
|
-
"assignTitle": "Location",
|
|
1289
|
-
"assignDescription": "Choose the content type where this field group will be available.",
|
|
1290
|
-
"contentType": "Content type",
|
|
1291
|
-
"position": "Position",
|
|
1292
|
-
"positions": {
|
|
1293
|
-
"normal": "Normal",
|
|
1294
|
-
"side": "Sidebar",
|
|
1295
|
-
"after_title": "After title"
|
|
1296
|
-
},
|
|
1297
|
-
"displayStyle": "Display style",
|
|
1298
|
-
"displayStyles": {
|
|
1299
|
-
"standard": "Standard",
|
|
1300
|
-
"seamless": "Seamless",
|
|
1301
|
-
"grouped": "Grouped"
|
|
1302
|
-
},
|
|
1303
|
-
"field": "Field",
|
|
1304
|
-
"fieldName": "Name/key",
|
|
1305
|
-
"fieldNamePlaceholder": "product_code",
|
|
1306
|
-
"fieldLabel": "Label",
|
|
1307
|
-
"fieldLabelPlaceholder": "Product code",
|
|
1308
|
-
"fieldType": "Type",
|
|
1309
|
-
"instructions": "Instructions",
|
|
1310
|
-
"instructionsPlaceholder": "Optional help text for editors",
|
|
1311
|
-
"required": "Required",
|
|
1312
|
-
"options": "Options",
|
|
1313
|
-
"optionsHint": "One option per line: value|Label",
|
|
1314
|
-
"addField": "Add field",
|
|
1315
|
-
"removeField": "Remove",
|
|
1316
|
-
"dragHint": "Drag to reorder",
|
|
1317
|
-
"editField": "Edit",
|
|
1318
|
-
"duplicateField": "Duplicate",
|
|
1319
|
-
"copySuffix": "(copy)",
|
|
1320
|
-
"selectFieldType": "Select field type",
|
|
1321
|
-
"searchFieldTypes": "Search field types…",
|
|
1322
|
-
"popularFields": "Popular",
|
|
1323
|
-
"noMatchingFieldTypes": "No field types found.",
|
|
1324
|
-
"fieldTypeHint": "Choose the type that best represents the value editors will enter in this field.",
|
|
1325
|
-
"selectField": "Select field",
|
|
1326
|
-
"save": "Save group",
|
|
1327
|
-
"cancel": "Cancel",
|
|
1328
|
-
"newFieldDefault": "New field",
|
|
1329
|
-
"saved": "Field group saved.",
|
|
1330
|
-
"deleted": "Field group deleted."
|
|
1331
|
-
},
|
|
1332
|
-
"listEyebrow": "Content structure",
|
|
1333
|
-
"validation": { "titleRequired": "Group name is required." },
|
|
1334
|
-
"tabs": {
|
|
1335
|
-
"locationRules": "Location rules",
|
|
1336
|
-
"presentation": "Presentation",
|
|
1337
|
-
"groupSettings": "Group settings"
|
|
1338
|
-
},
|
|
1339
|
-
"fieldSettings": {
|
|
1340
|
-
"general": "General",
|
|
1341
|
-
"fieldLabel": "Field label",
|
|
1342
|
-
"fieldName": "Field name",
|
|
1343
|
-
"instructions": "Instructions",
|
|
1344
|
-
"required": "Required",
|
|
1345
|
-
"defaultValue": "Default value",
|
|
1346
|
-
"placeholder": "Placeholder",
|
|
1347
|
-
"presentation": "Presentation",
|
|
1348
|
-
"wrapperWidth": "Wrapper width (%)",
|
|
1349
|
-
"labelPlacement": "Label",
|
|
1350
|
-
"instructionPlacement": "Instructions",
|
|
1351
|
-
"top": "Above",
|
|
1352
|
-
"left": "Left",
|
|
1353
|
-
"above": "Before field",
|
|
1354
|
-
"below": "After field",
|
|
1355
|
-
"textSettings": "Text",
|
|
1356
|
-
"prepend": "Prepend",
|
|
1357
|
-
"append": "Append",
|
|
1358
|
-
"minLength": "Minimum length",
|
|
1359
|
-
"maxLength": "Maximum length",
|
|
1360
|
-
"rows": "Rows",
|
|
1361
|
-
"newLines": "New lines",
|
|
1362
|
-
"wpautop": "Automatic paragraphs",
|
|
1363
|
-
"none": "None",
|
|
1364
|
-
"numberSettings": "Number",
|
|
1365
|
-
"min": "Minimum",
|
|
1366
|
-
"max": "Maximum",
|
|
1367
|
-
"step": "Step",
|
|
1368
|
-
"choiceSettings": "Choices",
|
|
1369
|
-
"choices": "Choices",
|
|
1370
|
-
"layout": "Layout",
|
|
1371
|
-
"vertical": "Vertical",
|
|
1372
|
-
"horizontal": "Horizontal",
|
|
1373
|
-
"multiple": "Multiple choice",
|
|
1374
|
-
"allowNull": "Allow empty",
|
|
1375
|
-
"mediaSettings": "Media",
|
|
1376
|
-
"returnFormat": "Return format",
|
|
1377
|
-
"array": "Array",
|
|
1378
|
-
"id": "ID",
|
|
1379
|
-
"previewSize": "Preview size",
|
|
1380
|
-
"library": "Library",
|
|
1381
|
-
"all": "All",
|
|
1382
|
-
"uploadedToPost": "Uploaded to this post",
|
|
1383
|
-
"allowedTypes": "Allowed types",
|
|
1384
|
-
"minWidth": "Minimum width",
|
|
1385
|
-
"maxWidth": "Maximum width",
|
|
1386
|
-
"minHeight": "Minimum height",
|
|
1387
|
-
"maxHeight": "Maximum height",
|
|
1388
|
-
"wysiwygSettings": "WYSIWYG",
|
|
1389
|
-
"tabs": "Tabs",
|
|
1390
|
-
"toolbar": "Toolbar",
|
|
1391
|
-
"mediaUpload": "Allow media upload",
|
|
1392
|
-
"dateSettings": "Date and time",
|
|
1393
|
-
"displayFormat": "Display format",
|
|
1394
|
-
"firstDay": "First day",
|
|
1395
|
-
"colorSettings": "Color",
|
|
1396
|
-
"enableOpacity": "Allow opacity",
|
|
1397
|
-
"mapSettings": "Map",
|
|
1398
|
-
"center": "Center",
|
|
1399
|
-
"zoom": "Zoom",
|
|
1400
|
-
"height": "Height",
|
|
1401
|
-
"relationSettings": "Relationship",
|
|
1402
|
-
"postType": "Post type",
|
|
1403
|
-
"taxonomy": "Taxonomy",
|
|
1404
|
-
"booleanSettings": "True/False",
|
|
1405
|
-
"message": "Message",
|
|
1406
|
-
"defaultChecked": "Checked by default",
|
|
1407
|
-
"linkSettings": "Link",
|
|
1408
|
-
"cloneSettings": "Clone",
|
|
1409
|
-
"cloneFields": "Fields to clone",
|
|
1410
|
-
"cloneHint": "One field name per line",
|
|
1411
|
-
"containerSettings": "Structure",
|
|
1412
|
-
"buttonLabel": "Button label",
|
|
1413
|
-
"collapsed": "Collapsed row title field",
|
|
1414
|
-
"subFields": "Subfields",
|
|
1415
|
-
"layouts": "Layouts",
|
|
1416
|
-
"addLayout": "Add layout",
|
|
1417
|
-
"newLayout": "New layout",
|
|
1418
|
-
"layoutName": "Layout name",
|
|
1419
|
-
"layoutLabel": "Layout label",
|
|
1420
|
-
"layoutDisplay": "Layout display",
|
|
1421
|
-
"conditionalLogic": "Conditional logic",
|
|
1422
|
-
"conditionalHint": "Show this field only when the rules match.",
|
|
1423
|
-
"chooseField": "Choose a field",
|
|
1424
|
-
"conditionValue": "Value",
|
|
1425
|
-
"addCondition": "Add condition",
|
|
1426
|
-
"addConditionGroup": "Add OR group",
|
|
1427
|
-
"conditionsAnd": "Conditions in this group use AND.",
|
|
1428
|
-
"choicesHint": "One choice per line in the format value|Label.",
|
|
1429
|
-
"cloneSource": "Source group or field",
|
|
1430
|
-
"cloneSourceHint": "Field group key or field name to copy fields from.",
|
|
1431
|
-
"requiredOn": "Required",
|
|
1432
|
-
"requiredOff": "Not required",
|
|
1433
|
-
"libraryAll": "All",
|
|
1434
|
-
"libraryUploaded": "Uploaded to this post",
|
|
1435
|
-
"ui": "Enhanced UI",
|
|
1436
|
-
"uiHint": "Use enhanced styling for the dropdown.",
|
|
1437
|
-
"wrapperClass": "Wrapper class",
|
|
1438
|
-
"wrapperWidthHint": "CSS width of the field wrapper, e.g. 50%.",
|
|
1439
|
-
"collapsedHint": "Name of the sub-field used as the row title.",
|
|
1440
|
-
"conditionalActive": "Active",
|
|
1441
|
-
"showIf": "Show if",
|
|
1442
|
-
"removeRule": "Remove rule",
|
|
1443
|
-
"and": "AND",
|
|
1444
|
-
"or": "OR",
|
|
1445
|
-
"noLayouts": "No layouts yet.",
|
|
1446
|
-
"validation": "Validation",
|
|
1447
|
-
"advanced": "Advanced",
|
|
1448
|
-
"fieldType": "Type",
|
|
1449
|
-
"fieldNameHint": "Letters, numbers and underscores only.",
|
|
1450
|
-
"ariaLabel": "Field settings",
|
|
1451
|
-
"operators": {
|
|
1452
|
-
"eq": "equals",
|
|
1453
|
-
"neq": "does not equal",
|
|
1454
|
-
"gt": "is greater than",
|
|
1455
|
-
"lt": "is less than",
|
|
1456
|
-
"contains": "contains",
|
|
1457
|
-
"hasAny": "has any value",
|
|
1458
|
-
"hasNone": "is empty"
|
|
1459
|
-
}
|
|
1460
|
-
},
|
|
1461
|
-
"actions": {
|
|
1462
|
-
"delete": "Delete",
|
|
1463
|
-
"confirmDelete": "Delete this field group?",
|
|
1464
|
-
"edit": "Edit",
|
|
1465
|
-
"duplicate": "Duplicate",
|
|
1466
|
-
"activate": "Activate",
|
|
1467
|
-
"deactivate": "Deactivate",
|
|
1468
|
-
"export": "Export",
|
|
1469
|
-
"import": "Import"
|
|
1470
|
-
},
|
|
1471
|
-
"import": {
|
|
1472
|
-
"description": "Import field groups from a JSON file. The file must contain a field group object or an array of field group objects. Existing groups with the same key will be updated; new groups will be created.",
|
|
1473
|
-
"dropzone": "Click to select a .json file, or drag and drop here",
|
|
1474
|
-
"hint": "File selected. Click Import to proceed."
|
|
1475
|
-
},
|
|
1476
|
-
"loading": "Loading custom fields…",
|
|
1477
|
-
"loadError": "Could not load custom fields."
|
|
1478
|
-
}
|
|
1479
|
-
};
|
|
1138
|
+
var settings = /*#__PURE__*/ JSON.parse("{\"general\":{\"title\":\"General\",\"description\":\"Basic information displayed on the site.\",\"back\":\"← All settings\",\"fields\":{\"siteTitle\":\"Site Title\",\"siteTitlePlaceholder\":\"My Site\",\"siteDescription\":\"Description\",\"siteDescriptionPlaceholder\":\"A new site built with OkCMS\",\"language\":\"Language\",\"timezone\":\"Timezone\",\"timezonePlaceholder\":\"America/Sao_Paulo\"},\"saved\":\"Settings saved ✓\",\"loading\":\"Loading…\",\"save\":\"Save\"},\"postTypes\":{\"title\":\"Content Types\",\"description\":\"Create custom content types (CPTs) beyond Posts and Pages.\",\"form\":{\"title\":\"Create Content Type\",\"slug\":\"Slug (singular, e.g.: portfolio)\",\"slugHint\":\"Only lowercase letters, numbers and underscore\",\"singular\":\"Singular name\",\"plural\":\"Plural name\",\"supports\":\"Supports\",\"supportsOptions\":{\"title\":\"Title\",\"editor\":\"Editor\",\"thumbnail\":\"Featured Image\",\"excerpt\":\"Excerpt\",\"revisions\":\"Revisions\"},\"hasArchive\":\"Has public archive (/slug)\",\"singleton\":\"Singleton (only one content entry)\",\"icon\":\"Sidebar menu icon\",\"iconHint\":\"Pick a Lucide icon — its name is what shows in the admin sidebar\",\"iconSearchPlaceholder\":\"Search icons…\",\"iconEmpty\":\"No icons found.\",\"iconCount\":\"{count} icons available\",\"editTitle\":\"Edit Content Type\",\"update\":\"Update\",\"cancel\":\"Cancel\",\"create\":\"Create\"},\"table\":{\"slug\":\"Slug\",\"singular\":\"Singular\",\"plural\":\"Plural\",\"source\":\"Source\",\"supports\":\"Supports\",\"archive\":\"Archive\",\"mode\":\"Mode\",\"singleton\":\"Singleton\",\"collection\":\"Collection\",\"icon\":\"Icon\",\"actions\":{\"edit\":\"Edit\",\"remove\":\"Remove\"}},\"sources\":{\"admin\":\"Admin\",\"core\":\"Core\"}},\"taxonomies\":{\"title\":\"Taxonomies\",\"description\":\"Create custom taxonomies and link them to content types.\",\"form\":{\"title\":\"Create Taxonomy\",\"name\":\"Name (e.g.: Genre)\",\"slug\":\"Slug (optional, generated from name)\",\"slugHint\":\"Only lowercase letters, numbers and underscore\",\"hierarchical\":\"Hierarchical (allows parent/child terms)\",\"attachTo\":\"Link to content types\",\"create\":\"Create\"},\"table\":{\"taxonomy\":\"Taxonomy\",\"hierarchical\":\"Hierarchical\",\"attachedTypes\":\"Linked content types\",\"actions\":{\"remove\":\"Remove\"}}},\"customFields\":{\"title\":\"Custom Fields\",\"description\":\"Create field groups, arrange fields and choose which standard fields are visible for each content type.\",\"contentType\":\"Content type\",\"location\":{\"title\":\"Location rules\",\"description\":\"Choose where this field group appears. Rules in the same group use AND; groups use OR.\",\"parameter\":\"Location parameter\",\"operator\":\"Operator\",\"chooseValue\":\"Choose a value\",\"valuePlaceholder\":\"Enter a value or slug\",\"showIf\":\"Show if\",\"removeRule\":\"Remove rule\",\"required\":\"Add a value to every location rule.\",\"addRule\":\"Add condition\",\"addGroup\":\"Add OR group\",\"remove\":\"Remove\",\"and\":\"AND\",\"or\":\"OR\",\"params\":{\"content_type\":\"Post type\",\"content_slug\":\"Post slug\",\"taxonomy\":\"Taxonomy\",\"term\":\"Term slug\",\"user_role\":\"User role\",\"page_template\":\"Page template\",\"post_status\":\"Post status\"},\"operators\":{\"eq\":\"is\",\"neq\":\"is not\"}},\"standard\":{\"title\":\"Standard fields\",\"description\":\"Title and slug are required. The other standard fields can be enabled or disabled.\",\"enabled\":\"Enabled\",\"locked\":\"Required\",\"fields\":{\"title\":\"Title\",\"slug\":\"Slug\",\"content\":\"Content\",\"excerpt\":\"Excerpt\",\"featured_image\":\"Featured image\",\"seo_title\":\"SEO title\",\"seo_description\":\"SEO description\",\"status\":\"Status\",\"author\":\"Author\"}},\"groups\":{\"title\":\"Field groups\",\"new\":\"New field group\",\"empty\":\"No custom field groups yet.\",\"emptyHint\":\"Create your first group to start modelling your content.\",\"search\":\"Search groups…\",\"groupName\":\"Group\",\"status\":\"Status\",\"fields\":\"fields\",\"appliesTo\":\"Applies to\",\"allContentTypes\":\"All content types\",\"edit\":\"Edit\",\"active\":\"Active\",\"inactive\":\"Inactive\"},\"form\":{\"createTitle\":\"Create field group\",\"editTitle\":\"Edit field group\",\"title\":\"Group name\",\"titlePlaceholder\":\"Example: Product details\",\"displayTitle\":\"Display title\",\"active\":\"Active\",\"backToGroups\":\"Back to groups\",\"editorEyebrow\":\"Field builder\",\"groupTitleHint\":\"This name identifies the field set inside the content editor.\",\"fieldsTitle\":\"Fields\",\"fieldsDescription\":\"Add, configure and order fields. Use the six-dot handle to drag without losing their position.\",\"noFields\":\"This group has no fields yet.\",\"settingsTitle\":\"Group settings\",\"settingsDescription\":\"Presentation and panel visibility options for this panel.\",\"description\":\"Description\",\"labelPlacement\":\"Label placement\",\"instructionPlacement\":\"Instruction placement\",\"menuOrder\":\"Menu order\",\"hideOnScreen\":\"Hide on edit screen\",\"hide\":{\"permalink\":\"Permalink\",\"content\":\"Content editor\",\"excerpt\":\"Excerpt\",\"featured_image\":\"Featured image\",\"author\":\"Author\",\"revisions\":\"Revisions\",\"comments\":\"Comments\"},\"assignTitle\":\"Location\",\"assignDescription\":\"Choose the content type where this field group will be available.\",\"contentType\":\"Content type\",\"position\":\"Position\",\"positions\":{\"normal\":\"Normal\",\"side\":\"Sidebar\",\"after_title\":\"After title\"},\"displayStyle\":\"Display style\",\"displayStyles\":{\"standard\":\"Standard\",\"seamless\":\"Seamless\",\"grouped\":\"Grouped\"},\"field\":\"Field\",\"fieldName\":\"Name/key\",\"fieldNamePlaceholder\":\"product_code\",\"fieldLabel\":\"Label\",\"fieldLabelPlaceholder\":\"Product code\",\"fieldType\":\"Type\",\"instructions\":\"Instructions\",\"instructionsPlaceholder\":\"Optional help text for editors\",\"required\":\"Required\",\"options\":\"Options\",\"optionsHint\":\"One option per line: value|Label\",\"addField\":\"Add field\",\"removeField\":\"Remove\",\"dragHint\":\"Drag to reorder\",\"editField\":\"Edit\",\"duplicateField\":\"Duplicate\",\"copySuffix\":\"(copy)\",\"selectFieldType\":\"Select field type\",\"searchFieldTypes\":\"Search field types…\",\"popularFields\":\"Popular\",\"noMatchingFieldTypes\":\"No field types found.\",\"fieldTypeHint\":\"Choose the type that best represents the value editors will enter in this field.\",\"selectField\":\"Select field\",\"save\":\"Save group\",\"cancel\":\"Cancel\",\"newFieldDefault\":\"New field\",\"saved\":\"Field group saved.\",\"deleted\":\"Field group deleted.\"},\"listEyebrow\":\"Content structure\",\"validation\":{\"titleRequired\":\"Group name is required.\"},\"tabs\":{\"locationRules\":\"Location rules\",\"presentation\":\"Presentation\",\"groupSettings\":\"Group settings\"},\"fieldSettings\":{\"general\":\"General\",\"fieldLabel\":\"Field label\",\"fieldName\":\"Field name\",\"instructions\":\"Instructions\",\"required\":\"Required\",\"defaultValue\":\"Default value\",\"placeholder\":\"Placeholder\",\"presentation\":\"Presentation\",\"wrapperWidth\":\"Wrapper width (%)\",\"labelPlacement\":\"Label\",\"instructionPlacement\":\"Instructions\",\"top\":\"Above\",\"left\":\"Left\",\"above\":\"Before field\",\"below\":\"After field\",\"textSettings\":\"Text\",\"prepend\":\"Prepend\",\"append\":\"Append\",\"minLength\":\"Minimum length\",\"maxLength\":\"Maximum length\",\"rows\":\"Rows\",\"newLines\":\"New lines\",\"wpautop\":\"Automatic paragraphs\",\"none\":\"None\",\"numberSettings\":\"Number\",\"min\":\"Minimum\",\"max\":\"Maximum\",\"step\":\"Step\",\"choiceSettings\":\"Choices\",\"choices\":\"Choices\",\"layout\":\"Layout\",\"vertical\":\"Vertical\",\"horizontal\":\"Horizontal\",\"multiple\":\"Multiple choice\",\"allowNull\":\"Allow empty\",\"mediaSettings\":\"Media\",\"returnFormat\":\"Return format\",\"array\":\"Array\",\"id\":\"ID\",\"previewSize\":\"Preview size\",\"library\":\"Library\",\"all\":\"All\",\"uploadedToPost\":\"Uploaded to this post\",\"allowedTypes\":\"Allowed types\",\"minWidth\":\"Minimum width\",\"maxWidth\":\"Maximum width\",\"minHeight\":\"Minimum height\",\"maxHeight\":\"Maximum height\",\"wysiwygSettings\":\"WYSIWYG\",\"tabs\":\"Tabs\",\"toolbar\":\"Toolbar\",\"mediaUpload\":\"Allow media upload\",\"dateSettings\":\"Date and time\",\"displayFormat\":\"Display format\",\"firstDay\":\"First day\",\"colorSettings\":\"Color\",\"enableOpacity\":\"Allow opacity\",\"mapSettings\":\"Map\",\"center\":\"Center\",\"zoom\":\"Zoom\",\"height\":\"Height\",\"relationSettings\":\"Relationship\",\"postType\":\"Post type\",\"taxonomy\":\"Taxonomy\",\"booleanSettings\":\"True/False\",\"message\":\"Message\",\"defaultChecked\":\"Checked by default\",\"linkSettings\":\"Link\",\"cloneSettings\":\"Clone\",\"cloneFields\":\"Fields to clone\",\"cloneHint\":\"One field name per line\",\"containerSettings\":\"Structure\",\"buttonLabel\":\"Button label\",\"collapsed\":\"Collapsed row title field\",\"subFields\":\"Subfields\",\"layouts\":\"Layouts\",\"addLayout\":\"Add layout\",\"newLayout\":\"New layout\",\"layoutName\":\"Layout name\",\"layoutLabel\":\"Layout label\",\"layoutDisplay\":\"Layout display\",\"conditionalLogic\":\"Conditional logic\",\"conditionalHint\":\"Show this field only when the rules match.\",\"chooseField\":\"Choose a field\",\"conditionValue\":\"Value\",\"addCondition\":\"Add condition\",\"addConditionGroup\":\"Add OR group\",\"conditionsAnd\":\"Conditions in this group use AND.\",\"choicesHint\":\"One choice per line in the format value|Label.\",\"cloneSource\":\"Source group or field\",\"cloneSourceHint\":\"Field group key or field name to copy fields from.\",\"requiredOn\":\"Required\",\"requiredOff\":\"Not required\",\"libraryAll\":\"All\",\"libraryUploaded\":\"Uploaded to this post\",\"ui\":\"Enhanced UI\",\"uiHint\":\"Use enhanced styling for the dropdown.\",\"wrapperClass\":\"Wrapper class\",\"wrapperWidthHint\":\"CSS width of the field wrapper, e.g. 50%.\",\"collapsedHint\":\"Name of the sub-field used as the row title.\",\"conditionalActive\":\"Active\",\"showIf\":\"Show if\",\"removeRule\":\"Remove rule\",\"and\":\"AND\",\"or\":\"OR\",\"noLayouts\":\"No layouts yet.\",\"validation\":\"Validation\",\"advanced\":\"Advanced\",\"fieldType\":\"Type\",\"fieldNameHint\":\"Letters, numbers and underscores only.\",\"ariaLabel\":\"Field settings\",\"operators\":{\"eq\":\"equals\",\"neq\":\"does not equal\",\"gt\":\"is greater than\",\"lt\":\"is less than\",\"contains\":\"contains\",\"hasAny\":\"has any value\",\"hasNone\":\"is empty\"}},\"actions\":{\"delete\":\"Delete\",\"confirmDelete\":\"Delete this field group?\",\"edit\":\"Edit\",\"duplicate\":\"Duplicate\",\"activate\":\"Activate\",\"deactivate\":\"Deactivate\",\"export\":\"Export\",\"import\":\"Import\"},\"import\":{\"description\":\"Import field groups from a JSON file. The file must contain a field group object or an array of field group objects. Existing groups with the same key will be updated; new groups will be created.\",\"dropzone\":\"Click to select a .json file, or drag and drop here\",\"hint\":\"File selected. Click Import to proceed.\"},\"loading\":\"Loading custom fields…\",\"loadError\":\"Could not load custom fields.\"}}");
|
|
1480
1139
|
var menus = {
|
|
1481
1140
|
"title": "Menus",
|
|
1482
1141
|
"description": "Create and manage the navigation menus used by your site.",
|
|
@@ -1565,8 +1224,36 @@ var access = {
|
|
|
1565
1224
|
"description": "Manage roles, permissions and tenants.",
|
|
1566
1225
|
"roles": "Roles",
|
|
1567
1226
|
"tenants": "Tenants",
|
|
1568
|
-
"permissions": "Permissions
|
|
1569
|
-
"domain": "Domain"
|
|
1227
|
+
"permissions": "Permissions",
|
|
1228
|
+
"domain": "Domain",
|
|
1229
|
+
"permissionsPlaceholder": "No permissions selected",
|
|
1230
|
+
"permissionsSummary": "{resources} resources · {count} permissions",
|
|
1231
|
+
"permissionSearch": "Filter resources…",
|
|
1232
|
+
"permissionNoMatches": "No resource matches.",
|
|
1233
|
+
"customPermissions": "Custom permissions",
|
|
1234
|
+
"customPermission": "permission:resource",
|
|
1235
|
+
"permissionsSelected": "{count} selected",
|
|
1236
|
+
"newRole": "New role",
|
|
1237
|
+
"editRole": "Edit role",
|
|
1238
|
+
"newTenant": "New tenant",
|
|
1239
|
+
"editTenant": "Edit tenant",
|
|
1240
|
+
"resources": {
|
|
1241
|
+
"content": "Content",
|
|
1242
|
+
"media": "Media",
|
|
1243
|
+
"category": "Categories",
|
|
1244
|
+
"tag": "Tags",
|
|
1245
|
+
"menu": "Menus",
|
|
1246
|
+
"user": "Users",
|
|
1247
|
+
"roles": "Roles",
|
|
1248
|
+
"theme": "Themes",
|
|
1249
|
+
"plugin": "Plugins",
|
|
1250
|
+
"setting": "Settings",
|
|
1251
|
+
"webhook": "Webhooks",
|
|
1252
|
+
"tenants": "Tenants",
|
|
1253
|
+
"audit": "Audit",
|
|
1254
|
+
"build": "Builds",
|
|
1255
|
+
"system": "System"
|
|
1256
|
+
}
|
|
1570
1257
|
};
|
|
1571
1258
|
var logs = {
|
|
1572
1259
|
"title": "Logs",
|
|
@@ -1694,6 +1381,18 @@ var themes = {
|
|
|
1694
1381
|
"configError": "Could not save the theme settings.",
|
|
1695
1382
|
"empty": "No themes installed."
|
|
1696
1383
|
};
|
|
1384
|
+
var notFound = {
|
|
1385
|
+
"title": "Page not found",
|
|
1386
|
+
"description": "The address may be out of date or the page may have moved. Check the URL or pick one of the sections below.",
|
|
1387
|
+
"requestedPath": "You tried to open",
|
|
1388
|
+
"suggestionsLabel": "Or visit one of these",
|
|
1389
|
+
"goHome": "Back to home",
|
|
1390
|
+
"goBack": "Go back"
|
|
1391
|
+
};
|
|
1392
|
+
var rateLimit = {
|
|
1393
|
+
"notice": "Request limit reached — wait a few seconds and try again.",
|
|
1394
|
+
"noticeRetry": "Request limit reached — retrying automatically in {seconds}s."
|
|
1395
|
+
};
|
|
1697
1396
|
var login = {
|
|
1698
1397
|
"title": "Login",
|
|
1699
1398
|
"subtitle": "Sign in to your account",
|
|
@@ -1740,7 +1439,8 @@ var common = {
|
|
|
1740
1439
|
},
|
|
1741
1440
|
"language": "Language",
|
|
1742
1441
|
"refresh": "Refresh",
|
|
1743
|
-
"actions": "Actions"
|
|
1442
|
+
"actions": "Actions",
|
|
1443
|
+
"add": "Add"
|
|
1744
1444
|
};
|
|
1745
1445
|
var activity = {
|
|
1746
1446
|
"justNow": "just now",
|
|
@@ -1805,6 +1505,8 @@ var en_default = {
|
|
|
1805
1505
|
webhooks,
|
|
1806
1506
|
plugins,
|
|
1807
1507
|
themes,
|
|
1508
|
+
notFound,
|
|
1509
|
+
rateLimit,
|
|
1808
1510
|
login,
|
|
1809
1511
|
common,
|
|
1810
1512
|
activity,
|
|
@@ -1898,4 +1600,4 @@ async function getTranslationsForLocale(locale) {
|
|
|
1898
1600
|
return loadLocale(locale);
|
|
1899
1601
|
}
|
|
1900
1602
|
//#endregion
|
|
1901
|
-
export {
|
|
1603
|
+
export { LOCALE_LABELS as a, LOCALE_FLAGS as i, useTranslation as n, SUPPORTED_LOCALES as o, apiClient as r, isRTLLocale as s, getTranslationsForLocale as t };
|