add alternative link
This commit is contained in:
@@ -0,0 +1 @@
|
|||||||
|
ALTER TABLE links DROP COLUMN alternate_link;
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
ALTER TABLE links ADD COLUMN alternate_link TEXT NOT NULL DEFAULT '';
|
||||||
@@ -6,6 +6,7 @@ pub struct Link {
|
|||||||
pub link: String,
|
pub link: String,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub icon: String,
|
pub icon: String,
|
||||||
|
pub alternate_link: String,
|
||||||
position: i64,
|
position: i64,
|
||||||
created_at: String,
|
created_at: String,
|
||||||
}
|
}
|
||||||
@@ -16,12 +17,14 @@ impl Link {
|
|||||||
name: String,
|
name: String,
|
||||||
link: String,
|
link: String,
|
||||||
icon: String,
|
icon: String,
|
||||||
|
alternate_link: String,
|
||||||
) -> Result<sqlx::mysql::MySqlQueryResult, sqlx::Error> {
|
) -> Result<sqlx::mysql::MySqlQueryResult, sqlx::Error> {
|
||||||
sqlx::query!(
|
sqlx::query!(
|
||||||
"INSERT INTO links (name, link, icon, position, created_at) VALUES (?, ?, ?, (SELECT COALESCE(MAX(position) + 1, 1) FROM links lin), ?)",
|
"INSERT INTO links (name, link, icon, alternate_link, position, created_at) VALUES (?, ?, ?, ?, (SELECT COALESCE(MAX(position) + 1, 1) FROM links lin), ?)",
|
||||||
name,
|
name,
|
||||||
link,
|
link,
|
||||||
icon,
|
icon,
|
||||||
|
alternate_link,
|
||||||
chrono::Local::now().naive_local(),
|
chrono::Local::now().naive_local(),
|
||||||
)
|
)
|
||||||
.execute(crate::database::get_db())
|
.execute(crate::database::get_db())
|
||||||
@@ -30,17 +33,20 @@ impl Link {
|
|||||||
|
|
||||||
#[cfg(feature = "ssr")]
|
#[cfg(feature = "ssr")]
|
||||||
pub async fn get_all() -> Result<Vec<Self>, sqlx::Error> {
|
pub async fn get_all() -> Result<Vec<Self>, sqlx::Error> {
|
||||||
sqlx::query!("SELECT id, name, link, icon, position, created_at FROM links ORDER BY position")
|
sqlx::query!(
|
||||||
.map(|x| Self {
|
"SELECT id, name, link, icon, alternate_link, position, created_at FROM links ORDER BY position"
|
||||||
id: x.id,
|
)
|
||||||
name: x.name,
|
.map(|x| Self {
|
||||||
link: x.link,
|
id: x.id,
|
||||||
icon: x.icon,
|
name: x.name,
|
||||||
position: x.position,
|
link: x.link,
|
||||||
created_at: x.created_at.format("%d/%m/%Y %H:%M").to_string(),
|
icon: x.icon,
|
||||||
})
|
alternate_link: x.alternate_link,
|
||||||
.fetch_all(crate::database::get_db())
|
position: x.position,
|
||||||
.await
|
created_at: x.created_at.format("%d/%m/%Y %H:%M").to_string(),
|
||||||
|
})
|
||||||
|
.fetch_all(crate::database::get_db())
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "ssr")]
|
#[cfg(feature = "ssr")]
|
||||||
@@ -49,7 +55,7 @@ impl Link {
|
|||||||
direction: String,
|
direction: String,
|
||||||
) -> Result<sqlx::mysql::MySqlQueryResult, sqlx::Error> {
|
) -> Result<sqlx::mysql::MySqlQueryResult, sqlx::Error> {
|
||||||
let link = sqlx::query!(
|
let link = sqlx::query!(
|
||||||
"SELECT id, name, link, icon, position, created_at FROM links WHERE id = ?",
|
"SELECT id, name, link, icon, alternate_link, position, created_at FROM links WHERE id = ?",
|
||||||
link_id
|
link_id
|
||||||
)
|
)
|
||||||
.map(|x| Self {
|
.map(|x| Self {
|
||||||
@@ -57,6 +63,7 @@ impl Link {
|
|||||||
name: x.name,
|
name: x.name,
|
||||||
link: x.link,
|
link: x.link,
|
||||||
icon: x.icon,
|
icon: x.icon,
|
||||||
|
alternate_link: x.alternate_link,
|
||||||
position: x.position,
|
position: x.position,
|
||||||
created_at: x.created_at.format("%d/%m/%Y %H:%M").to_string(),
|
created_at: x.created_at.format("%d/%m/%Y %H:%M").to_string(),
|
||||||
})
|
})
|
||||||
@@ -100,16 +107,18 @@ impl Link {
|
|||||||
name: String,
|
name: String,
|
||||||
link: String,
|
link: String,
|
||||||
icon: String,
|
icon: String,
|
||||||
|
alternate_link: String,
|
||||||
) -> Result<sqlx::mysql::MySqlQueryResult, sqlx::Error> {
|
) -> Result<sqlx::mysql::MySqlQueryResult, sqlx::Error> {
|
||||||
sqlx::query!(
|
sqlx::query!(
|
||||||
"UPDATE links SET name = ?, link = ?, icon = ? WHERE id = ?",
|
"UPDATE links SET name = ?, link = ?, icon = ?, alternate_link = ? WHERE id = ?",
|
||||||
name,
|
name,
|
||||||
link,
|
link,
|
||||||
icon,
|
icon,
|
||||||
|
alternate_link,
|
||||||
id
|
id
|
||||||
)
|
)
|
||||||
.execute(crate::database::get_db())
|
.execute(crate::database::get_db())
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "ssr")]
|
#[cfg(feature = "ssr")]
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ pub async fn get_links() -> Result<Vec<crate::models::Link>, ServerFnError> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[server(LinkAction, "/api")]
|
#[server(LinkAction, "/api")]
|
||||||
pub async fn add_value(name: String, link: String, icon: String) -> Result<(), ServerFnError> {
|
pub async fn add_value(name: String, link: String, icon: String, alternate_link: String) -> Result<(), ServerFnError> {
|
||||||
crate::models::Link::insert(name, link, icon)
|
crate::models::Link::insert(name, link, icon, alternate_link)
|
||||||
.await
|
.await
|
||||||
.map(|_| ())
|
.map(|_| ())
|
||||||
.map_err(|x| {
|
.map_err(|x| {
|
||||||
@@ -29,6 +29,7 @@ pub fn Links() -> impl IntoView {
|
|||||||
let (show_form, set_show_form) = create_signal(false);
|
let (show_form, set_show_form) = create_signal(false);
|
||||||
let (edit_link, set_edit_link) = create_signal::<Option<crate::models::Link>>(None);
|
let (edit_link, set_edit_link) = create_signal::<Option<crate::models::Link>>(None);
|
||||||
let (edit, set_edit) = create_signal(false);
|
let (edit, set_edit) = create_signal(false);
|
||||||
|
let (use_alternate, set_use_alternate) = create_signal(false);
|
||||||
|
|
||||||
let link_action = create_server_action::<LinkAction>();
|
let link_action = create_server_action::<LinkAction>();
|
||||||
let update_action = create_server_action::<UpdateLinkAction>();
|
let update_action = create_server_action::<UpdateLinkAction>();
|
||||||
@@ -59,7 +60,7 @@ pub fn Links() -> impl IntoView {
|
|||||||
children=move |(_, link)| {
|
children=move |(_, link)| {
|
||||||
let link = create_rw_signal(link);
|
let link = create_rw_signal(link);
|
||||||
view!{
|
view!{
|
||||||
<Link link edit set_edit_link set_show_form links />
|
<Link link edit use_alternate set_edit_link set_show_form links />
|
||||||
}
|
}
|
||||||
}/>
|
}/>
|
||||||
}
|
}
|
||||||
@@ -69,6 +70,13 @@ pub fn Links() -> impl IntoView {
|
|||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<div class="flex justify-end gap-5 mx-5">
|
<div class="flex justify-end gap-5 mx-5">
|
||||||
|
<label class="flex items-center gap-2 cursor-pointer select-none">
|
||||||
|
<input type="checkbox"
|
||||||
|
prop:checked=move || use_alternate.get()
|
||||||
|
on:change=move |ev| set_use_alternate.set(event_target_checked(&ev))
|
||||||
|
class="accent-third size-4 cursor-pointer" />
|
||||||
|
"Lien alternatif"
|
||||||
|
</label>
|
||||||
<button on:click=move |_| {set_edit.set(!edit.get())} class="bg-prim-light hover:bg-prim-lightest rounded-full px-5 py-3 text-second-dark hover:text-third transition-colors">"Modifier"</button>
|
<button on:click=move |_| {set_edit.set(!edit.get())} class="bg-prim-light hover:bg-prim-lightest rounded-full px-5 py-3 text-second-dark hover:text-third transition-colors">"Modifier"</button>
|
||||||
<button on:click=move |_| {
|
<button on:click=move |_| {
|
||||||
set_edit_link.set(None);
|
set_edit_link.set(None);
|
||||||
@@ -105,6 +113,7 @@ pub async fn change_position(link_id: String, direction: String) -> Result<(), S
|
|||||||
fn Link<T: 'static + Clone, S: 'static>(
|
fn Link<T: 'static + Clone, S: 'static>(
|
||||||
link: RwSignal<crate::models::Link>,
|
link: RwSignal<crate::models::Link>,
|
||||||
edit: ReadSignal<bool>,
|
edit: ReadSignal<bool>,
|
||||||
|
use_alternate: ReadSignal<bool>,
|
||||||
set_edit_link: WriteSignal<Option<crate::models::Link>>,
|
set_edit_link: WriteSignal<Option<crate::models::Link>>,
|
||||||
set_show_form: WriteSignal<bool>,
|
set_show_form: WriteSignal<bool>,
|
||||||
links: Resource<T, S>,
|
links: Resource<T, S>,
|
||||||
@@ -113,7 +122,7 @@ fn Link<T: 'static + Clone, S: 'static>(
|
|||||||
<li class="w-44 lg:w-fit">
|
<li class="w-44 lg:w-fit">
|
||||||
<a class="bg-prim-light container flex flex-col lg:gap-2 lg:flex-row w-full item-center hover:scale-110 transition hover:bg-prim-lightest border-b hover:border-third border-transparent rounded-lg text-center hover:text-third px-5 py-4"
|
<a class="bg-prim-light container flex flex-col lg:gap-2 lg:flex-row w-full item-center hover:scale-110 transition hover:bg-prim-lightest border-b hover:border-third border-transparent rounded-lg text-center hover:text-third px-5 py-4"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
href={move || link.with(|x| x.link.to_string())}>
|
href={move || link.with(|x| if use_alternate.get() { x.alternate_link.clone() } else { x.link.clone() })}>
|
||||||
<img src={move || link.with(|x| x.icon.to_string())}
|
<img src={move || link.with(|x| x.icon.to_string())}
|
||||||
alt={move || link.with(|x| x.name.to_string())}
|
alt={move || link.with(|x| x.name.to_string())}
|
||||||
class="size-32 lg:size-10 object-cover overflow-hidden" />
|
class="size-32 lg:size-10 object-cover overflow-hidden" />
|
||||||
@@ -194,10 +203,15 @@ fn DeleteButton<T: 'static + Clone, S: 'static>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[server(UpdateLinkAction, "/api")]
|
#[server(UpdateLinkAction, "/api")]
|
||||||
pub async fn update_link(id: String, name: String, link: String, icon: String) -> Result<(), ServerFnError> {
|
pub async fn update_link(
|
||||||
crate::models::Link::update(id, name, link, icon)
|
id: String,
|
||||||
|
name: String,
|
||||||
|
link: String,
|
||||||
|
icon: String,
|
||||||
|
alternate_link: String,
|
||||||
|
) -> Result<(), ServerFnError> {
|
||||||
|
crate::models::Link::update(id, name, link, icon, alternate_link)
|
||||||
.await
|
.await
|
||||||
.map(|_| ())
|
.map(|_| ())
|
||||||
.map_err(|x| {
|
.map_err(|x| {
|
||||||
@@ -216,8 +230,11 @@ fn LinkFormModal(
|
|||||||
) -> impl IntoView {
|
) -> impl IntoView {
|
||||||
let is_edit = link.is_some();
|
let is_edit = link.is_some();
|
||||||
let (name, set_name) = create_signal(link.as_ref().map(|x| x.name.clone()).unwrap_or_default());
|
let (name, set_name) = create_signal(link.as_ref().map(|x| x.name.clone()).unwrap_or_default());
|
||||||
let (link_url, set_link_url) = create_signal(link.as_ref().map(|x| x.link.clone()).unwrap_or_default());
|
let (link_url, set_link_url) =
|
||||||
|
create_signal(link.as_ref().map(|x| x.link.clone()).unwrap_or_default());
|
||||||
let (icon, set_icon) = create_signal(link.as_ref().map(|x| x.icon.clone()).unwrap_or_default());
|
let (icon, set_icon) = create_signal(link.as_ref().map(|x| x.icon.clone()).unwrap_or_default());
|
||||||
|
let (alternate_link, set_alternate_link) =
|
||||||
|
create_signal(link.as_ref().map(|x| x.alternate_link.clone()).unwrap_or_default());
|
||||||
let link_id = link.as_ref().map(|x| x.id.to_string());
|
let link_id = link.as_ref().map(|x| x.id.to_string());
|
||||||
|
|
||||||
view! {
|
view! {
|
||||||
@@ -238,6 +255,8 @@ fn LinkFormModal(
|
|||||||
set_link_url=set_link_url
|
set_link_url=set_link_url
|
||||||
icon=icon
|
icon=icon
|
||||||
set_icon=set_icon
|
set_icon=set_icon
|
||||||
|
alternate_link=alternate_link
|
||||||
|
set_alternate_link=set_alternate_link
|
||||||
/>
|
/>
|
||||||
<FormButtons set_show=set_show />
|
<FormButtons set_show=set_show />
|
||||||
</ActionForm>
|
</ActionForm>
|
||||||
@@ -252,6 +271,8 @@ fn LinkFormModal(
|
|||||||
set_link_url=set_link_url
|
set_link_url=set_link_url
|
||||||
icon=icon
|
icon=icon
|
||||||
set_icon=set_icon
|
set_icon=set_icon
|
||||||
|
alternate_link=alternate_link
|
||||||
|
set_alternate_link=set_alternate_link
|
||||||
/>
|
/>
|
||||||
<FormButtons set_show=set_show />
|
<FormButtons set_show=set_show />
|
||||||
</ActionForm>
|
</ActionForm>
|
||||||
@@ -270,6 +291,8 @@ fn LinkFormFields(
|
|||||||
set_link_url: WriteSignal<String>,
|
set_link_url: WriteSignal<String>,
|
||||||
icon: ReadSignal<String>,
|
icon: ReadSignal<String>,
|
||||||
set_icon: WriteSignal<String>,
|
set_icon: WriteSignal<String>,
|
||||||
|
alternate_link: ReadSignal<String>,
|
||||||
|
set_alternate_link: WriteSignal<String>,
|
||||||
) -> impl IntoView {
|
) -> impl IntoView {
|
||||||
view! {
|
view! {
|
||||||
<div>
|
<div>
|
||||||
@@ -290,6 +313,16 @@ fn LinkFormFields(
|
|||||||
class="text-center bg-prim border border-transparent rounded-lg focus:border-third px-2 py-2 w-full" />
|
class="text-center bg-prim border border-transparent rounded-lg focus:border-third px-2 py-2 w-full" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label class="block mt-3 mb-1">"Lien alternatif"</label>
|
||||||
|
<input type="url"
|
||||||
|
name="alternate_link"
|
||||||
|
placeholder="http..."
|
||||||
|
prop:value=move || alternate_link.get()
|
||||||
|
on:input=move |ev| set_alternate_link.set(event_target_value(&ev))
|
||||||
|
class="text-center bg-prim border border-transparent rounded-lg focus:border-third px-2 py-2 w-full" />
|
||||||
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label class="block mt-3 mb-1">"Icône"</label>
|
<label class="block mt-3 mb-1">"Icône"</label>
|
||||||
<input type="url"
|
<input type="url"
|
||||||
@@ -313,4 +346,4 @@ fn FormButtons(set_show: WriteSignal<bool>) -> impl IntoView {
|
|||||||
class="bg-third hover:bg-third-light rounded-lg transition-colors px-2 py-1 flex-1">"Valider"</button>
|
class="bg-third hover:bg-third-light rounded-lg transition-colors px-2 py-1 flex-1">"Valider"</button>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user