fix
This commit is contained in:
@@ -66,6 +66,7 @@
|
||||
let busyUserId = $state<string | null>(null);
|
||||
let error = $state("");
|
||||
let success = $state("");
|
||||
let inviteAcceptLink = $state<string | null>(null);
|
||||
let tab = $state<TabKey>("users");
|
||||
let search = $state("");
|
||||
let staffOnly = $state(false);
|
||||
@@ -99,6 +100,7 @@
|
||||
|
||||
let syncOpen = $state(false);
|
||||
let syncCompany = $state<AdminOrgCompany | null>(null);
|
||||
let syncWpCategoriesFile = $state<File | null>(null);
|
||||
|
||||
const cloneDestLabel = $derived.by(() => {
|
||||
const selected = cloneDestOptions.find((c) => c.id === cloneDestId);
|
||||
@@ -407,6 +409,7 @@
|
||||
|
||||
function openSyncDialog(company: AdminOrgCompany) {
|
||||
syncCompany = company;
|
||||
syncWpCategoriesFile = null;
|
||||
syncOpen = true;
|
||||
error = "";
|
||||
success = "";
|
||||
@@ -468,7 +471,8 @@
|
||||
const targetName = syncCompany.name;
|
||||
try {
|
||||
const res = await syncAdminCompanyA1(syncCompany.id, {
|
||||
reprocessSampleLimit: 25
|
||||
reprocessSampleLimit: 25,
|
||||
wpProductCategoriesFile: syncWpCategoriesFile
|
||||
});
|
||||
const r = res.result;
|
||||
const mappedWith = Number(r.mapped_with_category ?? 0);
|
||||
@@ -489,6 +493,7 @@
|
||||
});
|
||||
syncOpen = false;
|
||||
syncCompany = null;
|
||||
syncWpCategoriesFile = null;
|
||||
} catch (err) {
|
||||
error = failureMessage(err, i18n.t("flash.admin.syncA1Error", { name: targetName }));
|
||||
} finally {
|
||||
@@ -500,6 +505,7 @@
|
||||
busy = true;
|
||||
error = "";
|
||||
success = "";
|
||||
inviteAcceptLink = null;
|
||||
try {
|
||||
const body = userId ? { user_id: userId } : {};
|
||||
const res = await api<{
|
||||
@@ -510,6 +516,7 @@
|
||||
skipped_rate_limited?: number;
|
||||
smtp_enabled: boolean;
|
||||
token?: string;
|
||||
accept_url?: string;
|
||||
}>("/api/admin/emails/set-password", { method: "POST", body });
|
||||
const parts = [
|
||||
`sent ${res.sent}`,
|
||||
@@ -518,9 +525,18 @@
|
||||
];
|
||||
if (res.skipped_synthetic) parts.push(`skipped test accounts ${res.skipped_synthetic}`);
|
||||
if (res.skipped_rate_limited) parts.push(`rate-limited ${res.skipped_rate_limited}`);
|
||||
success = i18n.t("flash.admin.invitesSummary", { parts: parts.join(", "), smtp: res.smtp_enabled ? i18n.t("flash.admin.smtpOn") : i18n.t("flash.admin.smtpOff") });
|
||||
if (res.token) {
|
||||
success += " Invite link available — copy and share it securely (email delivery is off).";
|
||||
success = i18n.t("flash.admin.invitesSummary", {
|
||||
parts: parts.join(", "),
|
||||
smtp: res.smtp_enabled ? i18n.t("flash.admin.smtpOn") : i18n.t("flash.admin.smtpOff")
|
||||
});
|
||||
const link =
|
||||
res.accept_url?.trim() ||
|
||||
(res.token
|
||||
? `${window.location.origin}/accept-invite?token=${encodeURIComponent(res.token)}`
|
||||
: "");
|
||||
if (link) {
|
||||
inviteAcceptLink = link;
|
||||
success += " " + i18n.t("admin.users.inviteLinkReady");
|
||||
}
|
||||
} catch (err) {
|
||||
error = failureMessage(err, "Send failed");
|
||||
@@ -529,6 +545,17 @@
|
||||
}
|
||||
}
|
||||
|
||||
async function copyInviteLink() {
|
||||
if (!inviteAcceptLink) return;
|
||||
try {
|
||||
await navigator.clipboard.writeText(inviteAcceptLink);
|
||||
success = i18n.t("admin.users.inviteLinkCopied");
|
||||
error = "";
|
||||
} catch {
|
||||
error = i18n.t("flash.settings.copyFailed");
|
||||
}
|
||||
}
|
||||
|
||||
function openPasswordDialog(user: AdminOrgUser) {
|
||||
passwordUser = user;
|
||||
passwordValue = "";
|
||||
@@ -613,6 +640,22 @@
|
||||
{:else}
|
||||
<Alert message={error} />
|
||||
<Alert tone="success" message={success} />
|
||||
{#if inviteAcceptLink}
|
||||
<div class="mb-4 flex flex-col gap-2 rounded-md border border-border bg-muted/30 p-3 sm:flex-row sm:items-center">
|
||||
<Input
|
||||
value={inviteAcceptLink}
|
||||
readonly
|
||||
autocomplete="off"
|
||||
spellcheck={false}
|
||||
class="font-mono text-xs"
|
||||
aria-label={i18n.t("admin.users.inviteLinkLabel")}
|
||||
/>
|
||||
<Button type="button" variant="outline" size="sm" onclick={() => copyInviteLink()}>
|
||||
<Copy class="mr-2 h-4 w-4" />
|
||||
{i18n.t("admin.users.copyInviteLink")}
|
||||
</Button>
|
||||
</div>
|
||||
{/if}
|
||||
{#if !staffRoleApiOk}
|
||||
<Alert
|
||||
tone="info"
|
||||
@@ -1087,6 +1130,39 @@
|
||||
{i18n.t("admin.users.syncA1Company", { name: syncCompany.name })}
|
||||
</p>
|
||||
{/if}
|
||||
<div class="space-y-2">
|
||||
<label class="block text-sm font-medium text-foreground" for="sync-a1-wp-categories">
|
||||
{i18n.t("admin.users.syncA1WpUpload")}
|
||||
</label>
|
||||
<input
|
||||
id="sync-a1-wp-categories"
|
||||
type="file"
|
||||
accept=".sql,text/plain,application/sql"
|
||||
class="block w-full text-sm text-foreground file:mr-3 file:rounded-md file:border-0 file:bg-muted file:px-3 file:py-1.5 file:text-sm file:font-medium"
|
||||
disabled={busy}
|
||||
onchange={(e) => {
|
||||
const input = e.currentTarget;
|
||||
syncWpCategoriesFile = input.files?.[0] ?? null;
|
||||
}}
|
||||
/>
|
||||
<p class="text-xs text-muted-foreground">{i18n.t("admin.users.syncA1WpUploadHint")}</p>
|
||||
{#if syncWpCategoriesFile}
|
||||
<div class="flex flex-wrap items-center gap-2 text-xs text-muted-foreground">
|
||||
<span>{syncWpCategoriesFile.name}</span>
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
size="sm"
|
||||
disabled={busy}
|
||||
onclick={() => {
|
||||
syncWpCategoriesFile = null;
|
||||
}}
|
||||
>
|
||||
{i18n.t("admin.users.syncA1WpUploadClear")}
|
||||
</Button>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
<p class="text-sm text-muted-foreground">
|
||||
{i18n.t("admin.users.syncA1Warning")}
|
||||
</p>
|
||||
@@ -1098,6 +1174,7 @@
|
||||
onclick={() => {
|
||||
syncOpen = false;
|
||||
syncCompany = null;
|
||||
syncWpCategoriesFile = null;
|
||||
}}
|
||||
>
|
||||
{i18n.t("admin.users.syncA1Cancel")}
|
||||
|
||||
Reference in New Issue
Block a user