Support API in frontend

This commit is contained in:
2026-04-01 11:35:47 +08:00
parent c8065caf1f
commit 743fe69f99
9 changed files with 464 additions and 23 deletions

267
frontend/src/pages/did.rs Normal file
View File

@@ -0,0 +1,267 @@
use yew::prelude::*;
use web_sys::{Event, HtmlInputElement, HtmlSelectElement};
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,
pub active: bool
}
#[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);
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();
Callback::from(move |_| {
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 {
let req = if id == 0 {
Request::post(&url)
.json(&did)
.unwrap()
} else {
Request::patch(&url)
.json(&did)
.unwrap()
};
req.send()
.await
.unwrap();
});
changed.set(false);
})
};
let handle_delete = Callback::from(move |_| {
wasm_bindgen_futures::spawn_local(async move {
Request::delete(&format!("{}/{}",API_BASE, id))
.body("")
.unwrap()
.send()
.await
.unwrap();
})
});
html!{
<tr>
<input type="text" class="hidden" value={props.did_id.to_string()}
/>
<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>
</tr>
}
}
#[component]
pub fn DidListComponent() -> Html {
let dids: UseStateHandle<Vec<Did>> = use_state(||vec![]);
{
let dids = dids.clone();
use_effect_with((), move |_| {
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);
});
});
}
html!{
<div>
<table class="table">
<DidComponent
did_id={0}
did_number={""}
target_type={""}
target={""}
active=true
/>
</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}
/>
}
}).collect::<Html>()
}
</tbody>
</table>
</div>
}
}