completed add album component, front and backend
This commit is contained in:
parent
64e93649af
commit
3746c370a2
66
src/api/albums.rs
Normal file
66
src/api/albums.rs
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
use leptos::*;
|
||||||
|
|
||||||
|
use cfg_if::cfg_if;
|
||||||
|
|
||||||
|
cfg_if! {
|
||||||
|
if #[cfg(feature = "ssr")] {
|
||||||
|
use crate::database::get_db_conn;
|
||||||
|
use diesel::prelude::*;
|
||||||
|
use time::Date;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add an album to the database
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `album_title` - The name of the artist to add
|
||||||
|
/// * `release_data` - The release date of the album (Optional)
|
||||||
|
/// * `image_path` - The path to the album's image file (Optional)
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// * `Result<(), Box<dyn Error>>` - A empty result if successful, or an error
|
||||||
|
///
|
||||||
|
#[server(endpoint = "albums/add-album")]
|
||||||
|
pub async fn add_album(album_title: String, release_date: Option<String>, image_path: Option<String>) -> Result<(), ServerFnError> {
|
||||||
|
use crate::schema::albums::{self};
|
||||||
|
use crate::models::Album;
|
||||||
|
use leptos::server_fn::error::NoCustomError;
|
||||||
|
|
||||||
|
let date_format = time::macros::format_description!("[year]-[month]-[day]");
|
||||||
|
let parsed_release_date = match release_date {
|
||||||
|
Some(date) => {
|
||||||
|
match Date::parse(&date, &date_format) {
|
||||||
|
Ok(parsed_date) => Some(parsed_date),
|
||||||
|
Err(_e) => return Err(ServerFnError::<NoCustomError>::ServerError("Invalid release date".to_string()))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => None
|
||||||
|
};
|
||||||
|
|
||||||
|
let image_path_arg = match image_path {
|
||||||
|
Some(image_path) => {
|
||||||
|
if image_path.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(image_path)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => None
|
||||||
|
};
|
||||||
|
|
||||||
|
let new_album = Album {
|
||||||
|
id: None,
|
||||||
|
title: album_title,
|
||||||
|
release_date: parsed_release_date,
|
||||||
|
image_path: image_path_arg
|
||||||
|
};
|
||||||
|
|
||||||
|
let db = &mut get_db_conn();
|
||||||
|
diesel::insert_into(albums::table)
|
||||||
|
.values(&new_album)
|
||||||
|
.execute(db)
|
||||||
|
.map_err(|e| ServerFnError::<NoCustomError>::ServerError(format!("Error adding album: {}", e)))?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
@ -1,5 +1,4 @@
|
|||||||
use leptos::*;
|
use leptos::*;
|
||||||
use crate::models::Artist;
|
|
||||||
|
|
||||||
use cfg_if::cfg_if;
|
use cfg_if::cfg_if;
|
||||||
|
|
||||||
@ -22,6 +21,7 @@ cfg_if! {
|
|||||||
#[server(endpoint = "artists/add-artist")]
|
#[server(endpoint = "artists/add-artist")]
|
||||||
pub async fn add_artist(artist_name: String) -> Result<(), ServerFnError> {
|
pub async fn add_artist(artist_name: String) -> Result<(), ServerFnError> {
|
||||||
use crate::schema::artists::dsl::*;
|
use crate::schema::artists::dsl::*;
|
||||||
|
use crate::models::Artist;
|
||||||
use leptos::server_fn::error::NoCustomError;
|
use leptos::server_fn::error::NoCustomError;
|
||||||
|
|
||||||
let new_artist = Artist {
|
let new_artist = Artist {
|
||||||
|
@ -1 +1,2 @@
|
|||||||
pub mod artists;
|
pub mod artists;
|
||||||
|
pub mod albums;
|
@ -64,6 +64,7 @@ use crate::components::search::*;
|
|||||||
use crate::components::personal::*;
|
use crate::components::personal::*;
|
||||||
use crate::components::upload::*;
|
use crate::components::upload::*;
|
||||||
use crate::components::add_artist::AddArtist;
|
use crate::components::add_artist::AddArtist;
|
||||||
|
use crate::components::add_album::AddAlbum;
|
||||||
|
|
||||||
/// Renders the home page of your application.
|
/// Renders the home page of your application.
|
||||||
#[component]
|
#[component]
|
||||||
@ -72,6 +73,7 @@ fn HomePage(play_status: RwSignal<PlayStatus>, upload_open: RwSignal<bool>, add_
|
|||||||
<div class="home-container">
|
<div class="home-container">
|
||||||
<Upload open=upload_open/>
|
<Upload open=upload_open/>
|
||||||
<AddArtist open=add_artist_open/>
|
<AddArtist open=add_artist_open/>
|
||||||
|
<AddAlbum open=add_album_open/>
|
||||||
<Sidebar upload_open=upload_open add_artist_open=add_artist_open add_album_open=add_album_open/>
|
<Sidebar upload_open=upload_open add_artist_open=add_artist_open add_album_open=add_album_open/>
|
||||||
// This <Outlet /> will render the child route components
|
// This <Outlet /> will render the child route components
|
||||||
<Outlet />
|
<Outlet />
|
||||||
|
@ -5,3 +5,4 @@ pub mod personal;
|
|||||||
pub mod upload;
|
pub mod upload;
|
||||||
pub mod upload_dropdown;
|
pub mod upload_dropdown;
|
||||||
pub mod add_artist;
|
pub mod add_artist;
|
||||||
|
pub mod add_album;
|
92
src/components/add_album.rs
Normal file
92
src/components/add_album.rs
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
use leptos::*;
|
||||||
|
use leptos::leptos_dom::log;
|
||||||
|
use leptos_icons::*;
|
||||||
|
use crate::api::albums::add_album;
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
pub fn AddAlbumBtn(add_album_open: RwSignal<bool>) -> impl IntoView {
|
||||||
|
let open_dialog = move |_| {
|
||||||
|
add_album_open.set(true);
|
||||||
|
};
|
||||||
|
view! {
|
||||||
|
<button class="add-album-btn add-btns" on:click=open_dialog>
|
||||||
|
Add Album
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[component]
|
||||||
|
pub fn AddAlbum(open: RwSignal<bool>) -> impl IntoView {
|
||||||
|
let album_title = create_rw_signal("".to_string());
|
||||||
|
let release_date = create_rw_signal("".to_string());
|
||||||
|
let image_path = create_rw_signal("".to_string());
|
||||||
|
|
||||||
|
let close_dialog = move |ev: leptos::ev::MouseEvent| {
|
||||||
|
ev.prevent_default();
|
||||||
|
open.set(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
let on_add_album = move |ev: leptos::ev::SubmitEvent| {
|
||||||
|
ev.prevent_default();
|
||||||
|
let album_title_clone = album_title.get();
|
||||||
|
let release_date_clone = Some(release_date.get());
|
||||||
|
let image_path_clone = Some(image_path.get());
|
||||||
|
|
||||||
|
spawn_local(async move {
|
||||||
|
let add_album_result = add_album(album_title_clone, release_date_clone, image_path_clone).await;
|
||||||
|
if let Err(err) = add_album_result {
|
||||||
|
log!("Error adding album: {:?}", err);
|
||||||
|
} else if let Ok(album) = add_album_result {
|
||||||
|
log!("Added album: {:?}", album);
|
||||||
|
album_title.set("".to_string());
|
||||||
|
release_date.set("".to_string());
|
||||||
|
image_path.set("".to_string());
|
||||||
|
}
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
view! {
|
||||||
|
<Show when=open fallback=move|| view!{}>
|
||||||
|
<div class="add-album-container">
|
||||||
|
<div class="upload-header">
|
||||||
|
<h1>Add Album</h1>
|
||||||
|
</div>
|
||||||
|
<div class="close-button" on:click=close_dialog><Icon icon=icondata::IoClose /></div>
|
||||||
|
<form class="create-album-form" action="POST" on:submit=on_add_album>
|
||||||
|
<div class="input-bx">
|
||||||
|
<input type="text" required class="text-input"
|
||||||
|
prop:value=album_title
|
||||||
|
on:input=move |ev: leptos::ev::Event| {
|
||||||
|
album_title.set(event_target_value(&ev));
|
||||||
|
}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
<span>Album Title</span>
|
||||||
|
</div>
|
||||||
|
<div class="release-date">
|
||||||
|
<div class="left">
|
||||||
|
<span>Release</span>
|
||||||
|
<span>Date</span>
|
||||||
|
</div>
|
||||||
|
<input class="info" type="date"
|
||||||
|
prop:value=release_date
|
||||||
|
on:input=move |ev: leptos::ev::Event| {
|
||||||
|
release_date.set(event_target_value(&ev));
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="input-bx">
|
||||||
|
<input type="text" class="text-input"
|
||||||
|
prop:value=image_path
|
||||||
|
on:input=move |ev: leptos::ev::Event| {
|
||||||
|
image_path.set(event_target_value(&ev));
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<span>Image Path</span>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="upload-button">Add</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</Show>
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -2,6 +2,7 @@ use leptos::*;
|
|||||||
use leptos_icons::*;
|
use leptos_icons::*;
|
||||||
use crate::components::upload::*;
|
use crate::components::upload::*;
|
||||||
use crate::components::add_artist::*;
|
use crate::components::add_artist::*;
|
||||||
|
use crate::components::add_album::*;
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
pub fn UploadDropdownBtn(dropdown_open: RwSignal<bool>) -> impl IntoView {
|
pub fn UploadDropdownBtn(dropdown_open: RwSignal<bool>) -> impl IntoView {
|
||||||
@ -23,6 +24,7 @@ pub fn UploadDropdown(upload_open: RwSignal<bool>, add_artist_open: RwSignal<boo
|
|||||||
<div class="upload-dropdown">
|
<div class="upload-dropdown">
|
||||||
<UploadBtn dialog_open=upload_open/>
|
<UploadBtn dialog_open=upload_open/>
|
||||||
<AddArtistBtn add_artist_open=add_artist_open/>
|
<AddArtistBtn add_artist_open=add_artist_open/>
|
||||||
|
<AddAlbumBtn add_album_open=add_album_open/>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
}
|
}
|
132
style/addAlbum.scss
Normal file
132
style/addAlbum.scss
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
@import "theme.scss";
|
||||||
|
|
||||||
|
.add-album-container {
|
||||||
|
position: fixed;
|
||||||
|
top: 45%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
width: 30rem;
|
||||||
|
height: 24rem;
|
||||||
|
border: 1px solid white;
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: 1rem;
|
||||||
|
padding-top: 0;
|
||||||
|
z-index: 2;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
background-color: #1c1c1c;
|
||||||
|
.upload-header {
|
||||||
|
font-size: .7rem;
|
||||||
|
font-weight: 300;
|
||||||
|
padding-bottom: 0;
|
||||||
|
border-bottom: 1px solid white;
|
||||||
|
font-family: "Roboto", sans-serif;
|
||||||
|
}
|
||||||
|
.close-button {
|
||||||
|
position: absolute;
|
||||||
|
top: 5px;
|
||||||
|
right: 5px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: 50%;
|
||||||
|
font-size: 1.6rem;
|
||||||
|
transition: all 0.3s;
|
||||||
|
border: none;
|
||||||
|
|
||||||
|
}
|
||||||
|
.close-button:hover {
|
||||||
|
transform: scale(1.1);
|
||||||
|
background-color: rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
.close-button:active {
|
||||||
|
transform: scale(0.8);
|
||||||
|
}
|
||||||
|
.create-album-form {
|
||||||
|
width:100%;
|
||||||
|
height: 100%;
|
||||||
|
position: relative;
|
||||||
|
.input-bx{
|
||||||
|
margin-top: 1rem;
|
||||||
|
width: 300px;
|
||||||
|
input{
|
||||||
|
width: 100%;
|
||||||
|
padding: 10px;
|
||||||
|
border: 2px solid #7f8fa6;
|
||||||
|
border-radius: 5px;
|
||||||
|
outline: none;
|
||||||
|
font-size: 1rem;
|
||||||
|
transition: 0.6s;
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
span{
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
padding: 10px;
|
||||||
|
font-size: 1rem;
|
||||||
|
color: #7f8fa6;
|
||||||
|
text-transform: uppercase;
|
||||||
|
pointer-events: none;
|
||||||
|
transition: 0.6s;
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
input:valid ~ span,
|
||||||
|
input:focus ~ span{
|
||||||
|
color: #fff;
|
||||||
|
transform: translateX(10px) translateY(-7px);
|
||||||
|
font-size: 0.65rem;
|
||||||
|
font-weight: 600;
|
||||||
|
padding: 0 10px;
|
||||||
|
background: #1c1c1c;
|
||||||
|
letter-spacing: 0.1rem;
|
||||||
|
}
|
||||||
|
input:valid,
|
||||||
|
input:focus{
|
||||||
|
color: #fff;
|
||||||
|
border: 2px solid #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
.release-date {
|
||||||
|
margin-top: 1rem;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
color: #7f8fa6;
|
||||||
|
font-family: "Roboto", sans-serif;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
.left {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
margin-left: 5px;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
span {
|
||||||
|
font-size: .85rem;
|
||||||
|
}
|
||||||
|
input {
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.upload-button {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 5px;
|
||||||
|
margin-top: 1rem;
|
||||||
|
padding: 10px;
|
||||||
|
background-color: #7f8fa6;
|
||||||
|
color: #fff;
|
||||||
|
width: 100%;
|
||||||
|
font-size: 1rem;
|
||||||
|
font-family: "Roboto", sans-serif;
|
||||||
|
border: none;
|
||||||
|
border-radius: 5px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: 0.3s;
|
||||||
|
&:hover {
|
||||||
|
background-color: #fff;
|
||||||
|
color: #7f8fa6;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -10,6 +10,7 @@
|
|||||||
@import 'personal.scss';
|
@import 'personal.scss';
|
||||||
@import 'upload.scss';
|
@import 'upload.scss';
|
||||||
@import 'addArtist.scss';
|
@import 'addArtist.scss';
|
||||||
|
@import 'addAlbum.scss';
|
||||||
|
|
||||||
body {
|
body {
|
||||||
font-family: sans-serif;
|
font-family: sans-serif;
|
||||||
|
@ -91,6 +91,41 @@
|
|||||||
border: 2px solid #fff;
|
border: 2px solid #fff;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.release-date {
|
||||||
|
margin-top: 1rem;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
color: #7f8fa6;
|
||||||
|
font-family: "Roboto", sans-serif;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
.left {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
margin-left: 5px;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
span {
|
||||||
|
font-size: .85rem;
|
||||||
|
}
|
||||||
|
input {
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.file {
|
||||||
|
margin-top: .5rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
span {
|
||||||
|
font-size: .9rem;
|
||||||
|
color: #7f8fa6;
|
||||||
|
font-family: "Roboto", sans-serif;
|
||||||
|
margin-left: 5px;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
input {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
.upload-button {
|
.upload-button {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 5px;
|
bottom: 5px;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user