@iblai/iblai-js 2.27.1 → 2.28.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.
|
@@ -1815,7 +1815,11 @@ var advancedCustomDomain$3 = {
|
|
|
1815
1815
|
noProjects: "You have not deployed any apps yet. Deploy one first, then come back to give it a domain.",
|
|
1816
1816
|
customDomainLabel: "Domain",
|
|
1817
1817
|
domainHelpPlatform: "Without the scheme or a leading “www.” — for example, app.example.com.",
|
|
1818
|
-
domainHelpApp: "
|
|
1818
|
+
domainHelpApp: "A domain you own, without the scheme or a leading “www.” — for example, app.example.com. Leave it empty to use the subdomain above.",
|
|
1819
|
+
subdomainLabel: "Subdomain",
|
|
1820
|
+
subdomainHelp: "Lowercase letters, digits and hyphens, like a username. The app is served at <subdomain>.<the platform’s shared domain>, with no DNS records to set.",
|
|
1821
|
+
oneOfSubdomainOrDomain: "Fill in either the subdomain or a domain you own, not both.",
|
|
1822
|
+
subdomainInvalid: "A subdomain is 1–63 lowercase letters, digits and hyphens, and cannot start or end with a hyphen.",
|
|
1819
1823
|
cancel: "Cancel",
|
|
1820
1824
|
save: "Save",
|
|
1821
1825
|
expandAriaLabel: "Expand domains",
|
|
@@ -1837,6 +1841,9 @@ var advancedCustomDomain$3 = {
|
|
|
1837
1841
|
copyNameHostAriaLabel: "Copy name/host: {domain}",
|
|
1838
1842
|
copyValueAriaLabel: "Copy value/target",
|
|
1839
1843
|
noRecordsYet: "No records yet. Check the domain again to fetch them.",
|
|
1844
|
+
sharedDomainLine: "Runs on our shared domain — nothing to set up.",
|
|
1845
|
+
openSite: "Open {domain}",
|
|
1846
|
+
servesApp: "Serves {app}",
|
|
1840
1847
|
confirmRemoveTitle: "Remove this domain?",
|
|
1841
1848
|
confirmDeleteBody: "{domain} will stop signing users in to this organization.",
|
|
1842
1849
|
confirmDetachBody: "{domain} will stop serving the app, here and at the hosting provider.",
|
|
@@ -9360,7 +9367,11 @@ var advancedCustomDomain$2 = {
|
|
|
9360
9367
|
noProjects: "Vous n’avez encore déployé aucune application. Déployez-en une, puis revenez lui donner un domaine.",
|
|
9361
9368
|
customDomainLabel: "Domaine",
|
|
9362
9369
|
domainHelpPlatform: "Sans le schéma ni un « www. » en tête — par exemple, app.example.com.",
|
|
9363
|
-
domainHelpApp: "
|
|
9370
|
+
domainHelpApp: "Un domaine qui vous appartient, sans le schéma ni un « www. » en tête — par exemple, app.example.com. Laissez ce champ vide pour utiliser le sous-domaine ci-dessus.",
|
|
9371
|
+
subdomainLabel: "Sous-domaine",
|
|
9372
|
+
subdomainHelp: "Lettres minuscules, chiffres et tirets, comme un nom d’utilisateur. L’application est servie à <sous-domaine>.<le domaine partagé de la plateforme>, sans aucun enregistrement DNS à créer.",
|
|
9373
|
+
oneOfSubdomainOrDomain: "Renseignez soit le sous-domaine, soit un domaine qui vous appartient, pas les deux.",
|
|
9374
|
+
subdomainInvalid: "Un sous-domaine compte de 1 à 63 lettres minuscules, chiffres et tirets, et ne peut ni commencer ni finir par un tiret.",
|
|
9364
9375
|
cancel: "Annuler",
|
|
9365
9376
|
save: "Enregistrer",
|
|
9366
9377
|
expandAriaLabel: "Déplier les domaines",
|
|
@@ -9382,6 +9393,9 @@ var advancedCustomDomain$2 = {
|
|
|
9382
9393
|
copyNameHostAriaLabel: "Copier le nom/hôte : {domain}",
|
|
9383
9394
|
copyValueAriaLabel: "Copier la valeur/cible",
|
|
9384
9395
|
noRecordsYet: "Aucun enregistrement pour l’instant. Vérifiez le domaine à nouveau pour les obtenir.",
|
|
9396
|
+
sharedDomainLine: "Fonctionne sur notre domaine partagé — rien à configurer.",
|
|
9397
|
+
openSite: "Ouvrir {domain}",
|
|
9398
|
+
servesApp: "Dessert {app}",
|
|
9385
9399
|
confirmRemoveTitle: "Supprimer ce domaine ?",
|
|
9386
9400
|
confirmDeleteBody: "{domain} ne permettra plus de se connecter à cette organisation.",
|
|
9387
9401
|
confirmDetachBody: "{domain} cessera de servir l’application, ici comme chez l’hébergeur.",
|
|
@@ -16905,7 +16919,11 @@ var advancedCustomDomain$1 = {
|
|
|
16905
16919
|
noProjects: "Todavía no has desplegado ninguna aplicación. Despliega una primero y vuelve para darle un dominio.",
|
|
16906
16920
|
customDomainLabel: "Dominio",
|
|
16907
16921
|
domainHelpPlatform: "Sin el esquema ni un “www.” inicial — por ejemplo, app.example.com.",
|
|
16908
|
-
domainHelpApp: "
|
|
16922
|
+
domainHelpApp: "Un dominio tuyo, sin el esquema ni un “www.” inicial — por ejemplo, app.example.com. Déjalo vacío para usar el subdominio de arriba.",
|
|
16923
|
+
subdomainLabel: "Subdominio",
|
|
16924
|
+
subdomainHelp: "Letras minúsculas, dígitos y guiones, como un nombre de usuario. La aplicación se sirve en <subdominio>.<el dominio compartido de la plataforma>, sin registros DNS que configurar.",
|
|
16925
|
+
oneOfSubdomainOrDomain: "Rellena el subdominio o un dominio tuyo, no ambos.",
|
|
16926
|
+
subdomainInvalid: "Un subdominio tiene entre 1 y 63 letras minúsculas, dígitos y guiones, y no puede empezar ni terminar con un guion.",
|
|
16909
16927
|
cancel: "Cancelar",
|
|
16910
16928
|
save: "Guardar",
|
|
16911
16929
|
expandAriaLabel: "Expandir dominios",
|
|
@@ -16927,6 +16945,9 @@ var advancedCustomDomain$1 = {
|
|
|
16927
16945
|
copyNameHostAriaLabel: "Copiar nombre/host: {domain}",
|
|
16928
16946
|
copyValueAriaLabel: "Copiar valor/destino",
|
|
16929
16947
|
noRecordsYet: "Aún no hay registros. Comprueba el dominio de nuevo para obtenerlos.",
|
|
16948
|
+
sharedDomainLine: "Funciona en nuestro dominio compartido — no hay nada que configurar.",
|
|
16949
|
+
openSite: "Abrir {domain}",
|
|
16950
|
+
servesApp: "Sirve {app}",
|
|
16930
16951
|
confirmRemoveTitle: "¿Eliminar este dominio?",
|
|
16931
16952
|
confirmDeleteBody: "{domain} dejará de permitir el inicio de sesión en esta organización.",
|
|
16932
16953
|
confirmDetachBody: "{domain} dejará de servir la aplicación, aquí y en el proveedor de alojamiento.",
|
|
@@ -24450,7 +24471,11 @@ var advancedCustomDomain = {
|
|
|
24450
24471
|
noProjects: "你还没有部署任何应用。请先部署一个,然后回来为它设置域名。",
|
|
24451
24472
|
customDomainLabel: "域名",
|
|
24452
24473
|
domainHelpPlatform: "不要带协议前缀或开头的“www.”,例如 app.example.com。",
|
|
24453
|
-
domainHelpApp: "
|
|
24474
|
+
domainHelpApp: "你拥有的域名,不要带协议前缀或开头的“www.”,例如 app.example.com。留空则使用上方的子域名。",
|
|
24475
|
+
subdomainLabel: "子域名",
|
|
24476
|
+
subdomainHelp: "小写字母、数字和连字符,像用户名一样。应用将通过 <子域名>.<平台的共享域名> 提供服务,无需设置任何 DNS 记录。",
|
|
24477
|
+
oneOfSubdomainOrDomain: "请填写子域名或你拥有的域名,二者只填其一。",
|
|
24478
|
+
subdomainInvalid: "子域名为 1 到 63 个小写字母、数字和连字符,且不能以连字符开头或结尾。",
|
|
24454
24479
|
cancel: "取消",
|
|
24455
24480
|
save: "保存",
|
|
24456
24481
|
expandAriaLabel: "展开域名",
|
|
@@ -24472,6 +24497,9 @@ var advancedCustomDomain = {
|
|
|
24472
24497
|
copyNameHostAriaLabel: "复制名称/主机:{domain}",
|
|
24473
24498
|
copyValueAriaLabel: "复制值/目标",
|
|
24474
24499
|
noRecordsYet: "暂无记录。请重新检查该域名以获取记录。",
|
|
24500
|
+
sharedDomainLine: "运行在我们的共享域名上——无需任何设置。",
|
|
24501
|
+
openSite: "打开 {domain}",
|
|
24502
|
+
servesApp: "服务于 {app}",
|
|
24475
24503
|
confirmRemoveTitle: "要移除这个域名吗?",
|
|
24476
24504
|
confirmDeleteBody: "{domain} 将不再用于登录该组织。",
|
|
24477
24505
|
confirmDetachBody: "{domain} 将不再为该应用提供服务,本处和托管服务商处都会解除。",
|
|
@@ -198286,15 +198314,23 @@ const sanitizeDomain = (domain) => domain
|
|
|
198286
198314
|
.replace(/^https?:\/\//, '')
|
|
198287
198315
|
.replace(/\/.*$/, '')
|
|
198288
198316
|
.replace(/^www\./, '');
|
|
198289
|
-
|
|
198317
|
+
/** One label under the platform's shared domain — the backend's own rule. */
|
|
198318
|
+
const SUBDOMAIN_RE = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
|
|
198319
|
+
/** Typed like a username: only case and surrounding whitespace are forgiven. */
|
|
198320
|
+
const sanitizeSubdomain = (subdomain) => subdomain.trim().toLowerCase();
|
|
198321
|
+
const AddDomainDialog = ({ open, onOpenChange, canAddPlatformDomain, defaultMode = 'platform', projects, isLoadingProjects, isSubmitting, onSubmitPlatformDomain, onSubmitAppDomain, }) => {
|
|
198322
|
+
var _a;
|
|
198290
198323
|
const t = useT();
|
|
198291
|
-
const
|
|
198324
|
+
const initialMode = canAddPlatformDomain ? defaultMode : 'app';
|
|
198325
|
+
const [mode, setMode] = useState(initialMode);
|
|
198292
198326
|
const [domainValue, setDomainValue] = useState('');
|
|
198327
|
+
const [subdomainValue, setSubdomainValue] = useState('');
|
|
198293
198328
|
const [projectId, setProjectId] = useState('');
|
|
198294
198329
|
const reset = () => {
|
|
198295
198330
|
setDomainValue('');
|
|
198331
|
+
setSubdomainValue('');
|
|
198296
198332
|
setProjectId('');
|
|
198297
|
-
setMode(
|
|
198333
|
+
setMode(initialMode);
|
|
198298
198334
|
};
|
|
198299
198335
|
const close = () => {
|
|
198300
198336
|
reset();
|
|
@@ -198306,10 +198342,27 @@ const AddDomainDialog = ({ open, onOpenChange, canAddPlatformDomain, projects, i
|
|
|
198306
198342
|
await onSubmitPlatformDomain(sanitizeDomain(domainValue));
|
|
198307
198343
|
}
|
|
198308
198344
|
else {
|
|
198309
|
-
await onSubmitAppDomain(Number(projectId),
|
|
198345
|
+
await onSubmitAppDomain(Number(projectId), {
|
|
198346
|
+
domain: sanitizeDomain(domainValue),
|
|
198347
|
+
subdomain: sanitizeSubdomain(subdomainValue),
|
|
198348
|
+
});
|
|
198310
198349
|
}
|
|
198311
198350
|
};
|
|
198312
|
-
const
|
|
198351
|
+
const hasDomain = domainValue.trim().length > 0;
|
|
198352
|
+
const subdomain = sanitizeSubdomain(subdomainValue);
|
|
198353
|
+
const hasSubdomain = subdomain.length > 0;
|
|
198354
|
+
// An app is served at exactly one address, and a subdomain has to be one the
|
|
198355
|
+
// backend will take — say which rule is unmet, and only once something was
|
|
198356
|
+
// typed, so an untouched form stays quiet.
|
|
198357
|
+
const appAddressProblem = mode === 'app' && hasDomain && hasSubdomain
|
|
198358
|
+
? 'both'
|
|
198359
|
+
: mode === 'app' && hasSubdomain && !SUBDOMAIN_RE.test(subdomain)
|
|
198360
|
+
? 'invalid'
|
|
198361
|
+
: null;
|
|
198362
|
+
const canSubmit = mode === 'platform'
|
|
198363
|
+
? hasDomain
|
|
198364
|
+
: projectId.trim().length > 0 && (hasDomain || hasSubdomain) && appAddressProblem === null;
|
|
198365
|
+
const selectedProject = projects.find((project) => String(project.id) === projectId);
|
|
198313
198366
|
return (jsx(Dialog, { open: open, onOpenChange: (next) => {
|
|
198314
198367
|
if (!next) {
|
|
198315
198368
|
reset();
|
|
@@ -198318,9 +198371,11 @@ const AddDomainDialog = ({ open, onOpenChange, canAddPlatformDomain, projects, i
|
|
|
198318
198371
|
}, children: jsxs(DialogContent, { className: "sm:max-w-[480px]", children: [jsxs(DialogHeader, { children: [jsx(DialogTitle, { children: t('advancedCustomDomain.dialogTitle') }), jsx(DialogDescription, { children: t('advancedCustomDomain.dialogDescription') })] }), jsxs("form", { onSubmit: handleSubmit, className: "space-y-4", children: [jsxs("div", { className: "space-y-2", children: [jsx("label", { htmlFor: "add-domain-kind", className: "text-sm", children: t('advancedCustomDomain.kindLabel') }), jsxs(Select$1, { value: mode, onValueChange: (next) => setMode(next), children: [jsx(SelectTrigger, { id: "add-domain-kind", children: jsx(SelectValue, {}) }), jsxs(SelectContent, { children: [canAddPlatformDomain && (jsx(SelectItem, { value: "platform", children: t('advancedCustomDomain.kindPlatform') })), jsx(SelectItem, { value: "app", children: t('advancedCustomDomain.kindApp') })] })] })] }), mode === 'app' && (jsxs("div", { className: "space-y-2", children: [jsx("label", { htmlFor: "add-domain-project", className: "text-sm", children: t('advancedCustomDomain.projectLabel') }), isLoadingProjects ? (jsx("p", { className: "text-sm text-[#646464]", children: t('advancedCustomDomain.loading') })) : projects.length === 0 ? (
|
|
198319
198372
|
// The backend lists only the caller's own projects, so this is
|
|
198320
198373
|
// "you have not deployed anything", not "the platform has not".
|
|
198321
|
-
jsx("p", { className: "text-sm text-[#646464]", children: t('advancedCustomDomain.noProjects') })) : (jsxs(Select$1, { value: projectId, onValueChange: setProjectId, children: [jsx(SelectTrigger, { id: "add-domain-project", children: jsx(SelectValue, { placeholder: t('advancedCustomDomain.projectPlaceholder') }) }), jsx(SelectContent, { children: projects.map((project) => (jsx(SelectItem, { value: String(project.id), children: project.name }, project.id))) })] }))] })), jsxs("div", { className: "space-y-2", children: [jsx("label", { htmlFor: "add-domain-
|
|
198374
|
+
jsx("p", { className: "text-sm text-[#646464]", children: t('advancedCustomDomain.noProjects') })) : (jsxs(Select$1, { value: projectId, onValueChange: setProjectId, children: [jsx(SelectTrigger, { id: "add-domain-project", children: jsx(SelectValue, { placeholder: t('advancedCustomDomain.projectPlaceholder') }) }), jsx(SelectContent, { children: projects.map((project) => (jsx(SelectItem, { value: String(project.id), children: project.name }, project.id))) })] }))] })), mode === 'app' && (jsxs("div", { className: "space-y-2", children: [jsx("label", { htmlFor: "add-domain-subdomain", className: "text-sm", children: t('advancedCustomDomain.subdomainLabel') }), jsx(Input, { id: "add-domain-subdomain", value: subdomainValue, onChange: (event) => setSubdomainValue(event.target.value),
|
|
198375
|
+
// The app's own name is the suggestion the deploy skill makes.
|
|
198376
|
+
placeholder: (_a = selectedProject === null || selectedProject === void 0 ? void 0 : selectedProject.name) !== null && _a !== void 0 ? _a : '', className: "w-full" }), jsx("p", { className: "text-xs text-[#646464]", children: t('advancedCustomDomain.subdomainHelp') })] })), jsxs("div", { className: "space-y-2", children: [jsx("label", { htmlFor: "add-domain-name", className: "text-sm", children: t('advancedCustomDomain.customDomainLabel') }), jsx(Input, { id: "add-domain-name", value: domainValue, onChange: (event) => setDomainValue(event.target.value), placeholder: "example.com", className: "w-full", required: mode === 'platform' }), jsx("p", { className: "text-xs text-[#646464]", children: mode === 'app'
|
|
198322
198377
|
? t('advancedCustomDomain.domainHelpApp')
|
|
198323
|
-
: t('advancedCustomDomain.domainHelpPlatform') })] }), jsxs("div", { className: "flex justify-end gap-2", children: [jsx(Button$1, { type: "button", variant: "outline", onClick: close, children: t('advancedCustomDomain.cancel') }), jsxs(Button$1, { type: "submit", disabled: isSubmitting || !canSubmit, children: [isSubmitting && jsx(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), t('advancedCustomDomain.save')] })] })] })] }) }));
|
|
198378
|
+
: t('advancedCustomDomain.domainHelpPlatform') }), appAddressProblem === 'both' && (jsx("p", { className: "text-xs text-[#646464]", role: "status", children: t('advancedCustomDomain.oneOfSubdomainOrDomain') })), appAddressProblem === 'invalid' && (jsx("p", { className: "text-xs text-[#646464]", role: "status", children: t('advancedCustomDomain.subdomainInvalid') }))] }), jsxs("div", { className: "flex justify-end gap-2", children: [jsx(Button$1, { type: "button", variant: "outline", onClick: close, children: t('advancedCustomDomain.cancel') }), jsxs(Button$1, { type: "submit", disabled: isSubmitting || !canSubmit, children: [isSubmitting && jsx(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), t('advancedCustomDomain.save')] })] })] })] }) }));
|
|
198324
198379
|
};
|
|
198325
198380
|
|
|
198326
198381
|
var SLOTTABLE_IDENTIFIER = Symbol("radix.slottable");
|
|
@@ -198512,7 +198567,7 @@ const resolveCname = async (domain, target) => {
|
|
|
198512
198567
|
const body = await response.json();
|
|
198513
198568
|
return Boolean((_a = body === null || body === void 0 ? void 0 : body.Answer) === null || _a === void 0 ? void 0 : _a.some((answer) => { var _a; return ((_a = answer.data) !== null && _a !== void 0 ? _a : '').replace(/\.$/, '') === target; }));
|
|
198514
198569
|
};
|
|
198515
|
-
const DomainRow = ({ row, sslValue, onReverify, onRemove, isBusy, onStatusChange, }) => {
|
|
198570
|
+
const DomainRow = ({ row, sharedApp, sslValue, onReverify, onRemove, isBusy, onStatusChange, }) => {
|
|
198516
198571
|
var _a;
|
|
198517
198572
|
const t = useT();
|
|
198518
198573
|
const isHosted = row.spa === VERCEL_SPA;
|
|
@@ -198584,7 +198639,7 @@ const DomainRow = ({ row, sslValue, onReverify, onRemove, isBusy, onStatusChange
|
|
|
198584
198639
|
const statusBadge = (jsx("span", { className: status === 'verified'
|
|
198585
198640
|
? 'rounded bg-green-100 px-2 py-1 text-xs text-green-800'
|
|
198586
198641
|
: 'rounded bg-gray-100 px-2 py-1 text-xs text-gray-600', children: status === 'checking' ? (jsxs("span", { className: "flex items-center gap-1", children: [jsx(LoaderCircle, { className: "h-3 w-3 animate-spin" }), t('advancedCustomDomain.verifying')] })) : (statusLabel()) }));
|
|
198587
|
-
return (jsxs("div", { className: "space-y-3 rounded-lg border border-gray-200 p-3 dark:border-gray-700", children: [jsxs("div", { className: "flex flex-wrap items-center justify-between gap-2", children: [jsxs("div", { className: "flex items-center gap-2", children: [jsx("span", { className: "text-sm", children: row.custom_domain }), jsx("span", { className: "text-xs text-[#646464]", children: isHosted ? t('advancedCustomDomain.kindApp') : t('advancedCustomDomain.kindPlatform') }), statusBadge] }), jsxs("div", { className: "flex items-center gap-2", children: [jsx(Button$1, { variant: "outline", size: "sm", onClick: () => (isHosted ? void onReverify(row) : void checkSpaDomain()), disabled: isBusy || status === 'checking' || (!isHosted && !row.registered_with_dns_pro), "aria-label": t('advancedCustomDomain.reverifyAriaLabel', { domain: row.custom_domain }), children: isBusy || status === 'checking' ? (jsx(LoaderCircle, { className: "h-4 w-4 animate-spin" })) : (jsxs(Fragment$1, { children: [jsx(RefreshCw, { className: "mr-1 h-4 w-4", "aria-hidden": "true" }), t('advancedCustomDomain.reverify')] })) }), jsx(Button$1, { variant: "outline", size: "sm", className: "text-red-600 hover:text-red-700", onClick: () => setConfirmOpen(true), disabled: isBusy, "aria-label": t('advancedCustomDomain.removeAriaLabel', { domain: row.custom_domain }), children: isHosted ? t('advancedCustomDomain.detach') : t('advancedCustomDomain.delete') })] })] }), records.length > 0 ? (jsxs(Table, { className: "table-fixed", children: [jsx(TableHeader, { children: jsxs(TableRow, { children: [jsx(TableHead, { className: "w-[90px]", children: t('advancedCustomDomain.tableType') }), jsx(TableHead, { className: "w-1/3", children: t('advancedCustomDomain.tableNameHost') }), jsx(TableHead, { children: t('advancedCustomDomain.tableValue') })] }) }), jsx(TableBody, { children: records.map((record, index) => (jsxs(TableRow, { children: [jsx(TableCell, { className: "text-sm", children: record.type }), jsx(TableCell, { children: jsxs("div", { className: "flex items-center gap-2", children: [jsx("span", { className: "text-sm break-all", children: record.name }), jsx(Button$1, { type: "button", variant: "ghost", size: "icon", className: "h-6 w-6 shrink-0", onClick: () => handleCopy(record.name, `name-${index}`), "aria-label": t('advancedCustomDomain.copyNameHostAriaLabel', {
|
|
198642
|
+
return (jsxs("div", { className: "space-y-3 rounded-lg border border-gray-200 p-3 dark:border-gray-700", children: [jsxs("div", { className: "flex flex-wrap items-center justify-between gap-2", children: [jsxs("div", { className: "flex items-center gap-2", children: [jsx("span", { className: "text-sm", children: row.custom_domain }), jsx("span", { className: "text-xs text-[#646464]", children: isHosted ? t('advancedCustomDomain.kindApp') : t('advancedCustomDomain.kindPlatform') }), statusBadge] }), jsxs("div", { className: "flex items-center gap-2", children: [jsx(Button$1, { variant: "outline", size: "sm", onClick: () => (isHosted ? void onReverify(row) : void checkSpaDomain()), disabled: isBusy || status === 'checking' || (!isHosted && !row.registered_with_dns_pro), "aria-label": t('advancedCustomDomain.reverifyAriaLabel', { domain: row.custom_domain }), children: isBusy || status === 'checking' ? (jsx(LoaderCircle, { className: "h-4 w-4 animate-spin" })) : (jsxs(Fragment$1, { children: [jsx(RefreshCw, { className: "mr-1 h-4 w-4", "aria-hidden": "true" }), t('advancedCustomDomain.reverify')] })) }), jsx(Button$1, { variant: "outline", size: "sm", className: "text-red-600 hover:text-red-700", onClick: () => setConfirmOpen(true), disabled: isBusy, "aria-label": t('advancedCustomDomain.removeAriaLabel', { domain: row.custom_domain }), children: isHosted ? t('advancedCustomDomain.detach') : t('advancedCustomDomain.delete') })] })] }), sharedApp ? (jsxs("div", { className: "space-y-1 text-sm text-[#646464]", children: [jsxs("p", { children: [t('advancedCustomDomain.sharedDomainLine'), ' ', jsx("a", { href: `https://${row.custom_domain}`, target: "_blank", rel: "noreferrer", className: "underline", children: t('advancedCustomDomain.openSite', { domain: row.custom_domain }) })] }), jsx("p", { className: "text-xs", children: t('advancedCustomDomain.servesApp', { app: sharedApp.name }) })] })) : records.length > 0 ? (jsxs(Table, { className: "table-fixed", children: [jsx(TableHeader, { children: jsxs(TableRow, { children: [jsx(TableHead, { className: "w-[90px]", children: t('advancedCustomDomain.tableType') }), jsx(TableHead, { className: "w-1/3", children: t('advancedCustomDomain.tableNameHost') }), jsx(TableHead, { children: t('advancedCustomDomain.tableValue') })] }) }), jsx(TableBody, { children: records.map((record, index) => (jsxs(TableRow, { children: [jsx(TableCell, { className: "text-sm", children: record.type }), jsx(TableCell, { children: jsxs("div", { className: "flex items-center gap-2", children: [jsx("span", { className: "text-sm break-all", children: record.name }), jsx(Button$1, { type: "button", variant: "ghost", size: "icon", className: "h-6 w-6 shrink-0", onClick: () => handleCopy(record.name, `name-${index}`), "aria-label": t('advancedCustomDomain.copyNameHostAriaLabel', {
|
|
198588
198643
|
domain: record.name,
|
|
198589
198644
|
}), children: copiedKey === `name-${index}` ? (jsx(Check, { className: "h-4 w-4 text-blue-600", "aria-hidden": "true" })) : (jsx(Copy, { className: "h-4 w-4", "aria-hidden": "true" })) })] }) }), jsxs(TableCell, { children: [jsxs("div", { className: "flex items-center gap-2", children: [jsx("span", { className: "text-sm break-all", children: record.value }), jsx(Button$1, { type: "button", variant: "ghost", size: "icon", className: "h-6 w-6 shrink-0", onClick: () => handleCopy(record.value, `value-${index}`), "aria-label": t('advancedCustomDomain.copyValueAriaLabel'), children: copiedKey === `value-${index}` ? (jsx(Check, { className: "h-4 w-4 text-blue-600", "aria-hidden": "true" })) : (jsx(Copy, { className: "h-4 w-4", "aria-hidden": "true" })) })] }), record.reason && jsx("p", { className: "mt-1 text-xs text-[#646464]", children: record.reason })] })] }, `${record.type}-${record.name}-${record.value}`))) })] })) : (jsx("p", { className: "text-sm text-[#646464]", children: t('advancedCustomDomain.noRecordsYet') })), jsx(AlertDialog, { open: confirmOpen, onOpenChange: setConfirmOpen, children: jsxs(AlertDialogContent, { children: [jsxs(AlertDialogHeader, { children: [jsx(AlertDialogTitle, { children: t('advancedCustomDomain.confirmRemoveTitle') }), jsx(AlertDialogDescription, { children: isHosted
|
|
198590
198645
|
? t('advancedCustomDomain.confirmDetachBody', { domain: row.custom_domain })
|
|
@@ -198642,9 +198697,20 @@ const CustomDomainsContent = ({ platformKey, username, currentSPA, currentPlatfo
|
|
|
198642
198697
|
const { data, isLoading, isFetching, refetch } = useGetCustomDomainsQuery({
|
|
198643
198698
|
params: { platform_key: platformKey },
|
|
198644
198699
|
});
|
|
198645
|
-
// Only the caller's own deployed apps are listable
|
|
198646
|
-
//
|
|
198647
|
-
|
|
198700
|
+
// Only the caller's own deployed apps are listable. The Add dialog offers
|
|
198701
|
+
// them, and a row on the platform's shared domain names the app it serves
|
|
198702
|
+
// (`shared_domain` is that address), so the list is wanted from the start.
|
|
198703
|
+
const { data: projectData, isFetching: isLoadingProjects } = useGetHostingProjectsQuery({ org: platformKey, username }, { skip: !username });
|
|
198704
|
+
// The caller's app served at a shared-domain address, by that address. A
|
|
198705
|
+
// row without a match is an own domain (or another user's app): it keeps
|
|
198706
|
+
// its records.
|
|
198707
|
+
const sharedAppFor = (row) => {
|
|
198708
|
+
var _a;
|
|
198709
|
+
return row.spa === VERCEL_SPA
|
|
198710
|
+
? ((_a = projectData === null || projectData === void 0 ? void 0 : projectData.projects) !== null && _a !== void 0 ? _a : []).find((project) => !!project.shared_domain &&
|
|
198711
|
+
project.shared_domain.toLowerCase() === row.custom_domain.toLowerCase())
|
|
198712
|
+
: undefined;
|
|
198713
|
+
};
|
|
198648
198714
|
const [createCustomDomain, { isLoading: isCreating }] = useCreateCustomDomainMutation();
|
|
198649
198715
|
const [deleteCustomDomain] = useDeleteCustomDomainMutation();
|
|
198650
198716
|
const [createHostingProjectDomain, { isLoading: isAttaching }] = useCreateHostingProjectDomainMutation();
|
|
@@ -198673,10 +198739,15 @@ const CustomDomainsContent = ({ platformKey, username, currentSPA, currentPlatfo
|
|
|
198673
198739
|
: typeof window !== 'undefined'
|
|
198674
198740
|
? window.location.hostname.split('.').slice(-2).join('.')
|
|
198675
198741
|
: 'iblai.app'}`;
|
|
198676
|
-
//
|
|
198677
|
-
//
|
|
198678
|
-
|
|
198679
|
-
|
|
198742
|
+
// The Add dialog offers the sign-in kind whenever it knows which app to bind
|
|
198743
|
+
// the domain to. How many sign-in domains an app may have is the platform's
|
|
198744
|
+
// decision, not the panel's: a refusal comes back as the platform's own
|
|
198745
|
+
// message (`reportError` shows it), so no count is enforced here.
|
|
198746
|
+
const canAddPlatformDomain = Boolean(currentSPA);
|
|
198747
|
+
// The kind the dialog opens on: an app that already has a sign-in domain is
|
|
198748
|
+
// more often adding one for a deployed app; the sign-in kind stays one
|
|
198749
|
+
// click away either way.
|
|
198750
|
+
const hasPlatformDomain = useMemo(() => customDomains.some((domain) => { var _a; return domain.spa !== VERCEL_SPA && ((_a = domain.spa) === null || _a === void 0 ? void 0 : _a.toLowerCase().includes(currentSPA.toLowerCase())); }), [customDomains, currentSPA]);
|
|
198680
198751
|
/**
|
|
198681
198752
|
* The status decides what the user should do next, so it gets its own
|
|
198682
198753
|
* message. 409 shares 400's: both mean "not this domain, not here", and the
|
|
@@ -198701,10 +198772,10 @@ const CustomDomainsContent = ({ platformKey, username, currentSPA, currentPlatfo
|
|
|
198701
198772
|
};
|
|
198702
198773
|
const reportError = (error, fallback) => {
|
|
198703
198774
|
const status = error === null || error === void 0 ? void 0 : error.status;
|
|
198704
|
-
// The server's own words go underneath the headline
|
|
198705
|
-
//
|
|
198706
|
-
|
|
198707
|
-
toast.error(headlineForStatus(status, fallback), { description });
|
|
198775
|
+
// The server's own words go underneath the headline. A 409 body never
|
|
198776
|
+
// names the organization holding the domain — the backend keeps that out
|
|
198777
|
+
// on purpose — and for a taken subdomain it carries the way out.
|
|
198778
|
+
toast.error(headlineForStatus(status, fallback), { description: serverDetail(error) });
|
|
198708
198779
|
};
|
|
198709
198780
|
const handleAddPlatformDomain = async (domain) => {
|
|
198710
198781
|
if (!domain) {
|
|
@@ -198728,15 +198799,16 @@ const CustomDomainsContent = ({ platformKey, username, currentSPA, currentPlatfo
|
|
|
198728
198799
|
reportError(error, t('advancedCustomDomain.createError'));
|
|
198729
198800
|
}
|
|
198730
198801
|
};
|
|
198731
|
-
const handleAddAppDomain = async (project,
|
|
198802
|
+
const handleAddAppDomain = async (project, address) => {
|
|
198732
198803
|
try {
|
|
198733
|
-
//
|
|
198734
|
-
// reads
|
|
198804
|
+
// Only the address that was filled in is sent; the dialog guarantees
|
|
198805
|
+
// exactly one. The backend reads an absent key, never an empty string.
|
|
198735
198806
|
await createHostingProjectDomain({
|
|
198736
198807
|
org: platformKey,
|
|
198737
198808
|
username,
|
|
198738
198809
|
project,
|
|
198739
|
-
domain: domain || undefined,
|
|
198810
|
+
domain: address.domain || undefined,
|
|
198811
|
+
subdomain: address.subdomain || undefined,
|
|
198740
198812
|
}).unwrap();
|
|
198741
198813
|
await refetch();
|
|
198742
198814
|
toast.success(t('advancedCustomDomain.createSuccess'));
|
|
@@ -198801,7 +198873,7 @@ const CustomDomainsContent = ({ platformKey, username, currentSPA, currentPlatfo
|
|
|
198801
198873
|
setIsModalOpen(true);
|
|
198802
198874
|
}, children: [jsx(Plus, { className: "h-4 w-4", "aria-hidden": "true" }), t('advancedCustomDomain.add')] }), jsx("button", { onClick: () => setIsCollapsed((previous) => !previous), className: "rounded p-1 hover:bg-gray-100 focus:ring-2 focus:ring-blue-500 focus:ring-offset-1 focus:outline-none dark:hover:bg-gray-800", "aria-label": isCollapsed
|
|
198803
198875
|
? t('advancedCustomDomain.expandAriaLabel')
|
|
198804
|
-
: t('advancedCustomDomain.collapseAriaLabel'), type: "button", children: isCollapsed ? (jsx(ChevronDown, { className: "h-5 w-5 text-gray-400", "aria-hidden": "true" })) : (jsx(ChevronUp, { className: "h-5 w-5 text-gray-400", "aria-hidden": "true" })) })] })] }), !isCollapsed && (jsx("div", { className: "mt-4", children: isLoading ? (jsx("div", { className: "p-4 text-gray-500", children: t('advancedCustomDomain.loading') })) : customDomains.length > 0 ? (jsx("div", { className: "space-y-3", children: sortedDomains.map((row) => (jsx(DomainRow, { row: row, sslValue: sslValue, onReverify: handleReverify, onRemove: handleRemove, isBusy: busyDomainId === row.id, onStatusChange: handleStatusChange }, row.id))) })) : (jsx("div", { className: "p-4 text-center text-sm text-gray-500", children: t('advancedCustomDomain.noDomainConfigured') })) })), jsx(AddDomainDialog, { open: isModalOpen, onOpenChange: setIsModalOpen, canAddPlatformDomain: canAddPlatformDomain, projects: (_a = projectData === null || projectData === void 0 ? void 0 : projectData.projects) !== null && _a !== void 0 ? _a : [], isLoadingProjects: isLoadingProjects, isSubmitting: isCreating || isAttaching, onSubmitPlatformDomain: handleAddPlatformDomain, onSubmitAppDomain: handleAddAppDomain })] }));
|
|
198876
|
+
: t('advancedCustomDomain.collapseAriaLabel'), type: "button", children: isCollapsed ? (jsx(ChevronDown, { className: "h-5 w-5 text-gray-400", "aria-hidden": "true" })) : (jsx(ChevronUp, { className: "h-5 w-5 text-gray-400", "aria-hidden": "true" })) })] })] }), !isCollapsed && (jsx("div", { className: "mt-4", children: isLoading ? (jsx("div", { className: "p-4 text-gray-500", children: t('advancedCustomDomain.loading') })) : customDomains.length > 0 ? (jsx("div", { className: "space-y-3", children: sortedDomains.map((row) => (jsx(DomainRow, { row: row, sharedApp: sharedAppFor(row), sslValue: sslValue, onReverify: handleReverify, onRemove: handleRemove, isBusy: busyDomainId === row.id, onStatusChange: handleStatusChange }, row.id))) })) : (jsx("div", { className: "p-4 text-center text-sm text-gray-500", children: t('advancedCustomDomain.noDomainConfigured') })) })), jsx(AddDomainDialog, { open: isModalOpen, onOpenChange: setIsModalOpen, canAddPlatformDomain: canAddPlatformDomain, defaultMode: hasPlatformDomain ? 'app' : 'platform', projects: (_a = projectData === null || projectData === void 0 ? void 0 : projectData.projects) !== null && _a !== void 0 ? _a : [], isLoadingProjects: isLoadingProjects, isSubmitting: isCreating || isAttaching, onSubmitPlatformDomain: handleAddPlatformDomain, onSubmitAppDomain: handleAddAppDomain })] }));
|
|
198805
198877
|
};
|
|
198806
198878
|
|
|
198807
198879
|
const StudentMentorCreationContent = ({ platformKey, }) => {
|
|
@@ -229871,7 +229943,9 @@ function SummerNoteTextEditor({ value, onChange, exportFormat = 'markdown', disa
|
|
|
229871
229943
|
isApplyingValueRef.current = false;
|
|
229872
229944
|
}
|
|
229873
229945
|
}, [value, isReady, toEditorHtml]);
|
|
229874
|
-
|
|
229946
|
+
// A layout effect: the loading node leaves in this same commit, so the editor
|
|
229947
|
+
// is never painted editable while disabled. The test helper waits on that node.
|
|
229948
|
+
React__default.useLayoutEffect(() => {
|
|
229875
229949
|
const instance = instanceRef.current;
|
|
229876
229950
|
if (!instance || !isReady) {
|
|
229877
229951
|
return;
|
|
@@ -229899,8 +229973,9 @@ function SummerNoteTextEditor({ value, onChange, exportFormat = 'markdown', disa
|
|
|
229899
229973
|
editable.style.maxHeight = `${Math.max(EDITOR_MAX_HEIGHT, minHeightPx)}px`;
|
|
229900
229974
|
}, [minHeightPx, isReady]);
|
|
229901
229975
|
// Summernote's editable region is a plain contenteditable div; describe it the
|
|
229902
|
-
// same way `RichTextEditor` describes its own.
|
|
229903
|
-
|
|
229976
|
+
// same way `RichTextEditor` describes its own. A layout effect, so the region
|
|
229977
|
+
// is never painted without its name.
|
|
229978
|
+
React__default.useLayoutEffect(() => {
|
|
229904
229979
|
const editable = editableRef.current;
|
|
229905
229980
|
if (!editable || !isReady) {
|
|
229906
229981
|
return;
|
|
@@ -96359,7 +96359,11 @@ var advancedCustomDomain$3 = {
|
|
|
96359
96359
|
noProjects: "You have not deployed any apps yet. Deploy one first, then come back to give it a domain.",
|
|
96360
96360
|
customDomainLabel: "Domain",
|
|
96361
96361
|
domainHelpPlatform: "Without the scheme or a leading “www.” — for example, app.example.com.",
|
|
96362
|
-
domainHelpApp: "
|
|
96362
|
+
domainHelpApp: "A domain you own, without the scheme or a leading “www.” — for example, app.example.com. Leave it empty to use the subdomain above.",
|
|
96363
|
+
subdomainLabel: "Subdomain",
|
|
96364
|
+
subdomainHelp: "Lowercase letters, digits and hyphens, like a username. The app is served at <subdomain>.<the platform’s shared domain>, with no DNS records to set.",
|
|
96365
|
+
oneOfSubdomainOrDomain: "Fill in either the subdomain or a domain you own, not both.",
|
|
96366
|
+
subdomainInvalid: "A subdomain is 1–63 lowercase letters, digits and hyphens, and cannot start or end with a hyphen.",
|
|
96363
96367
|
cancel: "Cancel",
|
|
96364
96368
|
save: "Save",
|
|
96365
96369
|
expandAriaLabel: "Expand domains",
|
|
@@ -96381,6 +96385,9 @@ var advancedCustomDomain$3 = {
|
|
|
96381
96385
|
copyNameHostAriaLabel: "Copy name/host: {domain}",
|
|
96382
96386
|
copyValueAriaLabel: "Copy value/target",
|
|
96383
96387
|
noRecordsYet: "No records yet. Check the domain again to fetch them.",
|
|
96388
|
+
sharedDomainLine: "Runs on our shared domain — nothing to set up.",
|
|
96389
|
+
openSite: "Open {domain}",
|
|
96390
|
+
servesApp: "Serves {app}",
|
|
96384
96391
|
confirmRemoveTitle: "Remove this domain?",
|
|
96385
96392
|
confirmDeleteBody: "{domain} will stop signing users in to this organization.",
|
|
96386
96393
|
confirmDetachBody: "{domain} will stop serving the app, here and at the hosting provider.",
|
|
@@ -103904,7 +103911,11 @@ var advancedCustomDomain$2 = {
|
|
|
103904
103911
|
noProjects: "Vous n’avez encore déployé aucune application. Déployez-en une, puis revenez lui donner un domaine.",
|
|
103905
103912
|
customDomainLabel: "Domaine",
|
|
103906
103913
|
domainHelpPlatform: "Sans le schéma ni un « www. » en tête — par exemple, app.example.com.",
|
|
103907
|
-
domainHelpApp: "
|
|
103914
|
+
domainHelpApp: "Un domaine qui vous appartient, sans le schéma ni un « www. » en tête — par exemple, app.example.com. Laissez ce champ vide pour utiliser le sous-domaine ci-dessus.",
|
|
103915
|
+
subdomainLabel: "Sous-domaine",
|
|
103916
|
+
subdomainHelp: "Lettres minuscules, chiffres et tirets, comme un nom d’utilisateur. L’application est servie à <sous-domaine>.<le domaine partagé de la plateforme>, sans aucun enregistrement DNS à créer.",
|
|
103917
|
+
oneOfSubdomainOrDomain: "Renseignez soit le sous-domaine, soit un domaine qui vous appartient, pas les deux.",
|
|
103918
|
+
subdomainInvalid: "Un sous-domaine compte de 1 à 63 lettres minuscules, chiffres et tirets, et ne peut ni commencer ni finir par un tiret.",
|
|
103908
103919
|
cancel: "Annuler",
|
|
103909
103920
|
save: "Enregistrer",
|
|
103910
103921
|
expandAriaLabel: "Déplier les domaines",
|
|
@@ -103926,6 +103937,9 @@ var advancedCustomDomain$2 = {
|
|
|
103926
103937
|
copyNameHostAriaLabel: "Copier le nom/hôte : {domain}",
|
|
103927
103938
|
copyValueAriaLabel: "Copier la valeur/cible",
|
|
103928
103939
|
noRecordsYet: "Aucun enregistrement pour l’instant. Vérifiez le domaine à nouveau pour les obtenir.",
|
|
103940
|
+
sharedDomainLine: "Fonctionne sur notre domaine partagé — rien à configurer.",
|
|
103941
|
+
openSite: "Ouvrir {domain}",
|
|
103942
|
+
servesApp: "Dessert {app}",
|
|
103929
103943
|
confirmRemoveTitle: "Supprimer ce domaine ?",
|
|
103930
103944
|
confirmDeleteBody: "{domain} ne permettra plus de se connecter à cette organisation.",
|
|
103931
103945
|
confirmDetachBody: "{domain} cessera de servir l’application, ici comme chez l’hébergeur.",
|
|
@@ -111449,7 +111463,11 @@ var advancedCustomDomain$1 = {
|
|
|
111449
111463
|
noProjects: "Todavía no has desplegado ninguna aplicación. Despliega una primero y vuelve para darle un dominio.",
|
|
111450
111464
|
customDomainLabel: "Dominio",
|
|
111451
111465
|
domainHelpPlatform: "Sin el esquema ni un “www.” inicial — por ejemplo, app.example.com.",
|
|
111452
|
-
domainHelpApp: "
|
|
111466
|
+
domainHelpApp: "Un dominio tuyo, sin el esquema ni un “www.” inicial — por ejemplo, app.example.com. Déjalo vacío para usar el subdominio de arriba.",
|
|
111467
|
+
subdomainLabel: "Subdominio",
|
|
111468
|
+
subdomainHelp: "Letras minúsculas, dígitos y guiones, como un nombre de usuario. La aplicación se sirve en <subdominio>.<el dominio compartido de la plataforma>, sin registros DNS que configurar.",
|
|
111469
|
+
oneOfSubdomainOrDomain: "Rellena el subdominio o un dominio tuyo, no ambos.",
|
|
111470
|
+
subdomainInvalid: "Un subdominio tiene entre 1 y 63 letras minúsculas, dígitos y guiones, y no puede empezar ni terminar con un guion.",
|
|
111453
111471
|
cancel: "Cancelar",
|
|
111454
111472
|
save: "Guardar",
|
|
111455
111473
|
expandAriaLabel: "Expandir dominios",
|
|
@@ -111471,6 +111489,9 @@ var advancedCustomDomain$1 = {
|
|
|
111471
111489
|
copyNameHostAriaLabel: "Copiar nombre/host: {domain}",
|
|
111472
111490
|
copyValueAriaLabel: "Copiar valor/destino",
|
|
111473
111491
|
noRecordsYet: "Aún no hay registros. Comprueba el dominio de nuevo para obtenerlos.",
|
|
111492
|
+
sharedDomainLine: "Funciona en nuestro dominio compartido — no hay nada que configurar.",
|
|
111493
|
+
openSite: "Abrir {domain}",
|
|
111494
|
+
servesApp: "Sirve {app}",
|
|
111474
111495
|
confirmRemoveTitle: "¿Eliminar este dominio?",
|
|
111475
111496
|
confirmDeleteBody: "{domain} dejará de permitir el inicio de sesión en esta organización.",
|
|
111476
111497
|
confirmDetachBody: "{domain} dejará de servir la aplicación, aquí y en el proveedor de alojamiento.",
|
|
@@ -118994,7 +119015,11 @@ var advancedCustomDomain = {
|
|
|
118994
119015
|
noProjects: "你还没有部署任何应用。请先部署一个,然后回来为它设置域名。",
|
|
118995
119016
|
customDomainLabel: "域名",
|
|
118996
119017
|
domainHelpPlatform: "不要带协议前缀或开头的“www.”,例如 app.example.com。",
|
|
118997
|
-
domainHelpApp: "
|
|
119018
|
+
domainHelpApp: "你拥有的域名,不要带协议前缀或开头的“www.”,例如 app.example.com。留空则使用上方的子域名。",
|
|
119019
|
+
subdomainLabel: "子域名",
|
|
119020
|
+
subdomainHelp: "小写字母、数字和连字符,像用户名一样。应用将通过 <子域名>.<平台的共享域名> 提供服务,无需设置任何 DNS 记录。",
|
|
119021
|
+
oneOfSubdomainOrDomain: "请填写子域名或你拥有的域名,二者只填其一。",
|
|
119022
|
+
subdomainInvalid: "子域名为 1 到 63 个小写字母、数字和连字符,且不能以连字符开头或结尾。",
|
|
118998
119023
|
cancel: "取消",
|
|
118999
119024
|
save: "保存",
|
|
119000
119025
|
expandAriaLabel: "展开域名",
|
|
@@ -119016,6 +119041,9 @@ var advancedCustomDomain = {
|
|
|
119016
119041
|
copyNameHostAriaLabel: "复制名称/主机:{domain}",
|
|
119017
119042
|
copyValueAriaLabel: "复制值/目标",
|
|
119018
119043
|
noRecordsYet: "暂无记录。请重新检查该域名以获取记录。",
|
|
119044
|
+
sharedDomainLine: "运行在我们的共享域名上——无需任何设置。",
|
|
119045
|
+
openSite: "打开 {domain}",
|
|
119046
|
+
servesApp: "服务于 {app}",
|
|
119019
119047
|
confirmRemoveTitle: "要移除这个域名吗?",
|
|
119020
119048
|
confirmDeleteBody: "{domain} 将不再用于登录该组织。",
|
|
119021
119049
|
confirmDetachBody: "{domain} 将不再为该应用提供服务,本处和托管服务商处都会解除。",
|
|
@@ -230705,15 +230733,23 @@ const sanitizeDomain = (domain) => domain
|
|
|
230705
230733
|
.replace(/^https?:\/\//, '')
|
|
230706
230734
|
.replace(/\/.*$/, '')
|
|
230707
230735
|
.replace(/^www\./, '');
|
|
230708
|
-
|
|
230736
|
+
/** One label under the platform's shared domain — the backend's own rule. */
|
|
230737
|
+
const SUBDOMAIN_RE = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
|
|
230738
|
+
/** Typed like a username: only case and surrounding whitespace are forgiven. */
|
|
230739
|
+
const sanitizeSubdomain = (subdomain) => subdomain.trim().toLowerCase();
|
|
230740
|
+
const AddDomainDialog = ({ open, onOpenChange, canAddPlatformDomain, defaultMode = 'platform', projects, isLoadingProjects, isSubmitting, onSubmitPlatformDomain, onSubmitAppDomain, }) => {
|
|
230741
|
+
var _a;
|
|
230709
230742
|
const t = useT();
|
|
230710
|
-
const
|
|
230743
|
+
const initialMode = canAddPlatformDomain ? defaultMode : 'app';
|
|
230744
|
+
const [mode, setMode] = useState(initialMode);
|
|
230711
230745
|
const [domainValue, setDomainValue] = useState('');
|
|
230746
|
+
const [subdomainValue, setSubdomainValue] = useState('');
|
|
230712
230747
|
const [projectId, setProjectId] = useState('');
|
|
230713
230748
|
const reset = () => {
|
|
230714
230749
|
setDomainValue('');
|
|
230750
|
+
setSubdomainValue('');
|
|
230715
230751
|
setProjectId('');
|
|
230716
|
-
setMode(
|
|
230752
|
+
setMode(initialMode);
|
|
230717
230753
|
};
|
|
230718
230754
|
const close = () => {
|
|
230719
230755
|
reset();
|
|
@@ -230725,10 +230761,27 @@ const AddDomainDialog = ({ open, onOpenChange, canAddPlatformDomain, projects, i
|
|
|
230725
230761
|
await onSubmitPlatformDomain(sanitizeDomain(domainValue));
|
|
230726
230762
|
}
|
|
230727
230763
|
else {
|
|
230728
|
-
await onSubmitAppDomain(Number(projectId),
|
|
230764
|
+
await onSubmitAppDomain(Number(projectId), {
|
|
230765
|
+
domain: sanitizeDomain(domainValue),
|
|
230766
|
+
subdomain: sanitizeSubdomain(subdomainValue),
|
|
230767
|
+
});
|
|
230729
230768
|
}
|
|
230730
230769
|
};
|
|
230731
|
-
const
|
|
230770
|
+
const hasDomain = domainValue.trim().length > 0;
|
|
230771
|
+
const subdomain = sanitizeSubdomain(subdomainValue);
|
|
230772
|
+
const hasSubdomain = subdomain.length > 0;
|
|
230773
|
+
// An app is served at exactly one address, and a subdomain has to be one the
|
|
230774
|
+
// backend will take — say which rule is unmet, and only once something was
|
|
230775
|
+
// typed, so an untouched form stays quiet.
|
|
230776
|
+
const appAddressProblem = mode === 'app' && hasDomain && hasSubdomain
|
|
230777
|
+
? 'both'
|
|
230778
|
+
: mode === 'app' && hasSubdomain && !SUBDOMAIN_RE.test(subdomain)
|
|
230779
|
+
? 'invalid'
|
|
230780
|
+
: null;
|
|
230781
|
+
const canSubmit = mode === 'platform'
|
|
230782
|
+
? hasDomain
|
|
230783
|
+
: projectId.trim().length > 0 && (hasDomain || hasSubdomain) && appAddressProblem === null;
|
|
230784
|
+
const selectedProject = projects.find((project) => String(project.id) === projectId);
|
|
230732
230785
|
return (jsx(Dialog, { open: open, onOpenChange: (next) => {
|
|
230733
230786
|
if (!next) {
|
|
230734
230787
|
reset();
|
|
@@ -230737,9 +230790,11 @@ const AddDomainDialog = ({ open, onOpenChange, canAddPlatformDomain, projects, i
|
|
|
230737
230790
|
}, children: jsxs(DialogContent, { className: "sm:max-w-[480px]", children: [jsxs(DialogHeader, { children: [jsx(DialogTitle, { children: t('advancedCustomDomain.dialogTitle') }), jsx(DialogDescription, { children: t('advancedCustomDomain.dialogDescription') })] }), jsxs("form", { onSubmit: handleSubmit, className: "space-y-4", children: [jsxs("div", { className: "space-y-2", children: [jsx("label", { htmlFor: "add-domain-kind", className: "text-sm", children: t('advancedCustomDomain.kindLabel') }), jsxs(Select$1, { value: mode, onValueChange: (next) => setMode(next), children: [jsx(SelectTrigger, { id: "add-domain-kind", children: jsx(SelectValue, {}) }), jsxs(SelectContent, { children: [canAddPlatformDomain && (jsx(SelectItem, { value: "platform", children: t('advancedCustomDomain.kindPlatform') })), jsx(SelectItem, { value: "app", children: t('advancedCustomDomain.kindApp') })] })] })] }), mode === 'app' && (jsxs("div", { className: "space-y-2", children: [jsx("label", { htmlFor: "add-domain-project", className: "text-sm", children: t('advancedCustomDomain.projectLabel') }), isLoadingProjects ? (jsx("p", { className: "text-sm text-[#646464]", children: t('advancedCustomDomain.loading') })) : projects.length === 0 ? (
|
|
230738
230791
|
// The backend lists only the caller's own projects, so this is
|
|
230739
230792
|
// "you have not deployed anything", not "the platform has not".
|
|
230740
|
-
jsx("p", { className: "text-sm text-[#646464]", children: t('advancedCustomDomain.noProjects') })) : (jsxs(Select$1, { value: projectId, onValueChange: setProjectId, children: [jsx(SelectTrigger, { id: "add-domain-project", children: jsx(SelectValue, { placeholder: t('advancedCustomDomain.projectPlaceholder') }) }), jsx(SelectContent, { children: projects.map((project) => (jsx(SelectItem, { value: String(project.id), children: project.name }, project.id))) })] }))] })), jsxs("div", { className: "space-y-2", children: [jsx("label", { htmlFor: "add-domain-
|
|
230793
|
+
jsx("p", { className: "text-sm text-[#646464]", children: t('advancedCustomDomain.noProjects') })) : (jsxs(Select$1, { value: projectId, onValueChange: setProjectId, children: [jsx(SelectTrigger, { id: "add-domain-project", children: jsx(SelectValue, { placeholder: t('advancedCustomDomain.projectPlaceholder') }) }), jsx(SelectContent, { children: projects.map((project) => (jsx(SelectItem, { value: String(project.id), children: project.name }, project.id))) })] }))] })), mode === 'app' && (jsxs("div", { className: "space-y-2", children: [jsx("label", { htmlFor: "add-domain-subdomain", className: "text-sm", children: t('advancedCustomDomain.subdomainLabel') }), jsx(Input, { id: "add-domain-subdomain", value: subdomainValue, onChange: (event) => setSubdomainValue(event.target.value),
|
|
230794
|
+
// The app's own name is the suggestion the deploy skill makes.
|
|
230795
|
+
placeholder: (_a = selectedProject === null || selectedProject === void 0 ? void 0 : selectedProject.name) !== null && _a !== void 0 ? _a : '', className: "w-full" }), jsx("p", { className: "text-xs text-[#646464]", children: t('advancedCustomDomain.subdomainHelp') })] })), jsxs("div", { className: "space-y-2", children: [jsx("label", { htmlFor: "add-domain-name", className: "text-sm", children: t('advancedCustomDomain.customDomainLabel') }), jsx(Input, { id: "add-domain-name", value: domainValue, onChange: (event) => setDomainValue(event.target.value), placeholder: "example.com", className: "w-full", required: mode === 'platform' }), jsx("p", { className: "text-xs text-[#646464]", children: mode === 'app'
|
|
230741
230796
|
? t('advancedCustomDomain.domainHelpApp')
|
|
230742
|
-
: t('advancedCustomDomain.domainHelpPlatform') })] }), jsxs("div", { className: "flex justify-end gap-2", children: [jsx(Button$1, { type: "button", variant: "outline", onClick: close, children: t('advancedCustomDomain.cancel') }), jsxs(Button$1, { type: "submit", disabled: isSubmitting || !canSubmit, children: [isSubmitting && jsx(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), t('advancedCustomDomain.save')] })] })] })] }) }));
|
|
230797
|
+
: t('advancedCustomDomain.domainHelpPlatform') }), appAddressProblem === 'both' && (jsx("p", { className: "text-xs text-[#646464]", role: "status", children: t('advancedCustomDomain.oneOfSubdomainOrDomain') })), appAddressProblem === 'invalid' && (jsx("p", { className: "text-xs text-[#646464]", role: "status", children: t('advancedCustomDomain.subdomainInvalid') }))] }), jsxs("div", { className: "flex justify-end gap-2", children: [jsx(Button$1, { type: "button", variant: "outline", onClick: close, children: t('advancedCustomDomain.cancel') }), jsxs(Button$1, { type: "submit", disabled: isSubmitting || !canSubmit, children: [isSubmitting && jsx(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), t('advancedCustomDomain.save')] })] })] })] }) }));
|
|
230743
230798
|
};
|
|
230744
230799
|
|
|
230745
230800
|
var SLOTTABLE_IDENTIFIER = Symbol("radix.slottable");
|
|
@@ -230931,7 +230986,7 @@ const resolveCname = async (domain, target) => {
|
|
|
230931
230986
|
const body = await response.json();
|
|
230932
230987
|
return Boolean((_a = body === null || body === void 0 ? void 0 : body.Answer) === null || _a === void 0 ? void 0 : _a.some((answer) => { var _a; return ((_a = answer.data) !== null && _a !== void 0 ? _a : '').replace(/\.$/, '') === target; }));
|
|
230933
230988
|
};
|
|
230934
|
-
const DomainRow = ({ row, sslValue, onReverify, onRemove, isBusy, onStatusChange, }) => {
|
|
230989
|
+
const DomainRow = ({ row, sharedApp, sslValue, onReverify, onRemove, isBusy, onStatusChange, }) => {
|
|
230935
230990
|
var _a;
|
|
230936
230991
|
const t = useT();
|
|
230937
230992
|
const isHosted = row.spa === VERCEL_SPA;
|
|
@@ -231003,7 +231058,7 @@ const DomainRow = ({ row, sslValue, onReverify, onRemove, isBusy, onStatusChange
|
|
|
231003
231058
|
const statusBadge = (jsx("span", { className: status === 'verified'
|
|
231004
231059
|
? 'rounded bg-green-100 px-2 py-1 text-xs text-green-800'
|
|
231005
231060
|
: 'rounded bg-gray-100 px-2 py-1 text-xs text-gray-600', children: status === 'checking' ? (jsxs("span", { className: "flex items-center gap-1", children: [jsx(LoaderCircle, { className: "h-3 w-3 animate-spin" }), t('advancedCustomDomain.verifying')] })) : (statusLabel()) }));
|
|
231006
|
-
return (jsxs("div", { className: "space-y-3 rounded-lg border border-gray-200 p-3 dark:border-gray-700", children: [jsxs("div", { className: "flex flex-wrap items-center justify-between gap-2", children: [jsxs("div", { className: "flex items-center gap-2", children: [jsx("span", { className: "text-sm", children: row.custom_domain }), jsx("span", { className: "text-xs text-[#646464]", children: isHosted ? t('advancedCustomDomain.kindApp') : t('advancedCustomDomain.kindPlatform') }), statusBadge] }), jsxs("div", { className: "flex items-center gap-2", children: [jsx(Button$1, { variant: "outline", size: "sm", onClick: () => (isHosted ? void onReverify(row) : void checkSpaDomain()), disabled: isBusy || status === 'checking' || (!isHosted && !row.registered_with_dns_pro), "aria-label": t('advancedCustomDomain.reverifyAriaLabel', { domain: row.custom_domain }), children: isBusy || status === 'checking' ? (jsx(LoaderCircle, { className: "h-4 w-4 animate-spin" })) : (jsxs(Fragment$1, { children: [jsx(RefreshCw, { className: "mr-1 h-4 w-4", "aria-hidden": "true" }), t('advancedCustomDomain.reverify')] })) }), jsx(Button$1, { variant: "outline", size: "sm", className: "text-red-600 hover:text-red-700", onClick: () => setConfirmOpen(true), disabled: isBusy, "aria-label": t('advancedCustomDomain.removeAriaLabel', { domain: row.custom_domain }), children: isHosted ? t('advancedCustomDomain.detach') : t('advancedCustomDomain.delete') })] })] }), records.length > 0 ? (jsxs(Table$1, { className: "table-fixed", children: [jsx(TableHeader$1, { children: jsxs(TableRow$1, { children: [jsx(TableHead, { className: "w-[90px]", children: t('advancedCustomDomain.tableType') }), jsx(TableHead, { className: "w-1/3", children: t('advancedCustomDomain.tableNameHost') }), jsx(TableHead, { children: t('advancedCustomDomain.tableValue') })] }) }), jsx(TableBody, { children: records.map((record, index) => (jsxs(TableRow$1, { children: [jsx(TableCell$1, { className: "text-sm", children: record.type }), jsx(TableCell$1, { children: jsxs("div", { className: "flex items-center gap-2", children: [jsx("span", { className: "text-sm break-all", children: record.name }), jsx(Button$1, { type: "button", variant: "ghost", size: "icon", className: "h-6 w-6 shrink-0", onClick: () => handleCopy(record.name, `name-${index}`), "aria-label": t('advancedCustomDomain.copyNameHostAriaLabel', {
|
|
231061
|
+
return (jsxs("div", { className: "space-y-3 rounded-lg border border-gray-200 p-3 dark:border-gray-700", children: [jsxs("div", { className: "flex flex-wrap items-center justify-between gap-2", children: [jsxs("div", { className: "flex items-center gap-2", children: [jsx("span", { className: "text-sm", children: row.custom_domain }), jsx("span", { className: "text-xs text-[#646464]", children: isHosted ? t('advancedCustomDomain.kindApp') : t('advancedCustomDomain.kindPlatform') }), statusBadge] }), jsxs("div", { className: "flex items-center gap-2", children: [jsx(Button$1, { variant: "outline", size: "sm", onClick: () => (isHosted ? void onReverify(row) : void checkSpaDomain()), disabled: isBusy || status === 'checking' || (!isHosted && !row.registered_with_dns_pro), "aria-label": t('advancedCustomDomain.reverifyAriaLabel', { domain: row.custom_domain }), children: isBusy || status === 'checking' ? (jsx(LoaderCircle, { className: "h-4 w-4 animate-spin" })) : (jsxs(Fragment$1, { children: [jsx(RefreshCw, { className: "mr-1 h-4 w-4", "aria-hidden": "true" }), t('advancedCustomDomain.reverify')] })) }), jsx(Button$1, { variant: "outline", size: "sm", className: "text-red-600 hover:text-red-700", onClick: () => setConfirmOpen(true), disabled: isBusy, "aria-label": t('advancedCustomDomain.removeAriaLabel', { domain: row.custom_domain }), children: isHosted ? t('advancedCustomDomain.detach') : t('advancedCustomDomain.delete') })] })] }), sharedApp ? (jsxs("div", { className: "space-y-1 text-sm text-[#646464]", children: [jsxs("p", { children: [t('advancedCustomDomain.sharedDomainLine'), ' ', jsx("a", { href: `https://${row.custom_domain}`, target: "_blank", rel: "noreferrer", className: "underline", children: t('advancedCustomDomain.openSite', { domain: row.custom_domain }) })] }), jsx("p", { className: "text-xs", children: t('advancedCustomDomain.servesApp', { app: sharedApp.name }) })] })) : records.length > 0 ? (jsxs(Table$1, { className: "table-fixed", children: [jsx(TableHeader$1, { children: jsxs(TableRow$1, { children: [jsx(TableHead, { className: "w-[90px]", children: t('advancedCustomDomain.tableType') }), jsx(TableHead, { className: "w-1/3", children: t('advancedCustomDomain.tableNameHost') }), jsx(TableHead, { children: t('advancedCustomDomain.tableValue') })] }) }), jsx(TableBody, { children: records.map((record, index) => (jsxs(TableRow$1, { children: [jsx(TableCell$1, { className: "text-sm", children: record.type }), jsx(TableCell$1, { children: jsxs("div", { className: "flex items-center gap-2", children: [jsx("span", { className: "text-sm break-all", children: record.name }), jsx(Button$1, { type: "button", variant: "ghost", size: "icon", className: "h-6 w-6 shrink-0", onClick: () => handleCopy(record.name, `name-${index}`), "aria-label": t('advancedCustomDomain.copyNameHostAriaLabel', {
|
|
231007
231062
|
domain: record.name,
|
|
231008
231063
|
}), children: copiedKey === `name-${index}` ? (jsx(Check, { className: "h-4 w-4 text-blue-600", "aria-hidden": "true" })) : (jsx(Copy, { className: "h-4 w-4", "aria-hidden": "true" })) })] }) }), jsxs(TableCell$1, { children: [jsxs("div", { className: "flex items-center gap-2", children: [jsx("span", { className: "text-sm break-all", children: record.value }), jsx(Button$1, { type: "button", variant: "ghost", size: "icon", className: "h-6 w-6 shrink-0", onClick: () => handleCopy(record.value, `value-${index}`), "aria-label": t('advancedCustomDomain.copyValueAriaLabel'), children: copiedKey === `value-${index}` ? (jsx(Check, { className: "h-4 w-4 text-blue-600", "aria-hidden": "true" })) : (jsx(Copy, { className: "h-4 w-4", "aria-hidden": "true" })) })] }), record.reason && jsx("p", { className: "mt-1 text-xs text-[#646464]", children: record.reason })] })] }, `${record.type}-${record.name}-${record.value}`))) })] })) : (jsx("p", { className: "text-sm text-[#646464]", children: t('advancedCustomDomain.noRecordsYet') })), jsx(AlertDialog, { open: confirmOpen, onOpenChange: setConfirmOpen, children: jsxs(AlertDialogContent, { children: [jsxs(AlertDialogHeader, { children: [jsx(AlertDialogTitle, { children: t('advancedCustomDomain.confirmRemoveTitle') }), jsx(AlertDialogDescription, { children: isHosted
|
|
231009
231064
|
? t('advancedCustomDomain.confirmDetachBody', { domain: row.custom_domain })
|
|
@@ -231061,9 +231116,20 @@ const CustomDomainsContent = ({ platformKey, username, currentSPA, currentPlatfo
|
|
|
231061
231116
|
const { data, isLoading, isFetching, refetch } = useGetCustomDomainsQuery({
|
|
231062
231117
|
params: { platform_key: platformKey },
|
|
231063
231118
|
});
|
|
231064
|
-
// Only the caller's own deployed apps are listable
|
|
231065
|
-
//
|
|
231066
|
-
|
|
231119
|
+
// Only the caller's own deployed apps are listable. The Add dialog offers
|
|
231120
|
+
// them, and a row on the platform's shared domain names the app it serves
|
|
231121
|
+
// (`shared_domain` is that address), so the list is wanted from the start.
|
|
231122
|
+
const { data: projectData, isFetching: isLoadingProjects } = useGetHostingProjectsQuery({ org: platformKey, username }, { skip: !username });
|
|
231123
|
+
// The caller's app served at a shared-domain address, by that address. A
|
|
231124
|
+
// row without a match is an own domain (or another user's app): it keeps
|
|
231125
|
+
// its records.
|
|
231126
|
+
const sharedAppFor = (row) => {
|
|
231127
|
+
var _a;
|
|
231128
|
+
return row.spa === VERCEL_SPA
|
|
231129
|
+
? ((_a = projectData === null || projectData === void 0 ? void 0 : projectData.projects) !== null && _a !== void 0 ? _a : []).find((project) => !!project.shared_domain &&
|
|
231130
|
+
project.shared_domain.toLowerCase() === row.custom_domain.toLowerCase())
|
|
231131
|
+
: undefined;
|
|
231132
|
+
};
|
|
231067
231133
|
const [createCustomDomain, { isLoading: isCreating }] = useCreateCustomDomainMutation();
|
|
231068
231134
|
const [deleteCustomDomain] = useDeleteCustomDomainMutation();
|
|
231069
231135
|
const [createHostingProjectDomain, { isLoading: isAttaching }] = useCreateHostingProjectDomainMutation();
|
|
@@ -231092,10 +231158,15 @@ const CustomDomainsContent = ({ platformKey, username, currentSPA, currentPlatfo
|
|
|
231092
231158
|
: typeof window !== 'undefined'
|
|
231093
231159
|
? window.location.hostname.split('.').slice(-2).join('.')
|
|
231094
231160
|
: 'iblai.app'}`;
|
|
231095
|
-
//
|
|
231096
|
-
//
|
|
231097
|
-
|
|
231098
|
-
|
|
231161
|
+
// The Add dialog offers the sign-in kind whenever it knows which app to bind
|
|
231162
|
+
// the domain to. How many sign-in domains an app may have is the platform's
|
|
231163
|
+
// decision, not the panel's: a refusal comes back as the platform's own
|
|
231164
|
+
// message (`reportError` shows it), so no count is enforced here.
|
|
231165
|
+
const canAddPlatformDomain = Boolean(currentSPA);
|
|
231166
|
+
// The kind the dialog opens on: an app that already has a sign-in domain is
|
|
231167
|
+
// more often adding one for a deployed app; the sign-in kind stays one
|
|
231168
|
+
// click away either way.
|
|
231169
|
+
const hasPlatformDomain = useMemo(() => customDomains.some((domain) => { var _a; return domain.spa !== VERCEL_SPA && ((_a = domain.spa) === null || _a === void 0 ? void 0 : _a.toLowerCase().includes(currentSPA.toLowerCase())); }), [customDomains, currentSPA]);
|
|
231099
231170
|
/**
|
|
231100
231171
|
* The status decides what the user should do next, so it gets its own
|
|
231101
231172
|
* message. 409 shares 400's: both mean "not this domain, not here", and the
|
|
@@ -231120,10 +231191,10 @@ const CustomDomainsContent = ({ platformKey, username, currentSPA, currentPlatfo
|
|
|
231120
231191
|
};
|
|
231121
231192
|
const reportError = (error, fallback) => {
|
|
231122
231193
|
const status = error === null || error === void 0 ? void 0 : error.status;
|
|
231123
|
-
// The server's own words go underneath the headline
|
|
231124
|
-
//
|
|
231125
|
-
|
|
231126
|
-
toast.error(headlineForStatus(status, fallback), { description });
|
|
231194
|
+
// The server's own words go underneath the headline. A 409 body never
|
|
231195
|
+
// names the organization holding the domain — the backend keeps that out
|
|
231196
|
+
// on purpose — and for a taken subdomain it carries the way out.
|
|
231197
|
+
toast.error(headlineForStatus(status, fallback), { description: serverDetail(error) });
|
|
231127
231198
|
};
|
|
231128
231199
|
const handleAddPlatformDomain = async (domain) => {
|
|
231129
231200
|
if (!domain) {
|
|
@@ -231147,15 +231218,16 @@ const CustomDomainsContent = ({ platformKey, username, currentSPA, currentPlatfo
|
|
|
231147
231218
|
reportError(error, t('advancedCustomDomain.createError'));
|
|
231148
231219
|
}
|
|
231149
231220
|
};
|
|
231150
|
-
const handleAddAppDomain = async (project,
|
|
231221
|
+
const handleAddAppDomain = async (project, address) => {
|
|
231151
231222
|
try {
|
|
231152
|
-
//
|
|
231153
|
-
// reads
|
|
231223
|
+
// Only the address that was filled in is sent; the dialog guarantees
|
|
231224
|
+
// exactly one. The backend reads an absent key, never an empty string.
|
|
231154
231225
|
await createHostingProjectDomain({
|
|
231155
231226
|
org: platformKey,
|
|
231156
231227
|
username,
|
|
231157
231228
|
project,
|
|
231158
|
-
domain: domain || undefined,
|
|
231229
|
+
domain: address.domain || undefined,
|
|
231230
|
+
subdomain: address.subdomain || undefined,
|
|
231159
231231
|
}).unwrap();
|
|
231160
231232
|
await refetch();
|
|
231161
231233
|
toast.success(t('advancedCustomDomain.createSuccess'));
|
|
@@ -231220,7 +231292,7 @@ const CustomDomainsContent = ({ platformKey, username, currentSPA, currentPlatfo
|
|
|
231220
231292
|
setIsModalOpen(true);
|
|
231221
231293
|
}, children: [jsx(Plus, { className: "h-4 w-4", "aria-hidden": "true" }), t('advancedCustomDomain.add')] }), jsx("button", { onClick: () => setIsCollapsed((previous) => !previous), className: "rounded p-1 hover:bg-gray-100 focus:ring-2 focus:ring-blue-500 focus:ring-offset-1 focus:outline-none dark:hover:bg-gray-800", "aria-label": isCollapsed
|
|
231222
231294
|
? t('advancedCustomDomain.expandAriaLabel')
|
|
231223
|
-
: t('advancedCustomDomain.collapseAriaLabel'), type: "button", children: isCollapsed ? (jsx(ChevronDown, { className: "h-5 w-5 text-gray-400", "aria-hidden": "true" })) : (jsx(ChevronUp, { className: "h-5 w-5 text-gray-400", "aria-hidden": "true" })) })] })] }), !isCollapsed && (jsx("div", { className: "mt-4", children: isLoading ? (jsx("div", { className: "p-4 text-gray-500", children: t('advancedCustomDomain.loading') })) : customDomains.length > 0 ? (jsx("div", { className: "space-y-3", children: sortedDomains.map((row) => (jsx(DomainRow, { row: row, sslValue: sslValue, onReverify: handleReverify, onRemove: handleRemove, isBusy: busyDomainId === row.id, onStatusChange: handleStatusChange }, row.id))) })) : (jsx("div", { className: "p-4 text-center text-sm text-gray-500", children: t('advancedCustomDomain.noDomainConfigured') })) })), jsx(AddDomainDialog, { open: isModalOpen, onOpenChange: setIsModalOpen, canAddPlatformDomain: canAddPlatformDomain, projects: (_a = projectData === null || projectData === void 0 ? void 0 : projectData.projects) !== null && _a !== void 0 ? _a : [], isLoadingProjects: isLoadingProjects, isSubmitting: isCreating || isAttaching, onSubmitPlatformDomain: handleAddPlatformDomain, onSubmitAppDomain: handleAddAppDomain })] }));
|
|
231295
|
+
: t('advancedCustomDomain.collapseAriaLabel'), type: "button", children: isCollapsed ? (jsx(ChevronDown, { className: "h-5 w-5 text-gray-400", "aria-hidden": "true" })) : (jsx(ChevronUp, { className: "h-5 w-5 text-gray-400", "aria-hidden": "true" })) })] })] }), !isCollapsed && (jsx("div", { className: "mt-4", children: isLoading ? (jsx("div", { className: "p-4 text-gray-500", children: t('advancedCustomDomain.loading') })) : customDomains.length > 0 ? (jsx("div", { className: "space-y-3", children: sortedDomains.map((row) => (jsx(DomainRow, { row: row, sharedApp: sharedAppFor(row), sslValue: sslValue, onReverify: handleReverify, onRemove: handleRemove, isBusy: busyDomainId === row.id, onStatusChange: handleStatusChange }, row.id))) })) : (jsx("div", { className: "p-4 text-center text-sm text-gray-500", children: t('advancedCustomDomain.noDomainConfigured') })) })), jsx(AddDomainDialog, { open: isModalOpen, onOpenChange: setIsModalOpen, canAddPlatformDomain: canAddPlatformDomain, defaultMode: hasPlatformDomain ? 'app' : 'platform', projects: (_a = projectData === null || projectData === void 0 ? void 0 : projectData.projects) !== null && _a !== void 0 ? _a : [], isLoadingProjects: isLoadingProjects, isSubmitting: isCreating || isAttaching, onSubmitPlatformDomain: handleAddPlatformDomain, onSubmitAppDomain: handleAddAppDomain })] }));
|
|
231224
231296
|
};
|
|
231225
231297
|
|
|
231226
231298
|
const StudentMentorCreationContent = ({ platformKey, }) => {
|
|
@@ -275743,7 +275815,7 @@ function LocalModelRow({ name, size, logo, providerLabel, status, progress, disa
|
|
|
275743
275815
|
style: { width: `${status === 'starting' ? 6 : Math.max(pct, 6)}%` } })), jsx("span", { "aria-hidden": "true", className: "relative z-10 flex h-8 w-8 flex-shrink-0 items-center justify-center overflow-hidden rounded-lg", children: jsx(LLMProviderLogo, { logo: logo, label: providerLabel !== null && providerLabel !== void 0 ? providerLabel : name, alt: "", className: cn({ grayscale: isBusyDisabled }) }) }), jsxs("span", { className: "relative z-10 flex min-w-0 flex-1 flex-col gap-1.5", children: [jsx("span", { className: "truncate text-sm font-medium text-[#646464] lowercase", children: name }), jsxs("span", { className: "flex min-w-0 items-center gap-1.5 text-sm text-gray-500", children: [jsx("span", { className: "flex-shrink-0 rounded bg-gray-100 px-2 py-0.5 text-sm font-medium text-gray-500", children: labels.onDevice }), status === 'not-installed' && (jsxs(Fragment$1, { children: [jsx("span", { "aria-hidden": "true", children: "\u00B7" }), jsx("span", { children: size }), jsx(Download, { className: "h-4 w-4", "aria-hidden": "true" })] })), status === 'starting' && (jsxs(Fragment$1, { children: [jsx("span", { "aria-hidden": "true", children: "\u00B7" }), jsx("span", { children: labels.starting })] })), status === 'downloading' && (jsxs(Fragment$1, { children: [jsx("span", { "aria-hidden": "true", children: "\u00B7" }), jsxs("span", { className: "tabular-nums group-hover:hidden group-focus-visible:hidden", children: [pct, "%"] }), jsxs("span", { className: "hidden items-center gap-1 text-blue-600 group-hover:flex group-focus-visible:flex", children: [jsx(X$2, { className: "h-3.5 w-3.5", "aria-hidden": "true" }), labels.cancel] })] })), status === 'selected' && (jsxs("span", { className: "flex items-center gap-1 text-blue-600", children: [jsx(Check, { className: "h-4 w-4", "aria-hidden": "true" }), labels.inUse] })), status === 'error' && (jsxs(Fragment$1, { children: [jsx("span", { "aria-hidden": "true", children: "\u00B7" }), jsx("span", { className: "truncate text-red-500", children: errorMessage || labels.downloadFailedRetry }), jsx(RotateCcw, { className: "h-4 w-4 flex-shrink-0 text-red-500", "aria-hidden": "true" })] }))] })] })] }));
|
|
275744
275816
|
}
|
|
275745
275817
|
|
|
275746
|
-
function LLMProviderModal({ isOpen, onClose, onSelect, llmProvider, isSelecting, mentorSettings, llms, labels: labelsProp, }) {
|
|
275818
|
+
function LLMProviderModal({ isOpen, onClose, onSelect, llmProvider, isSelecting, mentorSettings, llms, labels: labelsProp, cloudOnly = false, }) {
|
|
275747
275819
|
var _a;
|
|
275748
275820
|
const t = useT();
|
|
275749
275821
|
const labels = React__default.useMemo(() => labelsProp !== null && labelsProp !== void 0 ? labelsProp : buildLLMTabLabels(t).providerModal, [labelsProp, t]);
|
|
@@ -275811,13 +275883,13 @@ function LLMProviderModal({ isOpen, onClose, onSelect, llmProvider, isSelecting,
|
|
|
275811
275883
|
}, [searchQuery, llmProvider]);
|
|
275812
275884
|
// Local models belonging to THIS provider (merged into the same list).
|
|
275813
275885
|
const localModels = React__default.useMemo(() => {
|
|
275814
|
-
if (!isAvailable)
|
|
275886
|
+
if (cloudOnly || !isAvailable)
|
|
275815
275887
|
return [];
|
|
275816
275888
|
const key = getProviderName(llmProvider.name);
|
|
275817
275889
|
// Per-device catalog: Tauri iOS lists only models with phone-sized weights.
|
|
275818
275890
|
return localModelsForThisDevice().filter((m) => getProviderName(m.provider) === key &&
|
|
275819
275891
|
m.name.toLowerCase().includes(searchQuery.toLowerCase()));
|
|
275820
|
-
}, [isAvailable, llmProvider.name, searchQuery]);
|
|
275892
|
+
}, [cloudOnly, isAvailable, llmProvider.name, searchQuery]);
|
|
275821
275893
|
const switchLLMAllowed = canSwitchLLm(llmProvider);
|
|
275822
275894
|
const switchProviderAllowed = canSwitchProvider(llms, llmProvider.name);
|
|
275823
275895
|
// Only an actual pull counts as "busy". A status refresh ('checking') must NOT
|
|
@@ -275843,7 +275915,7 @@ function LLMProviderModal({ isOpen, onClose, onSelect, llmProvider, isSelecting,
|
|
|
275843
275915
|
// is off.
|
|
275844
275916
|
const isCloudActive = (llm) => (mentorSettings === null || mentorSettings === void 0 ? void 0 : mentorSettings.llm_name) === llm.llm_name &&
|
|
275845
275917
|
(mentorSettings === null || mentorSettings === void 0 ? void 0 : mentorSettings.llm_provider) === llmProvider.name &&
|
|
275846
|
-
!localSel.enabled;
|
|
275918
|
+
(cloudOnly || !localSel.enabled);
|
|
275847
275919
|
// Order the WHOLE list — cloud + local — available (usable now) first,
|
|
275848
275920
|
// unavailable last, with the in-use model leading. "Available" for a cloud
|
|
275849
275921
|
// model means the provider is switchable (has credentials + models); for a
|
|
@@ -275912,7 +275984,7 @@ function LLMProviderModal({ isOpen, onClose, onSelect, llmProvider, isSelecting,
|
|
|
275912
275984
|
};
|
|
275913
275985
|
const selectCloud = (llmName) => {
|
|
275914
275986
|
// Picking a cloud model turns local mode off so routing uses the cloud LLM.
|
|
275915
|
-
if (localSel.enabled) {
|
|
275987
|
+
if (!cloudOnly && localSel.enabled) {
|
|
275916
275988
|
setLocalLLMEnabled(false);
|
|
275917
275989
|
setLocalSel((prev) => ({ ...prev, enabled: false }));
|
|
275918
275990
|
window.dispatchEvent(new Event(LOCAL_LLM_CHANGED_EVENT));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@iblai/iblai-js",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.28.0",
|
|
4
4
|
"description": "Unified JavaScript SDK for IBL.ai — re-exports data-layer, web-containers, and web-utils under a single package",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -76,10 +76,10 @@
|
|
|
76
76
|
"axios": "1.13.6",
|
|
77
77
|
"dotenv": "16.6.1",
|
|
78
78
|
"winston": "3.19.0",
|
|
79
|
-
"@iblai/
|
|
80
|
-
"@iblai/
|
|
81
|
-
"@iblai/
|
|
82
|
-
"@iblai/web-
|
|
79
|
+
"@iblai/mcp": "1.24.0",
|
|
80
|
+
"@iblai/web-utils": "2.6.0",
|
|
81
|
+
"@iblai/data-layer": "1.25.0",
|
|
82
|
+
"@iblai/web-containers": "1.34.0"
|
|
83
83
|
},
|
|
84
84
|
"peerDependencies": {
|
|
85
85
|
"@iblai/iblai-api": "4.166.0-ai",
|