Skip to content

Commit ae0a5ba

Browse files
committed
Refactor routing form and destination selector[latest]
1 parent ff43676 commit ae0a5ba

39 files changed

Lines changed: 955 additions & 1807 deletions

locales/en.toml

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -586,9 +586,7 @@ action_trunks_title = "Action & trunks"
586586
action_trunks_desc = "Choose how destinations are selected and how weights are distributed across trunks."
587587
dest_type_label = "Destination type"
588588
dest_sip_trunks = "SIP trunks"
589-
dest_queue = "Queue"
590-
dest_voicemail = "Voicemail"
591-
dest_ivr = "IVR"
589+
dest_other = "Destination"
592590
selection_algorithm_label = "Selection algorithm"
593591
hash_key_label = "Hash key"
594592
trunk_pool_label = "Trunk pool"
@@ -1599,6 +1597,19 @@ loading = "Loading..."
15991597
copied = "Copied"
16001598
url_copied = "Metrics URL copied to clipboard"
16011599

1600+
[destination_selector]
1601+
extension = "Extension"
1602+
queue = "Queue"
1603+
ivr = "IVR"
1604+
voicemail = "Voicemail"
1605+
uri = "SIP URI"
1606+
search_extension = "Search extension..."
1607+
select_queue = "Select queue..."
1608+
select_ivr = "Select IVR..."
1609+
no_results = "No results"
1610+
voicemail_extension = "Extension number"
1611+
uri_placeholder = "sip:user@domain"
1612+
16021613
[diagnostics]
16031614
title = "Diagnostics"
16041615
operational_validation = "Operational validation"

locales/zh.toml

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -586,9 +586,7 @@ action_trunks_title = "操作与中继"
586586
action_trunks_desc = "选择目标的选取方式以及各中继的权重分配。"
587587
dest_type_label = "目标类型"
588588
dest_sip_trunks = "SIP 中继"
589-
dest_queue = "队列"
590-
dest_voicemail = "语音信箱"
591-
dest_ivr = "IVR"
589+
dest_other = "其他目标"
592590
selection_algorithm_label = "选择算法"
593591
hash_key_label = "哈希字段"
594592
trunk_pool_label = "中继池"
@@ -1584,6 +1582,19 @@ loading = "加载中..."
15841582
copied = "已复制"
15851583
url_copied = "指标链接已复制到剪贴板"
15861584

1585+
[destination_selector]
1586+
extension = "分机"
1587+
queue = "队列"
1588+
ivr = "IVR"
1589+
voicemail = "语音信箱"
1590+
uri = "SIP URI"
1591+
search_extension = "搜索分机..."
1592+
select_queue = "选择队列..."
1593+
select_ivr = "选择 IVR..."
1594+
no_results = "无结果"
1595+
voicemail_extension = "分机号码"
1596+
uri_placeholder = "sip:user@domain"
1597+
15871598
[diagnostics]
15881599
title = "诊断"
15891600
operational_validation = "运行验证"

src/addons/cc

Submodule cc updated from dc2d050 to 6fce8ad

src/addons/ivr_editor

Submodule ivr_editor updated from 89b4198 to e49d615

src/addons/queue/console/handlers.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,13 @@ pub async fn page_queue_create(
231231
AuthRequired(_): AuthRequired,
232232
) -> Response {
233233
let script_path = format!("{}/queues/new", state.base_path());
234+
let forwarding_catalog = if let Some(proxy_config) =
235+
crate::console::catalog::load_proxy_config(state.app_state().as_ref())
236+
{
237+
crate::console::catalog::build_forwarding_catalog(&proxy_config)
238+
} else {
239+
crate::console::catalog::ForwardingCatalog::empty()
240+
};
234241
state.render_with_headers(
235242
"queue_detail.html",
236243
json!({
@@ -242,6 +249,7 @@ pub async fn page_queue_create(
242249
"tags": Vec::<String>::new(),
243250
"metadata_text": "",
244251
},
252+
"forwarding_catalog": forwarding_catalog,
245253
"create_url": state.url_for("/queues"),
246254
"update_url": Value::Null,
247255
"list_url": state.url_for("/queues"),
@@ -276,6 +284,13 @@ pub async fn page_queue_edit(
276284
let tags = queue_tags(model.metadata.as_ref());
277285

278286
let script_path = format!("{}/queues/{}", state.base_path(), model.id);
287+
let forwarding_catalog = if let Some(proxy_config) =
288+
crate::console::catalog::load_proxy_config(state.app_state().as_ref())
289+
{
290+
crate::console::catalog::build_forwarding_catalog(&proxy_config)
291+
} else {
292+
crate::console::catalog::ForwardingCatalog::empty()
293+
};
279294
state.render_with_headers(
280295
"queue_detail.html",
281296
json!({
@@ -291,6 +306,7 @@ pub async fn page_queue_edit(
291306
"metadata_text": metadata_text,
292307
"updated_at": model.updated_at.to_rfc3339(),
293308
},
309+
"forwarding_catalog": forwarding_catalog,
294310
"create_url": state.url_for("/queues"),
295311
"update_url": state.url_for(&format!("/queues/{}", model.id)),
296312
"list_url": state.url_for("/queues"),

src/addons/queue/templates/queue.html

Lines changed: 2 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -211,35 +211,15 @@ <h1 class="text-2xl font-semibold text-slate-900">{{ "queue.title" | t }}</h1>
211211
d="M13.5 3.5l3 3-8 8H5.5v-3l8-8z" />
212212
</svg>
213213
</a>
214-
<button type="button" :title="tt('queue.export_queue')"
215-
class="inline-flex items-center justify-center rounded-full border border-slate-200 p-2 text-slate-500 transition"
216-
:class="exportingQueueId === queue.id ? 'cursor-not-allowed border-slate-100 text-slate-300' : 'hover:border-indigo-300 hover:text-indigo-600'"
217-
:disabled="exportingQueueId === queue.id" @click="exportQueue(queue)">
218-
<span class="sr-only" x-text="tt('queue.export_queue')"></span>
219-
<template x-if="exportingQueueId === queue.id">
220-
<svg class="h-4 w-4 animate-spin" viewBox="0 0 24 24" fill="none"
221-
stroke="currentColor" stroke-width="1.8">
222-
<path stroke-linecap="round" stroke-linejoin="round"
223-
d="M12 3v3m6.364 1.636-2.121 2.121M21 12h-3m-1.636 6.364-2.121-2.121M12 21v-3m-6.364-1.636 2.121-2.121M3 12h3m1.636-6.364 2.121 2.121" />
224-
</svg>
225-
</template>
226-
<template x-if="exportingQueueId !== queue.id">
227-
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
228-
stroke-width="1.5" stroke="currentColor" class="size-4">
229-
<path stroke-linecap="round" stroke-linejoin="round"
230-
d="M19.5 13.5 12 21m0 0-7.5-7.5M12 21V3" />
231-
</svg>
232-
</template>
233-
</button>
234214
<button type="button" :title="tt('queue.delete_queue')"
235215
class="inline-flex items-center justify-center rounded-full border border-slate-200 p-2 text-slate-500 transition"
236216
:class="processingDelete === queue.id ? 'cursor-not-allowed border-slate-100 text-slate-300' : 'hover:border-rose-300 hover:text-rose-600'"
237217
:disabled="processingDelete === queue.id" @click="confirmDelete(queue)">
238218
<span class="sr-only" x-text="tt('queue.delete_queue')"></span>
239-
<svg class="h-4 w-4" viewBox="0 0 20 20" fill="none" stroke="currentColor"
219+
<svg class="h-4 w-4" viewBox="0 0 24 24" fill="none" stroke="currentColor"
240220
stroke-width="1.6">
241221
<path stroke-linecap="round" stroke-linejoin="round"
242-
d="M6 7h8m-7 0v8m3-8v8m3-8v8M4 7h12M8 4h4a1 1 0 011 1v2H7V5a1 1 0 011-1z" />
222+
d="M3 6h18M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2M10 11v6M14 11v6" />
243223
</svg>
244224
</button>
245225
</div>
@@ -372,7 +352,6 @@ <h1 class="text-2xl font-semibold text-slate-900">{{ "queue.title" | t }}</h1>
372352
perPage: 20,
373353
processingDelete: null,
374354
exportingAll: false,
375-
exportingQueueId: null,
376355
reloading: false,
377356
pendingReload: false,
378357
showingFrom: 0,
@@ -699,36 +678,6 @@ <h1 class="text-2xl font-semibold text-slate-900">{{ "queue.title" | t }}</h1>
699678
this.exportingAll = false;
700679
}
701680
},
702-
exportEndpoint(queue) {
703-
if (!queue) return null;
704-
if (typeof queue.export_url === 'string' && queue.export_url.trim().length) return queue.export_url;
705-
return `${this.apiBase}/queues/${queue.id}/export`;
706-
},
707-
async exportQueue(queue) {
708-
if (!queue || !queue.id || this.exportingQueueId === queue.id) return;
709-
const endpoint = this.exportEndpoint(queue);
710-
if (!endpoint) return;
711-
this.exportingQueueId = queue.id;
712-
this.error = null;
713-
try {
714-
const response = await fetch(endpoint, {
715-
method: 'POST',
716-
headers: { 'Accept': 'application/json' },
717-
});
718-
const data = await response.json().catch(() => ({}));
719-
if (!response.ok) throw new Error(data?.message || this.tt('queue.export_error'));
720-
const path = data?.path || data?.paths?.[0];
721-
this.flash = path
722-
? this.tt('queue.queue_exported_to').replace('{path}', path)
723-
: this.tt('queue.queue_exported');
724-
await this.refresh();
725-
} catch (err) {
726-
console.error(err);
727-
this.error = err?.message || this.tt('queue.export_error');
728-
} finally {
729-
this.exportingQueueId = null;
730-
}
731-
},
732681
}));
733682
});
734683
</script>

src/addons/queue/templates/queue_detail.html

Lines changed: 5 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -207,24 +207,6 @@ <h2 class="text-sm font-semibold text-slate-900" x-text="tt('queue.section_targe
207207
<span class="text-xs font-normal text-slate-400"
208208
x-text="tt('queue.field_ring_timeout_hint')"></span>
209209
</label>
210-
<label class="md:col-span-2 flex flex-col gap-2 text-sm font-medium text-slate-700">
211-
<span x-text="tt('queue.field_acd_policy')"></span>
212-
<template x-if="acdPolicies.length">
213-
<select x-model="form.spec.acd_policy"
214-
class="rounded-lg border border-slate-200 bg-white px-3 py-2 text-sm text-slate-700 focus:border-sky-300 focus:outline-none focus:ring-2 focus:ring-sky-200">
215-
<option value="" x-text="tt('queue.field_acd_policy_placeholder')"></option>
216-
<template x-for="policy in acdPolicies" :key="policy.name">
217-
<option :value="policy.name" x-text="policy.name"></option>
218-
</template>
219-
</select>
220-
</template>
221-
<template x-if="!acdPolicies.length">
222-
<input type="text" x-model.trim="form.spec.acd_policy"
223-
class="rounded-lg border border-slate-200 px-3 py-2 text-sm text-slate-700 focus:border-sky-300 focus:outline-none focus:ring-2 focus:ring-sky-200"
224-
:placeholder="tt('queue.field_acd_policy_placeholder')">
225-
</template>
226-
<span class="text-xs font-normal text-slate-400" x-text="tt('queue.field_acd_policy_hint')"></span>
227-
</label>
228210
</div>
229211
<div class="space-y-3">
230212
<div class="flex items-center justify-between">
@@ -329,12 +311,13 @@ <h2 class="text-sm font-semibold text-slate-900" x-text="tt('queue.section_fallb
329311
<span x-text="tt('queue.fallback_failure_opt')"></span>
330312
</label>
331313
</div>
332-
<div class="grid gap-4 md:grid-cols-2" x-show="fallbackMode === 'redirect'" x-cloak>
314+
<div x-show="fallbackMode === 'redirect'" x-cloak
315+
@destination-change="form.spec.fallback.redirect = $event.detail.type === 'extension' ? 'sip:' + $event.detail.value : ($event.detail.type === 'uri' ? $event.detail.value : $event.detail.type + ':' + $event.detail.value)">
333316
<label class="flex flex-col gap-2 text-sm font-medium text-slate-700">
334317
<span x-text="tt('queue.fallback_redirect_label')"></span>
335-
<input type="text" x-model.trim="form.spec.fallback.redirect"
336-
class="rounded-lg border border-slate-200 px-3 py-2 text-sm text-slate-700 focus:border-sky-300 focus:outline-none focus:ring-2 focus:ring-sky-200"
337-
:placeholder="tt('queue.fallback_redirect_placeholder')">
318+
{% set queue_items = forwarding_catalog.queues | default([]) %}
319+
{% set ivr_items = forwarding_catalog.ivr_projects | default([]) %}
320+
{% include "console/_destination_selector.html" %}
338321
</label>
339322
</div>
340323
<div class="grid gap-4 md:grid-cols-2" x-show="fallbackMode === 'failure'" x-cloak>
@@ -742,7 +725,6 @@ <h3 class="text-sm font-semibold text-slate-800" x-text="tt('queue.section_final
742725
pageTitle: options.mode === 'edit' ? (window._queueTranslations?.queue?.detail_subtitle_edit || 'Edit queue') : (window._queueTranslations?.queue?.new_queue || 'New queue'),
743726
activeTab: 'overview',
744727
skillGroups: [],
745-
acdPolicies: [],
746728
pendingSkillGroup: '',
747729
form: {
748730
name: '',
@@ -770,7 +752,6 @@ <h3 class="text-sm font-semibold text-slate-800" x-text="tt('queue.section_final
770752
targets: [],
771753
skill_groups: [],
772754
},
773-
acd_policy: '',
774755
voice_prompts: null,
775756
// Callback
776757
callback: {
@@ -831,7 +812,6 @@ <h3 class="text-sm font-semibold text-slate-800" x-text="tt('queue.section_final
831812
this.apiPrefix = this.normalizeApiPrefix(this.apiPrefix || window.__consoleApiPrefix || '/api');
832813
this.applyModel(this.model || {});
833814
this.loadSkillGroups();
834-
this.loadAcdPolicies();
835815
},
836816
extractSkillGroupId(value) {
837817
const raw = String(value || '').trim();
@@ -904,27 +884,6 @@ <h3 class="text-sm font-semibold text-slate-800" x-text="tt('queue.section_final
904884
});
905885
}
906886
},
907-
loadAcdPolicies() {
908-
const loader = window.__ccAcdPoliciesLoader;
909-
if (typeof loader === 'function') {
910-
loader()
911-
.then((policies) => {
912-
this.acdPolicies = Array.isArray(policies) ? policies : [];
913-
})
914-
.catch(() => {
915-
this.acdPolicies = [];
916-
});
917-
return;
918-
}
919-
fetch(this.apiPrefix + '/cc/acd/policies', { credentials: 'same-origin' })
920-
.then(res => res.ok ? res.json() : { data: [] })
921-
.then(payload => {
922-
this.acdPolicies = Array.isArray(payload?.data) ? payload.data : [];
923-
})
924-
.catch(() => {
925-
this.acdPolicies = [];
926-
});
927-
},
928887

929888
tt(key) {
930889
const parts = key.split('.');
@@ -1171,7 +1130,6 @@ <h3 class="text-sm font-semibold text-slate-800" x-text="tt('queue.section_final
11711130
});
11721131
})(),
11731132
},
1174-
acd_policy: raw?.acd_policy || '',
11751133
voice_prompts: raw?.voice_prompts || null,
11761134
// Callback
11771135
callback: {
@@ -1321,10 +1279,6 @@ <h3 class="text-sm font-semibold text-slate-800" x-text="tt('queue.section_final
13211279
targets,
13221280
};
13231281
}
1324-
const acdPolicy = (this.form.spec.acd_policy || '').trim();
1325-
if (acdPolicy) {
1326-
spec.acd_policy = acdPolicy;
1327-
}
13281282
// Voice prompts
13291283
spec.voice_prompts = this.buildVoicePrompts();
13301284
// Callback

src/addons/sbc

Submodule sbc updated from cfc4ed3 to 0e14012

src/addons/wholesale

Submodule wholesale updated from 9f6cdd1 to 06cbc3a

0 commit comments

Comments
 (0)