2026-04-01 11:35:47 +08:00
|
|
|
use yew::prelude::*;
|
2026-04-01 14:03:49 +08:00
|
|
|
use web_sys::{Event, HtmlInputElement, HtmlSelectElement, HtmlDialogElement};
|
2026-04-01 11:35:47 +08:00
|
|
|
use web_sys::wasm_bindgen::JsCast;
|
|
|
|
|
use gloo_net::http::Request;
|
|
|
|
|
use serde::{Serialize, Deserialize};
|
|
|
|
|
use yew_icons::{Icon, IconData};
|
|
|
|
|
|
|
|
|
|
const API_BASE: &str ="http://172.16.0.155:3000/api/fs";
|
|
|
|
|
#[derive(Serialize, Deserialize)]
|
|
|
|
|
pub struct Did {
|
|
|
|
|
id: i32,
|
|
|
|
|
did_number: String,
|
|
|
|
|
target_type: String,
|
|
|
|
|
target: Option<String>,
|
|
|
|
|
active: bool
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Properties, PartialEq)]
|
|
|
|
|
pub struct DidProps {
|
|
|
|
|
pub did_id: i32,
|
|
|
|
|
pub did_number: String,
|
|
|
|
|
pub target_type: String,
|
|
|
|
|
pub target: String,
|
2026-04-01 14:03:49 +08:00
|
|
|
pub active: bool,
|
|
|
|
|
pub on_changed: Callback<()>
|
2026-04-01 11:35:47 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[component]
|
|
|
|
|
pub fn DidComponent(props: &DidProps) -> Html {
|
|
|
|
|
let id = props.did_id;
|
|
|
|
|
let changed = use_state(||false);
|
|
|
|
|
let did_number = use_state(||props.did_number.clone());
|
|
|
|
|
let target_type = use_state(||props.target_type.clone());
|
|
|
|
|
let target = use_state(||props.target.clone());
|
|
|
|
|
let active= use_state(||props.active);
|
2026-04-01 14:03:49 +08:00
|
|
|
let on_changed = props.on_changed.clone();
|
|
|
|
|
let dialog_ref: NodeRef = use_node_ref();
|
2026-04-01 11:35:47 +08:00
|
|
|
|
|
|
|
|
let handle_did_input = {
|
|
|
|
|
let did_number = did_number.clone();
|
|
|
|
|
let changed = changed.clone();
|
|
|
|
|
Callback::from(move |event: InputEvent| {
|
|
|
|
|
let did_number = did_number.clone();
|
|
|
|
|
let t = event.target().and_then(|t| t.dyn_into::<HtmlInputElement>().ok());
|
|
|
|
|
if let Some(input) = t {
|
|
|
|
|
did_number.set(input.value());
|
|
|
|
|
}
|
|
|
|
|
changed.set(true);
|
|
|
|
|
}
|
|
|
|
|
)};
|
|
|
|
|
|
|
|
|
|
let handle_target_input = {
|
|
|
|
|
let target = target.clone();
|
|
|
|
|
let changed = changed.clone();
|
|
|
|
|
Callback::from(move |event: InputEvent| {
|
|
|
|
|
let target = target.clone();
|
|
|
|
|
let t = event.target().and_then(|t| t.dyn_into::<HtmlInputElement>().ok());
|
|
|
|
|
if let Some(input) = t {
|
|
|
|
|
target.set(input.value());
|
|
|
|
|
}
|
|
|
|
|
changed.set(true);
|
|
|
|
|
}
|
|
|
|
|
)};
|
|
|
|
|
|
|
|
|
|
let handle_active_change = {
|
|
|
|
|
let active = active.clone();
|
|
|
|
|
let changed = changed.clone();
|
|
|
|
|
Callback::from(move |event: Event| {
|
|
|
|
|
let active = active.clone();
|
|
|
|
|
let t = event
|
|
|
|
|
.target()
|
|
|
|
|
.and_then(|t| t.dyn_into::<HtmlInputElement>().ok());
|
|
|
|
|
if let Some(input) = t {
|
|
|
|
|
active.set(input.checked());
|
|
|
|
|
}
|
|
|
|
|
changed.set(true);
|
|
|
|
|
}
|
|
|
|
|
)};
|
|
|
|
|
|
|
|
|
|
let handle_target_type_change = {
|
|
|
|
|
let target_type = target_type.clone();
|
|
|
|
|
let changed = changed.clone();
|
|
|
|
|
Callback::from(move |event: Event| {
|
|
|
|
|
let t = event
|
|
|
|
|
.target()
|
|
|
|
|
.and_then(|t| t.dyn_into::<HtmlSelectElement>().ok())
|
|
|
|
|
.unwrap();
|
|
|
|
|
target_type.set(t.value());
|
|
|
|
|
changed.set(true);
|
|
|
|
|
gloo_console::log!(t.value());
|
|
|
|
|
}
|
|
|
|
|
)};
|
|
|
|
|
|
|
|
|
|
let handle_save = {
|
|
|
|
|
let changed = changed.clone();
|
|
|
|
|
let did_number = did_number.clone();
|
|
|
|
|
let target_type = target_type.clone();
|
|
|
|
|
let target = target.clone();
|
|
|
|
|
let active = active.clone();
|
2026-04-01 14:03:49 +08:00
|
|
|
let on_changed = on_changed.clone();
|
2026-04-01 11:35:47 +08:00
|
|
|
Callback::from(move |_| {
|
2026-04-01 14:03:49 +08:00
|
|
|
let changed = changed.clone();
|
|
|
|
|
let on_changed = on_changed.clone();
|
2026-04-01 11:35:47 +08:00
|
|
|
let did = Did {
|
|
|
|
|
id,
|
|
|
|
|
did_number: (*did_number).clone(),
|
|
|
|
|
target_type: (*target_type).clone(),
|
|
|
|
|
target: Some((*target).clone()),
|
|
|
|
|
active: (*active).clone()
|
|
|
|
|
};
|
|
|
|
|
let url = if id == 0 {
|
|
|
|
|
API_BASE.to_string()
|
|
|
|
|
} else {
|
|
|
|
|
format!("{}/{}", API_BASE, id)
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
wasm_bindgen_futures::spawn_local(async move {
|
2026-04-01 14:03:49 +08:00
|
|
|
let changed = changed.clone();
|
|
|
|
|
let on_changed = on_changed.clone();
|
2026-04-01 11:35:47 +08:00
|
|
|
let req = if id == 0 {
|
|
|
|
|
Request::post(&url)
|
|
|
|
|
.json(&did)
|
|
|
|
|
.unwrap()
|
|
|
|
|
} else {
|
|
|
|
|
Request::patch(&url)
|
|
|
|
|
.json(&did)
|
|
|
|
|
.unwrap()
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
req.send()
|
|
|
|
|
.await
|
|
|
|
|
.unwrap();
|
|
|
|
|
|
2026-04-01 14:03:49 +08:00
|
|
|
changed.set(false);
|
|
|
|
|
on_changed.emit(());
|
2026-04-01 11:35:47 +08:00
|
|
|
});
|
|
|
|
|
})
|
|
|
|
|
};
|
|
|
|
|
|
2026-04-01 14:03:49 +08:00
|
|
|
let handle_confirm = Callback::from(move |_| {
|
|
|
|
|
let on_changed = on_changed.clone();
|
2026-04-01 11:35:47 +08:00
|
|
|
wasm_bindgen_futures::spawn_local(async move {
|
2026-04-01 14:03:49 +08:00
|
|
|
let on_changed = on_changed.clone();
|
|
|
|
|
Request::delete(&format!("{}/{}",API_BASE, id))
|
2026-04-01 11:35:47 +08:00
|
|
|
.body("")
|
|
|
|
|
.unwrap()
|
|
|
|
|
.send()
|
|
|
|
|
.await
|
|
|
|
|
.unwrap();
|
2026-04-01 14:03:49 +08:00
|
|
|
on_changed.emit(());
|
2026-04-01 11:35:47 +08:00
|
|
|
})
|
|
|
|
|
});
|
2026-04-01 14:03:49 +08:00
|
|
|
|
|
|
|
|
let handle_delete: Callback<MouseEvent> = {
|
|
|
|
|
let dialog_ref = dialog_ref.clone();
|
|
|
|
|
Callback::from(move |_e| {
|
|
|
|
|
let d = dialog_ref.cast::<HtmlDialogElement>().unwrap();
|
|
|
|
|
d.show_modal().unwrap();
|
|
|
|
|
})
|
|
|
|
|
};
|
|
|
|
|
|
2026-04-01 11:35:47 +08:00
|
|
|
html!{
|
|
|
|
|
<tr>
|
|
|
|
|
<td>
|
|
|
|
|
<input type="text"
|
|
|
|
|
class="input"
|
|
|
|
|
value={(*did_number).to_string()}
|
|
|
|
|
id="did_number"
|
|
|
|
|
oninput={handle_did_input.clone()}
|
|
|
|
|
/>
|
|
|
|
|
</td>
|
|
|
|
|
<td>
|
|
|
|
|
<select class="select select-ghost" onchange={handle_target_type_change}>
|
|
|
|
|
<option selected={props.target_type == "Url"}>{"Url"}</option>
|
|
|
|
|
<option selected={props.target_type == "Moh"}>{"Moh"}</option>
|
|
|
|
|
<option selected={props.target_type == "NoService"}>{"NoService"}</option>
|
|
|
|
|
<option selected={props.target_type == "NightMode"}>{"NightMode"}</option>
|
|
|
|
|
<option selected={props.target_type == "ExternalNumber"}>{"ExternalNumber"}</option>
|
|
|
|
|
<option selected={props.target_type == "CustomMessage"}>{"CustomMessage"}</option>
|
|
|
|
|
</select>
|
|
|
|
|
</td>
|
|
|
|
|
<td>
|
|
|
|
|
<input type="text"
|
|
|
|
|
class="input"
|
|
|
|
|
id="target"
|
|
|
|
|
value={(*target).clone()}
|
|
|
|
|
oninput={handle_target_input.clone()}
|
|
|
|
|
/>
|
|
|
|
|
</td>
|
|
|
|
|
<td>
|
|
|
|
|
<input type="checkbox"
|
|
|
|
|
checked={*active}
|
|
|
|
|
class="toggle"
|
|
|
|
|
id="active"
|
|
|
|
|
onchange={handle_active_change.clone()}
|
|
|
|
|
/>
|
|
|
|
|
</td>
|
|
|
|
|
<td class="flex justify-end">
|
|
|
|
|
if *changed && id != 0 {
|
|
|
|
|
<button class="btn btn-primary btn-outline btn-circle" onclick={handle_save.clone()}>
|
|
|
|
|
<Icon
|
|
|
|
|
data={IconData::LUCIDE_SAVE}
|
|
|
|
|
/>
|
|
|
|
|
</button>
|
|
|
|
|
}
|
|
|
|
|
if id != 0 {
|
|
|
|
|
<button class="btn btn-warning btn-outline btn-circle ml-2" onclick={handle_delete}>
|
|
|
|
|
<Icon
|
|
|
|
|
data={IconData::LUCIDE_TRASH_2}
|
|
|
|
|
/>
|
|
|
|
|
</button>
|
|
|
|
|
}
|
|
|
|
|
if id == 0 {
|
|
|
|
|
<button class="btn btn-primary btn-outline btn-circle ml-2" onclick={handle_save}>
|
|
|
|
|
<Icon
|
|
|
|
|
data={IconData::LUCIDE_PLUS}
|
|
|
|
|
/>
|
|
|
|
|
</button>
|
|
|
|
|
}
|
|
|
|
|
</td>
|
2026-04-01 14:03:49 +08:00
|
|
|
<dialog ref={dialog_ref} class="modal">
|
|
|
|
|
<div class="modal-box">
|
|
|
|
|
<h3 class="font-bold text-lg">{"Warning"}</h3>
|
|
|
|
|
<p class="py-4">{format!("About to delete DID: {}", *did_number)}</p>
|
|
|
|
|
<div class="modal-action">
|
|
|
|
|
<form method="dialog">
|
|
|
|
|
<button onclick={handle_confirm} class="btn btn-warning mr-2">{"Yes"}</button>
|
|
|
|
|
<button class="btn">{"No"}</button>
|
|
|
|
|
</form>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</dialog>
|
2026-04-01 11:35:47 +08:00
|
|
|
</tr>
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[component]
|
|
|
|
|
pub fn DidListComponent() -> Html {
|
2026-04-01 14:03:49 +08:00
|
|
|
let changed = use_state(||0);
|
|
|
|
|
let trigger = use_force_update();
|
2026-04-01 11:35:47 +08:00
|
|
|
let dids: UseStateHandle<Vec<Did>> = use_state(||vec![]);
|
|
|
|
|
{
|
2026-04-01 14:03:49 +08:00
|
|
|
let changed = changed.clone();
|
2026-04-01 11:35:47 +08:00
|
|
|
let dids = dids.clone();
|
2026-04-01 14:03:49 +08:00
|
|
|
use_effect_with(changed, move |_| {
|
2026-04-01 11:35:47 +08:00
|
|
|
let dids = dids.clone();
|
|
|
|
|
wasm_bindgen_futures::spawn_local(async move {
|
|
|
|
|
let res = Request::get(API_BASE)
|
|
|
|
|
.send()
|
|
|
|
|
.await
|
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
|
|
let dids_fetched: Vec<Did> = res.json().await.unwrap();
|
|
|
|
|
dids.set(dids_fetched);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|
2026-04-01 14:03:49 +08:00
|
|
|
let handle_change = Callback::from(move |()| {
|
|
|
|
|
let count = *changed + 1;
|
|
|
|
|
gloo_console::log!(count);
|
|
|
|
|
changed.set(count);
|
|
|
|
|
trigger.force_update();
|
|
|
|
|
web_sys::window().unwrap().location().reload().unwrap();
|
|
|
|
|
});
|
|
|
|
|
|
2026-04-01 11:35:47 +08:00
|
|
|
html!{
|
|
|
|
|
<div>
|
|
|
|
|
<table class="table">
|
|
|
|
|
<DidComponent
|
|
|
|
|
did_id={0}
|
|
|
|
|
did_number={""}
|
2026-04-01 14:03:49 +08:00
|
|
|
target_type={"NoService"}
|
2026-04-01 11:35:47 +08:00
|
|
|
target={""}
|
|
|
|
|
active=true
|
2026-04-01 14:03:49 +08:00
|
|
|
on_changed={handle_change.clone()}
|
2026-04-01 11:35:47 +08:00
|
|
|
/>
|
|
|
|
|
</table>
|
|
|
|
|
<div class="divider"></div>
|
|
|
|
|
<table class="table table-zebra">
|
|
|
|
|
<thead>
|
|
|
|
|
<tr>
|
|
|
|
|
<th>{"DID number"}</th>
|
|
|
|
|
<th>{"Target type"}</th>
|
|
|
|
|
<th>{"Target"}</th>
|
|
|
|
|
<th>{"Active"}</th>
|
|
|
|
|
<th></th>
|
|
|
|
|
</tr>
|
|
|
|
|
</thead>
|
|
|
|
|
<tbody>
|
|
|
|
|
{
|
|
|
|
|
dids.iter().map(|d|{
|
|
|
|
|
html!{
|
|
|
|
|
<DidComponent
|
|
|
|
|
did_id={d.id}
|
|
|
|
|
did_number={d.did_number.clone()}
|
|
|
|
|
target_type={d.target_type.clone()}
|
|
|
|
|
target={d.target.clone().unwrap_or_default()}
|
|
|
|
|
active ={d.active}
|
2026-04-01 14:03:49 +08:00
|
|
|
on_changed={handle_change.clone()}
|
2026-04-01 11:35:47 +08:00
|
|
|
/>
|
|
|
|
|
}
|
|
|
|
|
}).collect::<Html>()
|
|
|
|
|
}
|
|
|
|
|
</tbody>
|
|
|
|
|
</table>
|
|
|
|
|
</div>
|
|
|
|
|
}
|
|
|
|
|
}
|