Compare commits
10 Commits
main
...
45-impleme
Author | SHA1 | Date | |
---|---|---|---|
6112e0dfac | |||
af604a9ddc | |||
bcb24c2a97 | |||
6676f2c533 | |||
3746c370a2 | |||
64e93649af | |||
fcc5870824 | |||
3ce762ce5b | |||
1ecd13d65f | |||
be775862f9 |
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(())
|
||||||
|
}
|
39
src/api/artists.rs
Normal file
39
src/api/artists.rs
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
use leptos::*;
|
||||||
|
|
||||||
|
use cfg_if::cfg_if;
|
||||||
|
|
||||||
|
cfg_if! {
|
||||||
|
if #[cfg(feature = "ssr")] {
|
||||||
|
use crate::database::get_db_conn;
|
||||||
|
use diesel::prelude::*;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add an artist to the database
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `artist_name` - The name of the artist to add
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// * `Result<(), Box<dyn Error>>` - A empty result if successful, or an error
|
||||||
|
///
|
||||||
|
#[server(endpoint = "artists/add-artist")]
|
||||||
|
pub async fn add_artist(artist_name: String) -> Result<(), ServerFnError> {
|
||||||
|
use crate::schema::artists::dsl::*;
|
||||||
|
use crate::models::Artist;
|
||||||
|
use leptos::server_fn::error::NoCustomError;
|
||||||
|
|
||||||
|
let new_artist = Artist {
|
||||||
|
id: None,
|
||||||
|
name: artist_name,
|
||||||
|
};
|
||||||
|
|
||||||
|
let db = &mut get_db_conn();
|
||||||
|
diesel::insert_into(artists)
|
||||||
|
.values(&new_artist)
|
||||||
|
.execute(db)
|
||||||
|
.map_err(|e| ServerFnError::<NoCustomError>::ServerError(format!("Error adding artist: {}", e)))?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
2
src/api/mod.rs
Normal file
2
src/api/mod.rs
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
pub mod artists;
|
||||||
|
pub mod albums;
|
18
src/app.rs
18
src/app.rs
@ -17,6 +17,8 @@ pub fn App() -> impl IntoView {
|
|||||||
let play_status = PlayStatus::default();
|
let play_status = PlayStatus::default();
|
||||||
let play_status = create_rw_signal(play_status);
|
let play_status = create_rw_signal(play_status);
|
||||||
let upload_open = create_rw_signal(false);
|
let upload_open = create_rw_signal(false);
|
||||||
|
let add_artist_open = create_rw_signal(false);
|
||||||
|
let add_album_open = create_rw_signal(false);
|
||||||
|
|
||||||
view! {
|
view! {
|
||||||
// injects a stylesheet into the document <head>
|
// injects a stylesheet into the document <head>
|
||||||
@ -37,7 +39,13 @@ pub fn App() -> impl IntoView {
|
|||||||
}>
|
}>
|
||||||
<main>
|
<main>
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path="" view=move || view! { <HomePage play_status=play_status upload_open=upload_open/> }>
|
<Route path="" view=move ||
|
||||||
|
view! { <HomePage play_status=play_status
|
||||||
|
upload_open=upload_open
|
||||||
|
add_artist_open=add_artist_open
|
||||||
|
add_album_open=add_album_open
|
||||||
|
/>
|
||||||
|
}>
|
||||||
<Route path="" view=Dashboard />
|
<Route path="" view=Dashboard />
|
||||||
<Route path="dashboard" view=Dashboard />
|
<Route path="dashboard" view=Dashboard />
|
||||||
<Route path="search" view=Search />
|
<Route path="search" view=Search />
|
||||||
@ -55,14 +63,18 @@ use crate::components::dashboard::*;
|
|||||||
use crate::components::search::*;
|
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_album::AddAlbum;
|
||||||
|
|
||||||
/// Renders the home page of your application.
|
/// Renders the home page of your application.
|
||||||
#[component]
|
#[component]
|
||||||
fn HomePage(play_status: RwSignal<PlayStatus>, upload_open: RwSignal<bool>) -> impl IntoView {
|
fn HomePage(play_status: RwSignal<PlayStatus>, upload_open: RwSignal<bool>, add_artist_open: RwSignal<bool>, add_album_open: RwSignal<bool>) -> impl IntoView {
|
||||||
view! {
|
view! {
|
||||||
<div class="home-container">
|
<div class="home-container">
|
||||||
<Upload open=upload_open/>
|
<Upload open=upload_open/>
|
||||||
<Sidebar upload_open=upload_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/>
|
||||||
// This <Outlet /> will render the child route components
|
// This <Outlet /> will render the child route components
|
||||||
<Outlet />
|
<Outlet />
|
||||||
<Personal />
|
<Personal />
|
||||||
|
@ -3,3 +3,6 @@ pub mod dashboard;
|
|||||||
pub mod search;
|
pub mod search;
|
||||||
pub mod personal;
|
pub mod personal;
|
||||||
pub mod upload;
|
pub mod upload;
|
||||||
|
pub mod upload_dropdown;
|
||||||
|
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>
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
62
src/components/add_artist.rs
Normal file
62
src/components/add_artist.rs
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
use leptos::*;
|
||||||
|
use leptos::leptos_dom::log;
|
||||||
|
use leptos_icons::*;
|
||||||
|
use crate::api::artists::add_artist;
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
pub fn AddArtistBtn(add_artist_open: RwSignal<bool>) -> impl IntoView {
|
||||||
|
let open_dialog = move |_| {
|
||||||
|
add_artist_open.set(true);
|
||||||
|
};
|
||||||
|
view! {
|
||||||
|
<button class="add-artist-btn add-btns" on:click=open_dialog>
|
||||||
|
Add Artist
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[component]
|
||||||
|
pub fn AddArtist(open: RwSignal<bool>) -> impl IntoView {
|
||||||
|
let artist_name = create_rw_signal("".to_string());
|
||||||
|
|
||||||
|
let close_dialog = move |ev: leptos::ev::MouseEvent| {
|
||||||
|
ev.prevent_default();
|
||||||
|
open.set(false);
|
||||||
|
};
|
||||||
|
let on_add_artist = move |ev: leptos::ev::SubmitEvent| {
|
||||||
|
ev.prevent_default();
|
||||||
|
let artist_name_clone = artist_name.get();
|
||||||
|
spawn_local(async move {
|
||||||
|
let add_artist_result = add_artist(artist_name_clone).await;
|
||||||
|
if let Err(err) = add_artist_result {
|
||||||
|
log!("Error adding artist: {:?}", err);
|
||||||
|
} else if let Ok(artist) = add_artist_result {
|
||||||
|
log!("Added artist: {:?}", artist);
|
||||||
|
artist_name.set("".to_string());
|
||||||
|
}
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
view! {
|
||||||
|
<Show when=open fallback=move|| view!{}>
|
||||||
|
<div class="add-artist-container">
|
||||||
|
<div class="upload-header">
|
||||||
|
<h1>Add Artist</h1>
|
||||||
|
</div>
|
||||||
|
<div class="close-button" on:click=close_dialog><Icon icon=icondata::IoClose /></div>
|
||||||
|
<form class="create-artist-form" action="POST" on:submit=on_add_artist>
|
||||||
|
<div class="input-bx">
|
||||||
|
<input type="text" name="title" required class="text-input"
|
||||||
|
prop:value=artist_name
|
||||||
|
on:input=move |ev: leptos::ev::Event| {
|
||||||
|
artist_name.set(event_target_value(&ev));
|
||||||
|
}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
<span>Artist Name</span>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="upload-button">Add</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</Show>
|
||||||
|
}
|
||||||
|
}
|
@ -1,13 +1,15 @@
|
|||||||
use leptos::leptos_dom::*;
|
use leptos::leptos_dom::*;
|
||||||
use leptos::*;
|
use leptos::*;
|
||||||
use leptos_icons::*;
|
use leptos_icons::*;
|
||||||
use crate::components::upload::*;
|
use crate::components::upload_dropdown::*;
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
pub fn Sidebar(upload_open: RwSignal<bool>) -> impl IntoView {
|
pub fn Sidebar(upload_open: RwSignal<bool>, add_artist_open: RwSignal<bool>, add_album_open: RwSignal<bool>) -> impl IntoView {
|
||||||
use leptos_router::use_location;
|
use leptos_router::use_location;
|
||||||
let location = use_location();
|
let location = use_location();
|
||||||
|
|
||||||
|
let dropdown_open = create_rw_signal(false);
|
||||||
|
|
||||||
let on_dashboard = Signal::derive(
|
let on_dashboard = Signal::derive(
|
||||||
move || location.pathname.get().starts_with("/dashboard") || location.pathname.get() == "/",
|
move || location.pathname.get().starts_with("/dashboard") || location.pathname.get() == "/",
|
||||||
);
|
);
|
||||||
@ -19,8 +21,26 @@ pub fn Sidebar(upload_open: RwSignal<bool>) -> impl IntoView {
|
|||||||
view! {
|
view! {
|
||||||
<div class="sidebar-container">
|
<div class="sidebar-container">
|
||||||
<div class="sidebar-top-container">
|
<div class="sidebar-top-container">
|
||||||
|
<Show
|
||||||
|
when=move || {upload_open.get() || add_artist_open.get() || add_album_open.get()}
|
||||||
|
fallback=move || view! {}
|
||||||
|
>
|
||||||
|
<div class="upload-overlay" on:click=move |_| {
|
||||||
|
upload_open.set(false);
|
||||||
|
add_artist_open.set(false);
|
||||||
|
add_album_open.set(false);
|
||||||
|
}></div>
|
||||||
|
</Show>
|
||||||
<h2 class="header">LibreTunes</h2>
|
<h2 class="header">LibreTunes</h2>
|
||||||
<UploadBtn dialog_open=upload_open />
|
<div class="upload-dropdown-container">
|
||||||
|
<UploadDropdownBtn dropdown_open=dropdown_open/>
|
||||||
|
<Show
|
||||||
|
when= move || dropdown_open()
|
||||||
|
fallback=move || view! {}
|
||||||
|
>
|
||||||
|
<UploadDropdown dropdown_open=dropdown_open upload_open=upload_open add_artist_open=add_artist_open add_album_open=add_album_open/>
|
||||||
|
</Show>
|
||||||
|
</div>
|
||||||
<a class="buttons" href="/dashboard" style={move || if on_dashboard() {"color: #e1e3e1"} else {""}} >
|
<a class="buttons" href="/dashboard" style={move || if on_dashboard() {"color: #e1e3e1"} else {""}} >
|
||||||
<Icon icon=icondata::OcHomeFillLg />
|
<Icon icon=icondata::OcHomeFillLg />
|
||||||
<h1>Dashboard</h1>
|
<h1>Dashboard</h1>
|
||||||
|
@ -16,11 +16,8 @@ pub fn UploadBtn(dialog_open: RwSignal<bool>) -> impl IntoView {
|
|||||||
};
|
};
|
||||||
|
|
||||||
view! {
|
view! {
|
||||||
<button class="upload-btn" on:click=open_dialog>
|
<button class="upload-btn add-btns" on:click=open_dialog>
|
||||||
<div class="add-sign">
|
Upload Song
|
||||||
<Icon icon=icondata::IoAddSharp />
|
|
||||||
</div>
|
|
||||||
Upload
|
|
||||||
</button>
|
</button>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
30
src/components/upload_dropdown.rs
Normal file
30
src/components/upload_dropdown.rs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
use leptos::*;
|
||||||
|
use leptos_icons::*;
|
||||||
|
use crate::components::upload::*;
|
||||||
|
use crate::components::add_artist::*;
|
||||||
|
use crate::components::add_album::*;
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
pub fn UploadDropdownBtn(dropdown_open: RwSignal<bool>) -> impl IntoView {
|
||||||
|
let open_dropdown = move |_| {
|
||||||
|
dropdown_open.set(!dropdown_open.get());
|
||||||
|
};
|
||||||
|
view! {
|
||||||
|
<button class={move || if dropdown_open() {"upload-dropdown-btn upload-dropdown-btn-active"} else {"upload-dropdown-btn"}} on:click=open_dropdown>
|
||||||
|
<div class="add-sign">
|
||||||
|
<Icon icon=icondata::IoAddSharp />
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
pub fn UploadDropdown(dropdown_open: RwSignal<bool>, upload_open: RwSignal<bool>, add_artist_open: RwSignal<bool>, add_album_open: RwSignal<bool>) -> impl IntoView {
|
||||||
|
view! {
|
||||||
|
<div class="upload-dropdown" on:click=move |_| dropdown_open.set(false)>
|
||||||
|
<UploadBtn dialog_open=upload_open />
|
||||||
|
<AddArtistBtn add_artist_open=add_artist_open/>
|
||||||
|
<AddAlbumBtn add_album_open=add_album_open/>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
@ -15,6 +15,7 @@ pub mod fileserv;
|
|||||||
pub mod error_template;
|
pub mod error_template;
|
||||||
pub mod upload;
|
pub mod upload;
|
||||||
pub mod util;
|
pub mod util;
|
||||||
|
pub mod api;
|
||||||
|
|
||||||
use cfg_if::cfg_if;
|
use cfg_if::cfg_if;
|
||||||
|
|
||||||
|
135
style/addAlbum.scss
Normal file
135
style/addAlbum.scss
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
@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;
|
||||||
|
z-index: 11;
|
||||||
|
.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{
|
||||||
|
position: relative;
|
||||||
|
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;
|
||||||
|
top: 1px;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
115
style/addArtist.scss
Normal file
115
style/addArtist.scss
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
@import "theme.scss";
|
||||||
|
|
||||||
|
.add-artist-container {
|
||||||
|
position: fixed;
|
||||||
|
top: 45%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
width: 30rem;
|
||||||
|
height: 15rem;
|
||||||
|
border: 1px solid white;
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: 1rem;
|
||||||
|
padding-top: 0;
|
||||||
|
z-index: 2;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
background-color: #1c1c1c;
|
||||||
|
z-index: 11;
|
||||||
|
.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-artist-form {
|
||||||
|
width:100%;
|
||||||
|
height: 100%;
|
||||||
|
position: relative;
|
||||||
|
.input-bx{
|
||||||
|
margin-top: 1rem;
|
||||||
|
width: 300px;
|
||||||
|
position: relative;
|
||||||
|
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;
|
||||||
|
top: 1px;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -6,6 +6,7 @@
|
|||||||
height: 100vh;
|
height: 100vh;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
.home-component {
|
.home-component {
|
||||||
background: #1c1c1c;
|
background: #1c1c1c;
|
||||||
|
@ -9,6 +9,8 @@
|
|||||||
@import 'search.scss';
|
@import 'search.scss';
|
||||||
@import 'personal.scss';
|
@import 'personal.scss';
|
||||||
@import 'upload.scss';
|
@import 'upload.scss';
|
||||||
|
@import 'addArtist.scss';
|
||||||
|
@import 'addAlbum.scss';
|
||||||
|
|
||||||
body {
|
body {
|
||||||
font-family: sans-serif;
|
font-family: sans-serif;
|
||||||
|
@ -11,16 +11,29 @@
|
|||||||
margin: 3px;
|
margin: 3px;
|
||||||
padding: 0.1rem 1rem 1rem 1rem;
|
padding: 0.1rem 1rem 1rem 1rem;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
.upload-overlay {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
background-color: rgba(0, 0, 0, 0.5);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
.header {
|
.header {
|
||||||
font-size: 1.2rem;
|
font-size: 1.2rem;
|
||||||
}
|
}
|
||||||
.upload-btn {
|
.upload-dropdown-container {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 10px;
|
top: 10px;
|
||||||
right: 7px;
|
right: 7px;
|
||||||
|
.upload-dropdown-btn {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
font-size: 0.9rem;
|
font-size: 0.9rem;
|
||||||
border-radius: 50px;
|
border-radius: 50px;
|
||||||
@ -29,17 +42,40 @@
|
|||||||
padding-right: 1rem;
|
padding-right: 1rem;
|
||||||
padding-left: 1rem;
|
padding-left: 1rem;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: background-color 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
.add-sign {
|
.add-sign {
|
||||||
font-size: 1.5rem;
|
font-size: 1.5rem;
|
||||||
margin-top: auto;
|
margin-top: auto;
|
||||||
margin-right: 5px;
|
|
||||||
color: white;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.upload-btn:hover {
|
.upload-dropdown-btn:hover {
|
||||||
background-color: #9e9e9e;
|
background-color: #9e9e9e;
|
||||||
}
|
}
|
||||||
|
.upload-dropdown-btn-active {
|
||||||
|
border-radius: 12.5px 12.5px 0 0;
|
||||||
|
width: 110px;
|
||||||
|
}
|
||||||
|
.upload-dropdown {
|
||||||
|
background-color: #f0ecec;
|
||||||
|
color: black;
|
||||||
|
width: 110px;
|
||||||
|
border-radius: 0 0 5px 5px;
|
||||||
|
.add-btns {
|
||||||
|
border: none;
|
||||||
|
border-bottom: 1px solid black;
|
||||||
|
width: 100%;
|
||||||
|
padding: 0.25rem;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.add-btns:first-child {
|
||||||
|
border-top: 1px solid black;
|
||||||
|
}
|
||||||
|
.add-btns:last-child {
|
||||||
|
border-radius: 0 0 5px 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.buttons {
|
.buttons {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
background-color: #1c1c1c;
|
background-color: #1c1c1c;
|
||||||
|
z-index: 11;
|
||||||
.close-button {
|
.close-button {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 5px;
|
top: 5px;
|
||||||
@ -27,9 +28,7 @@
|
|||||||
font-size: 1.6rem;
|
font-size: 1.6rem;
|
||||||
transition: all 0.3s;
|
transition: all 0.3s;
|
||||||
border: none;
|
border: none;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.close-button:hover {
|
.close-button:hover {
|
||||||
transform: scale(1.1);
|
transform: scale(1.1);
|
||||||
background-color: rgba(255, 255, 255, 0.1);
|
background-color: rgba(255, 255, 255, 0.1);
|
||||||
@ -48,6 +47,9 @@
|
|||||||
padding: .1rem;
|
padding: .1rem;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
position: relative;
|
||||||
.input-bx{
|
.input-bx{
|
||||||
margin-top: 1rem;
|
margin-top: 1rem;
|
||||||
position: relative;
|
position: relative;
|
||||||
@ -126,6 +128,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.upload-button {
|
.upload-button {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 5px;
|
||||||
|
width: 100%;
|
||||||
margin-top: 1rem;
|
margin-top: 1rem;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
background-color: #7f8fa6;
|
background-color: #7f8fa6;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user