Merge pull request 'User Authentication and Other fixes' (#1) from user-auth into main
Reviewed-on: #1
This commit was merged in pull request #1.
This commit is contained in:
6
.env-dev
Normal file
6
.env-dev
Normal file
@@ -0,0 +1,6 @@
|
||||
DATABASE_URL="sqlite:../data-debug/db/sqlite.db?mode=rwc"
|
||||
IMAGES_DIR="../data-debug/images"
|
||||
CORS_URL="*"
|
||||
MEILI_URL="http://localhost:7700"
|
||||
MEILI_KEY="asdasdasd"
|
||||
BACKEND_URL="http://localhost:8081"
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -10,6 +10,7 @@ images/*
|
||||
data/*
|
||||
data-debug/*
|
||||
booksman.tar
|
||||
frontend/node_modules/
|
||||
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
|
||||
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
|
||||
# Cargo.lock
|
||||
|
||||
637
backend/Cargo.lock
generated
637
backend/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -9,7 +9,7 @@ booksman-orm = { path = "../orm" }
|
||||
booksman-search = { path = "../search" }
|
||||
migration = { path = "../migration" }
|
||||
entity = { path = "../entity" }
|
||||
axum = "^0.5"
|
||||
axum = "^0.6"
|
||||
axum-extra = { version = "^0.3", features = ["spa"] }
|
||||
clap = { version = "^3", features = ["derive"] }
|
||||
dotenvy = "0.15.0"
|
||||
@@ -27,9 +27,16 @@ tracing = "^0.1"
|
||||
tracing-subscriber = "^0.3"
|
||||
itertools = "0.10"
|
||||
chrono = "0.4"
|
||||
axum-login = "0.4"
|
||||
axum-macros = "0.3"
|
||||
#features = ["sqlite"]
|
||||
|
||||
[dependencies.rand]
|
||||
version = "0.8.5"
|
||||
features = ["min_const_gen"]
|
||||
|
||||
[dependencies.sea-orm]
|
||||
version = "^0.9.2" # sea-orm version
|
||||
version = "^0.10.6" # sea-orm version
|
||||
features = [
|
||||
"debug-print",
|
||||
"runtime-tokio-native-tls",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -13,4 +13,4 @@ serde = { version = "1", features = ["derive"] }
|
||||
chrono = "0.4"
|
||||
|
||||
[dependencies.sea-orm]
|
||||
version = "^0.9.2" # sea-orm version
|
||||
version = "^0.10.6" # sea-orm version
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
//! SeaORM Entity. Generated by sea-orm-codegen 0.9.3
|
||||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.10.6
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
|
||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
|
||||
#[sea_orm(table_name = "book")]
|
||||
pub struct Model {
|
||||
#[sea_orm(primary_key)]
|
||||
@@ -19,12 +19,15 @@ pub struct Model {
|
||||
pub time_added: Option<String>,
|
||||
pub rating: Option<i32>,
|
||||
pub comments: Option<String>,
|
||||
pub user_id: i32,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
pub enum Relation {
|
||||
#[sea_orm(has_many = "super::book_author::Entity")]
|
||||
BookAuthor,
|
||||
#[sea_orm(has_many = "super::book_isbn::Entity")]
|
||||
BookIsbn,
|
||||
#[sea_orm(has_many = "super::book_person::Entity")]
|
||||
BookPerson,
|
||||
#[sea_orm(has_many = "super::book_place::Entity")]
|
||||
@@ -33,8 +36,14 @@ pub enum Relation {
|
||||
BookSubject,
|
||||
#[sea_orm(has_many = "super::book_time::Entity")]
|
||||
BookTime,
|
||||
#[sea_orm(has_many = "super::book_isbn::Entity")]
|
||||
BookIsbn,
|
||||
#[sea_orm(
|
||||
belongs_to = "super::user::Entity",
|
||||
from = "Column::UserId",
|
||||
to = "super::user::Column::Id",
|
||||
on_update = "Cascade",
|
||||
on_delete = "Cascade"
|
||||
)]
|
||||
User,
|
||||
}
|
||||
|
||||
impl Related<super::book_author::Entity> for Entity {
|
||||
@@ -43,6 +52,12 @@ impl Related<super::book_author::Entity> for Entity {
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::book_isbn::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::BookIsbn.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::book_person::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::BookPerson.def()
|
||||
@@ -67,9 +82,9 @@ impl Related<super::book_time::Entity> for Entity {
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::book_isbn::Entity> for Entity {
|
||||
impl Related<super::user::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::BookIsbn.def()
|
||||
Relation::User.def()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
//! SeaORM Entity. Generated by sea-orm-codegen 0.9.3
|
||||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.10.6
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
|
||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
|
||||
#[sea_orm(table_name = "book_author")]
|
||||
pub struct Model {
|
||||
#[sea_orm(primary_key)]
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
//! SeaORM Entity. Generated by sea-orm-codegen 0.9.3
|
||||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.10.6
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
|
||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
|
||||
#[sea_orm(table_name = "book_isbn")]
|
||||
pub struct Model {
|
||||
#[sea_orm(primary_key)]
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
//! SeaORM Entity. Generated by sea-orm-codegen 0.9.3
|
||||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.10.6
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
|
||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
|
||||
#[sea_orm(table_name = "book_person")]
|
||||
pub struct Model {
|
||||
#[sea_orm(primary_key)]
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
//! SeaORM Entity. Generated by sea-orm-codegen 0.9.3
|
||||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.10.6
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
|
||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
|
||||
#[sea_orm(table_name = "book_place")]
|
||||
pub struct Model {
|
||||
#[sea_orm(primary_key)]
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
//! SeaORM Entity. Generated by sea-orm-codegen 0.9.3
|
||||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.10.6
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
|
||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
|
||||
#[sea_orm(table_name = "book_subject")]
|
||||
pub struct Model {
|
||||
#[sea_orm(primary_key)]
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
//! SeaORM Entity. Generated by sea-orm-codegen 0.9.3
|
||||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.10.6
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
|
||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
|
||||
#[sea_orm(table_name = "book_time")]
|
||||
pub struct Model {
|
||||
#[sea_orm(primary_key)]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//! SeaORM Entity. Generated by sea-orm-codegen 0.9.3
|
||||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.10.6
|
||||
|
||||
pub mod prelude;
|
||||
|
||||
@@ -9,3 +9,4 @@ pub mod book_person;
|
||||
pub mod book_place;
|
||||
pub mod book_subject;
|
||||
pub mod book_time;
|
||||
pub mod user;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//! SeaORM Entity. Generated by sea-orm-codegen 0.9.3
|
||||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.10.6
|
||||
|
||||
pub use super::book::Entity as Book;
|
||||
pub use super::book_author::Entity as BookAuthor;
|
||||
@@ -7,3 +7,4 @@ pub use super::book_person::Entity as BookPerson;
|
||||
pub use super::book_place::Entity as BookPlace;
|
||||
pub use super::book_subject::Entity as BookSubject;
|
||||
pub use super::book_time::Entity as BookTime;
|
||||
pub use super::user::Entity as User;
|
||||
|
||||
26
backend/entity/src/entities/user.rs
Normal file
26
backend/entity/src/entities/user.rs
Normal file
@@ -0,0 +1,26 @@
|
||||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.10.6
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
|
||||
#[sea_orm(table_name = "user")]
|
||||
pub struct Model {
|
||||
#[sea_orm(primary_key)]
|
||||
pub id: i32,
|
||||
pub user_name: Option<String>,
|
||||
pub password_hash: String,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
pub enum Relation {
|
||||
#[sea_orm(has_many = "super::book::Entity")]
|
||||
Book,
|
||||
}
|
||||
|
||||
impl Related<super::book::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::Book.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
||||
@@ -1,3 +1 @@
|
||||
pub mod entities;
|
||||
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ async-std = { version = "^1", features = ["attributes", "tokio1"] }
|
||||
chrono = "0.4"
|
||||
|
||||
[dependencies.sea-orm-migration]
|
||||
version = "^0.9.2"
|
||||
version = "^0.10.6"
|
||||
features = [
|
||||
# Enable at least one `ASYNC_RUNTIME` and `DATABASE_DRIVER` feature if you want to run migration via CLI.
|
||||
# View the list of supported features at https://www.sea-ql.org/SeaORM/docs/install-and-config/database-and-async-runtime.
|
||||
|
||||
@@ -10,6 +10,30 @@ impl MigrationTrait for Migration {
|
||||
// Replace the sample below with your own migration scripts
|
||||
//todo!();
|
||||
|
||||
manager
|
||||
.create_table(
|
||||
Table::create()
|
||||
.table(User::Table)
|
||||
.if_not_exists()
|
||||
.col(
|
||||
ColumnDef::new(User::Id)
|
||||
.integer()
|
||||
.not_null()
|
||||
.auto_increment()
|
||||
.primary_key(),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(User::UserName)
|
||||
.string()
|
||||
.unique_key()
|
||||
.not_null(),
|
||||
)
|
||||
.col(ColumnDef::new(User::PasswordHash).string().not_null())
|
||||
.to_owned(),
|
||||
)
|
||||
.await
|
||||
.expect("Migration failed");
|
||||
|
||||
manager
|
||||
.create_table(
|
||||
Table::create()
|
||||
@@ -34,9 +58,19 @@ impl MigrationTrait for Migration {
|
||||
.col(ColumnDef::new(Book::TimeAdded).time())
|
||||
.col(ColumnDef::new(Book::Rating).integer())
|
||||
.col(ColumnDef::new(Book::Comments).string())
|
||||
.col(ColumnDef::new(Book::UserId).integer().not_null())
|
||||
.foreign_key(
|
||||
ForeignKey::create()
|
||||
.name("FK_2e303c3a712662f1fc2a4d0aavc")
|
||||
.from(Book::Table, Book::UserId)
|
||||
.to(User::Table, User::Id)
|
||||
.on_delete(ForeignKeyAction::Cascade)
|
||||
.on_update(ForeignKeyAction::Cascade),
|
||||
)
|
||||
.to_owned(),
|
||||
)
|
||||
.await.expect("Migration failed");
|
||||
.await
|
||||
.expect("Migration failed");
|
||||
|
||||
manager
|
||||
.create_table(
|
||||
@@ -62,7 +96,8 @@ impl MigrationTrait for Migration {
|
||||
)
|
||||
.to_owned(),
|
||||
)
|
||||
.await.expect("Migration failed");
|
||||
.await
|
||||
.expect("Migration failed");
|
||||
|
||||
manager
|
||||
.create_table(
|
||||
@@ -88,7 +123,8 @@ impl MigrationTrait for Migration {
|
||||
)
|
||||
.to_owned(),
|
||||
)
|
||||
.await.expect("Migration failed");
|
||||
.await
|
||||
.expect("Migration failed");
|
||||
|
||||
manager
|
||||
.create_table(
|
||||
@@ -114,7 +150,8 @@ impl MigrationTrait for Migration {
|
||||
)
|
||||
.to_owned(),
|
||||
)
|
||||
.await.expect("Migration failed");
|
||||
.await
|
||||
.expect("Migration failed");
|
||||
|
||||
manager
|
||||
.create_table(
|
||||
@@ -140,7 +177,8 @@ impl MigrationTrait for Migration {
|
||||
)
|
||||
.to_owned(),
|
||||
)
|
||||
.await.expect("Migration failed");
|
||||
.await
|
||||
.expect("Migration failed");
|
||||
|
||||
manager
|
||||
.create_table(
|
||||
@@ -166,7 +204,8 @@ impl MigrationTrait for Migration {
|
||||
)
|
||||
.to_owned(),
|
||||
)
|
||||
.await.expect("Migration failed");
|
||||
.await
|
||||
.expect("Migration failed");
|
||||
|
||||
manager
|
||||
.create_table(
|
||||
@@ -201,22 +240,28 @@ impl MigrationTrait for Migration {
|
||||
|
||||
manager
|
||||
.drop_table(Table::drop().table(Book::Table).to_owned())
|
||||
.await.expect("Drop failed");
|
||||
.await
|
||||
.expect("Drop failed");
|
||||
manager
|
||||
.drop_table(Table::drop().table(BookAuthor::Table).to_owned())
|
||||
.await.expect("Drop failed");
|
||||
.await
|
||||
.expect("Drop failed");
|
||||
manager
|
||||
.drop_table(Table::drop().table(BookPerson::Table).to_owned())
|
||||
.await.expect("Drop failed");
|
||||
.await
|
||||
.expect("Drop failed");
|
||||
manager
|
||||
.drop_table(Table::drop().table(BookPlace::Table).to_owned())
|
||||
.await.expect("Drop failed");
|
||||
.await
|
||||
.expect("Drop failed");
|
||||
manager
|
||||
.drop_table(Table::drop().table(BookSubject::Table).to_owned())
|
||||
.await.expect("Drop failed");
|
||||
.await
|
||||
.expect("Drop failed");
|
||||
manager
|
||||
.drop_table(Table::drop().table(BookTime::Table).to_owned())
|
||||
.await.expect("Drop failed");
|
||||
.await
|
||||
.expect("Drop failed");
|
||||
manager
|
||||
.drop_table(Table::drop().table(BookISBN::Table).to_owned())
|
||||
.await
|
||||
@@ -246,6 +291,7 @@ enum Book {
|
||||
TimeAdded,
|
||||
Rating,
|
||||
Comments,
|
||||
UserId,
|
||||
}
|
||||
|
||||
#[derive(Iden)]
|
||||
@@ -295,3 +341,11 @@ enum BookISBN {
|
||||
ISBN,
|
||||
BookId,
|
||||
}
|
||||
|
||||
#[derive(Iden)]
|
||||
enum User {
|
||||
Table,
|
||||
Id,
|
||||
UserName,
|
||||
PasswordHash,
|
||||
}
|
||||
|
||||
@@ -8,9 +8,12 @@ publish = false
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
entity = { path = "../entity" }
|
||||
chrono = "0.4"
|
||||
axum-login = "0.4"
|
||||
axum = "^0.6"
|
||||
eyre = "0.6.8"
|
||||
|
||||
[dependencies.sea-orm]
|
||||
version = "^0.9.2" # sea-orm version
|
||||
version = "^0.10.6" # sea-orm version
|
||||
features = [
|
||||
"debug-print",
|
||||
"runtime-tokio-native-tls",
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
mod mutation;
|
||||
mod query;
|
||||
mod userstore;
|
||||
|
||||
pub use mutation::*;
|
||||
pub use query::*;
|
||||
pub use userstore::*;
|
||||
|
||||
pub use sea_orm;
|
||||
|
||||
@@ -1,14 +1,39 @@
|
||||
//use ::entity::entities::prelude::Book;
|
||||
//use ::entity::entities::{prelude::*, *};
|
||||
use ::entity::entities::{book::Entity as Book};
|
||||
use ::entity::entities::book::Entity as Book;
|
||||
use ::entity::entities::user::Entity as User;
|
||||
//, book_author::Entity as Author, book_person::Entity as Person, book_place::Entity as Place, book_subject::Entity as Subject, book_time::Entity as Time, book_isbn::Entity as ISBN};
|
||||
use ::entity::entities::{book,book_author,book_person,book_place,book_subject,book_time,book_isbn};
|
||||
use ::entity::entities::{
|
||||
book, book_author, book_isbn, book_person, book_place, book_subject, book_time, user,
|
||||
};
|
||||
use sea_orm::*;
|
||||
//use ::entity::entities::prelude::Book;
|
||||
|
||||
pub struct Mutation;
|
||||
|
||||
impl Mutation {
|
||||
pub async fn create_user(
|
||||
db: &DbConn,
|
||||
form_data: user::Model,
|
||||
) -> Result<InsertResult<user::ActiveModel>, DbErr> {
|
||||
let record = user::ActiveModel {
|
||||
user_name: Set(form_data.user_name.to_owned()),
|
||||
password_hash: Set(form_data.password_hash.to_owned()),
|
||||
..Default::default()
|
||||
};
|
||||
User::insert(record).exec(db).await
|
||||
}
|
||||
|
||||
pub async fn delete_user(db: &DbConn, id: i32) -> Result<DeleteResult, DbErr> {
|
||||
let user: user::ActiveModel = User::find_by_id(id)
|
||||
.one(db)
|
||||
.await?
|
||||
.ok_or(DbErr::Custom("Cannot find user.".to_owned()))
|
||||
.map(Into::into)?;
|
||||
|
||||
user.delete(db).await
|
||||
}
|
||||
|
||||
pub async fn create_book(
|
||||
db: &DbConn,
|
||||
form_data: book::Model,
|
||||
@@ -26,6 +51,7 @@ impl Mutation {
|
||||
time_added: Set(form_data.time_added.to_owned()),
|
||||
rating: Set(form_data.rating.to_owned()),
|
||||
comments: Set(form_data.comments.to_owned()),
|
||||
user_id: Set(form_data.user_id.to_owned()),
|
||||
..Default::default()
|
||||
};
|
||||
Book::insert(record).exec(db).await
|
||||
@@ -113,7 +139,6 @@ impl Mutation {
|
||||
id: i32,
|
||||
form_data: book::Model,
|
||||
) -> Result<book::Model, DbErr> {
|
||||
|
||||
let book: book::ActiveModel = Book::find_by_id(id)
|
||||
.one(db)
|
||||
.await?
|
||||
@@ -134,7 +159,10 @@ impl Mutation {
|
||||
time_added: Set(form_data.time_added.to_owned()),
|
||||
rating: Set(form_data.rating.to_owned()),
|
||||
comments: Set(form_data.comments.to_owned()),
|
||||
}.update(db).await
|
||||
user_id: Set(form_data.user_id.to_owned()),
|
||||
}
|
||||
.update(db)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn delete_book(db: &DbConn, id: i32) -> Result<DeleteResult, DbErr> {
|
||||
@@ -147,53 +175,48 @@ impl Mutation {
|
||||
book.delete(db).await
|
||||
}
|
||||
|
||||
|
||||
pub async fn delete_book_author(db: &DbConn, id: i32) -> Result<DeleteResult, DbErr> {
|
||||
book_author::Entity::delete_many().filter(
|
||||
Condition::any()
|
||||
.add(book_author::Column::BookId.eq(id))
|
||||
).exec(db).await
|
||||
book_author::Entity::delete_many()
|
||||
.filter(Condition::any().add(book_author::Column::BookId.eq(id)))
|
||||
.exec(db)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn delete_book_person(db: &DbConn, id: i32) -> Result<DeleteResult, DbErr> {
|
||||
book_person::Entity::delete_many().filter(
|
||||
Condition::any()
|
||||
.add(book_person::Column::BookId.eq(id))
|
||||
).exec(db).await
|
||||
book_person::Entity::delete_many()
|
||||
.filter(Condition::any().add(book_person::Column::BookId.eq(id)))
|
||||
.exec(db)
|
||||
.await
|
||||
}
|
||||
|
||||
|
||||
pub async fn delete_book_place(db: &DbConn, id: i32) -> Result<DeleteResult, DbErr> {
|
||||
book_place::Entity::delete_many().filter(
|
||||
Condition::any()
|
||||
.add(book_place::Column::BookId.eq(id))
|
||||
).exec(db).await
|
||||
book_place::Entity::delete_many()
|
||||
.filter(Condition::any().add(book_place::Column::BookId.eq(id)))
|
||||
.exec(db)
|
||||
.await
|
||||
}
|
||||
|
||||
|
||||
pub async fn delete_book_subject(db: &DbConn, id: i32) -> Result<DeleteResult, DbErr> {
|
||||
book_subject::Entity::delete_many().filter(
|
||||
Condition::any()
|
||||
.add(book_subject::Column::BookId.eq(id))
|
||||
).exec(db).await
|
||||
book_subject::Entity::delete_many()
|
||||
.filter(Condition::any().add(book_subject::Column::BookId.eq(id)))
|
||||
.exec(db)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn delete_book_time(db: &DbConn, id: i32) -> Result<DeleteResult, DbErr> {
|
||||
book_time::Entity::delete_many().filter(
|
||||
Condition::any()
|
||||
.add(book_time::Column::BookId.eq(id))
|
||||
).exec(db).await
|
||||
book_time::Entity::delete_many()
|
||||
.filter(Condition::any().add(book_time::Column::BookId.eq(id)))
|
||||
.exec(db)
|
||||
.await
|
||||
}
|
||||
|
||||
|
||||
pub async fn delete_book_isbn(db: &DbConn, id: i32) -> Result<DeleteResult, DbErr> {
|
||||
book_isbn::Entity::delete_many().filter(
|
||||
Condition::any()
|
||||
.add(book_isbn::Column::BookId.eq(id))
|
||||
).exec(db).await
|
||||
book_isbn::Entity::delete_many()
|
||||
.filter(Condition::any().add(book_isbn::Column::BookId.eq(id)))
|
||||
.exec(db)
|
||||
.await
|
||||
}
|
||||
|
||||
|
||||
pub async fn delete_all_books(db: &DbConn) -> Result<DeleteResult, DbErr> {
|
||||
Book::delete_many().exec(db).await
|
||||
}
|
||||
|
||||
@@ -1,34 +1,14 @@
|
||||
use ::entity::entities::book::Entity as Book;
|
||||
use ::entity::entities::user::Entity as User;
|
||||
|
||||
//, book_author::Entity as Author, book_person::Entity as Person, book_place::Entity as Place, book_subject::Entity as Subject, book_time::Entity as Time, book_isbn::Entity as ISBN};
|
||||
use ::entity::entities::{book,book_author,book_person,book_place,book_subject,book_time,book_isbn};
|
||||
use ::entity::entities::{
|
||||
book, book_author, book_isbn, book_person, book_place, book_subject, book_time, user,
|
||||
};
|
||||
//use ::entity::entities::{prelude::*, *};
|
||||
use sea_orm::*;
|
||||
//use sea_query::Expr;
|
||||
|
||||
#[derive(FromQueryResult,Clone)]
|
||||
pub struct BookAndMeta {
|
||||
pub id: i32,
|
||||
pub open_library_key: String,
|
||||
pub title: String,
|
||||
pub edition_count: i32,
|
||||
pub first_publish_year: i32,
|
||||
pub median_page_count: i32,
|
||||
pub goodread_id: String,
|
||||
pub description: String,
|
||||
pub cover: String,
|
||||
pub location: String,
|
||||
pub time_added: String,
|
||||
pub rating: i32,
|
||||
pub comments: String,
|
||||
pub author_name: String,
|
||||
pub person: String,
|
||||
pub place: String,
|
||||
pub subject: String,
|
||||
pub time: String,
|
||||
pub isbn: String,
|
||||
}
|
||||
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct BookAndMetaV2 {
|
||||
pub book: book::Model,
|
||||
@@ -48,7 +28,24 @@ impl Query {
|
||||
Book::find_by_id(id).one(db).await
|
||||
}
|
||||
|
||||
/* pub async fn find_bookplusmeta_by_id(db: &DbConn, id: i32) -> Result<Option<(book::Model, Vec<book_author::Model>, Vec<book_person::Model>)>, DbErr> {
|
||||
pub async fn find_userid_by_name(
|
||||
db: &DbConn,
|
||||
name: String,
|
||||
) -> Result<Option<user::Model>, DbErr> {
|
||||
User::find()
|
||||
.filter(user::Column::UserName.eq(name))
|
||||
.one(db)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn find_user_by_id(db: &DbConn, id: i32) -> Result<Option<user::Model>, DbErr> {
|
||||
User::find_by_id(id).one(db).await
|
||||
}
|
||||
|
||||
pub async fn list_all_users(db: &DbConn) -> Result<Vec<user::Model>, DbErr> {
|
||||
User::find().all(db).await
|
||||
}
|
||||
/* pub async fn find_bookplusmeta_by_id(db: &DbConn, id: i32) -> Result<Option<(book::Model, Vec<book_author::Model>, Vec<book_person::Model>)>, DbErr> {
|
||||
let book = Book::find_by_id(id).one(db).await?.unwrap();
|
||||
let authors = book.find_related(Author).all(db).await?;
|
||||
let persons = book.find_related(Person).all(db).await?;
|
||||
@@ -59,35 +56,63 @@ impl Query {
|
||||
/// If ok, returns (post models, num pages).
|
||||
pub async fn find_books_in_page(
|
||||
db: &DbConn,
|
||||
page: usize,
|
||||
posts_per_page: usize,
|
||||
) -> Result<(Vec<book::Model>, usize), DbErr> {
|
||||
page: u64,
|
||||
posts_per_page: u64,
|
||||
userid: i32,
|
||||
sort: String,
|
||||
) -> Result<(Vec<book::Model>, u64), DbErr> {
|
||||
// Setup paginator
|
||||
if sort == "desc".to_string() {
|
||||
let paginator = Book::find()
|
||||
.filter(book::Column::UserId.eq(userid))
|
||||
.order_by_desc(book::Column::Id)
|
||||
.paginate(db, posts_per_page);
|
||||
let num_pages = paginator.num_pages().await?;
|
||||
return paginator.fetch_page(page - 1).await.map(|p| (p, num_pages));
|
||||
} else {
|
||||
let paginator = Book::find()
|
||||
.filter(book::Column::UserId.eq(userid))
|
||||
.order_by_asc(book::Column::Id)
|
||||
.paginate(db, posts_per_page);
|
||||
let num_pages = paginator.num_pages().await?;
|
||||
|
||||
// Fetch paginated posts
|
||||
paginator.fetch_page(page - 1).await.map(|p| (p, num_pages))
|
||||
return paginator.fetch_page(page - 1).await.map(|p| (p, num_pages));
|
||||
}
|
||||
|
||||
pub async fn find_books_plus_meta_in_page(
|
||||
// Fetch paginated posts
|
||||
}
|
||||
|
||||
pub async fn find_books_plus_meta_in_page(
|
||||
db: &DbConn,
|
||||
page: usize,
|
||||
posts_per_page: usize,
|
||||
) -> Result<(Vec<BookAndMetaV2>, usize), DbErr> {
|
||||
userid: i32,
|
||||
sort: String,
|
||||
) -> Result<(Vec<BookAndMetaV2>, u64), DbErr> {
|
||||
// Setup paginator
|
||||
let books = Self::find_books_in_page(db,page,posts_per_page).await?;
|
||||
let books = Self::find_books_in_page(
|
||||
db,
|
||||
page.try_into().unwrap(),
|
||||
posts_per_page.try_into().unwrap(),
|
||||
userid,
|
||||
sort,
|
||||
)
|
||||
.await?;
|
||||
let book_ids: Vec<i32> = books.0.clone().into_iter().map(|b| b.id).collect();
|
||||
println!("SIZE IS {} and {:?}", book_ids.len(), book_ids);
|
||||
let mut resbooks: Vec<BookAndMetaV2> = Vec::with_capacity(book_ids.len());
|
||||
for book in books.0.iter() {
|
||||
let bauthors: Vec<book_author::Model> = book.find_related(book_author::Entity).all(db).await?;
|
||||
let bpersons: Vec<book_person::Model> = book.find_related(book_person::Entity).all(db).await?;
|
||||
let bplaces: Vec<book_place::Model> = book.find_related(book_place::Entity).all(db).await?;
|
||||
let bsubjects: Vec<book_subject::Model> = book.find_related(book_subject::Entity).all(db).await?;
|
||||
let btimes: Vec<book_time::Model> = book.find_related(book_time::Entity).all(db).await?;
|
||||
let bisbns: Vec<book_isbn::Model> = book.find_related(book_isbn::Entity).all(db).await?;
|
||||
let bauthors: Vec<book_author::Model> =
|
||||
book.find_related(book_author::Entity).all(db).await?;
|
||||
let bpersons: Vec<book_person::Model> =
|
||||
book.find_related(book_person::Entity).all(db).await?;
|
||||
let bplaces: Vec<book_place::Model> =
|
||||
book.find_related(book_place::Entity).all(db).await?;
|
||||
let bsubjects: Vec<book_subject::Model> =
|
||||
book.find_related(book_subject::Entity).all(db).await?;
|
||||
let btimes: Vec<book_time::Model> =
|
||||
book.find_related(book_time::Entity).all(db).await?;
|
||||
let bisbns: Vec<book_isbn::Model> =
|
||||
book.find_related(book_isbn::Entity).all(db).await?;
|
||||
|
||||
let bookandmeta = BookAndMetaV2 {
|
||||
book: (book.clone()),
|
||||
@@ -96,14 +121,11 @@ pub async fn find_books_plus_meta_in_page(
|
||||
places: bplaces,
|
||||
subjects: bsubjects,
|
||||
times: btimes,
|
||||
isbns: bisbns
|
||||
isbns: bisbns,
|
||||
};
|
||||
resbooks.push(bookandmeta);
|
||||
|
||||
}
|
||||
|
||||
Ok((resbooks,books.1))
|
||||
Ok((resbooks, books.1))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
60
backend/orm/src/userstore.rs
Normal file
60
backend/orm/src/userstore.rs
Normal file
@@ -0,0 +1,60 @@
|
||||
use crate::Query;
|
||||
use axum::async_trait;
|
||||
use axum_login::{secrecy::SecretVec, AuthUser, UserStore};
|
||||
use sea_orm::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct AxumUser {
|
||||
pub id: i32,
|
||||
pub password_hash: String,
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
type Result<T = ()> = std::result::Result<T, eyre::Error>;
|
||||
|
||||
impl<Role> AuthUser<Role> for AxumUser
|
||||
where
|
||||
Role: PartialOrd + PartialEq + Clone + Send + Sync + 'static,
|
||||
{
|
||||
fn get_id(&self) -> String {
|
||||
format!("{}", self.id)
|
||||
}
|
||||
|
||||
fn get_password_hash(&self) -> axum_login::secrecy::SecretVec<u8> {
|
||||
SecretVec::new(self.password_hash.clone().into())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct AxumUserStore {
|
||||
conn: DatabaseConnection,
|
||||
}
|
||||
|
||||
impl AxumUserStore {
|
||||
pub fn new(conn: &DatabaseConnection) -> Self {
|
||||
Self { conn: conn.clone() }
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<Role> UserStore<Role> for AxumUserStore
|
||||
where
|
||||
Role: PartialOrd + PartialEq + Clone + Send + Sync + 'static,
|
||||
{
|
||||
type User = AxumUser;
|
||||
|
||||
async fn load_user(&self, user_id: &str) -> Result<Option<Self::User>> {
|
||||
// my user id is a Vec<u8>, so it's stored base64 encoded
|
||||
let id: i32 = user_id.parse().unwrap();
|
||||
let user = Query::find_user_by_id(&self.conn, id).await?;
|
||||
match user {
|
||||
Some(u) => Ok(Some(AxumUser {
|
||||
id: u.id,
|
||||
password_hash: u.password_hash,
|
||||
name: u.user_name.unwrap(),
|
||||
})),
|
||||
None => Ok(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
use meilisearch_sdk::client::*;
|
||||
use meilisearch_sdk::search::*;
|
||||
use serde::{Serialize, Deserialize};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Deserialize, Serialize, Debug, Clone)]
|
||||
pub struct BookMeili {
|
||||
@@ -25,35 +25,37 @@ pub struct BookMeili {
|
||||
pub isbn: Vec<String>,
|
||||
}
|
||||
|
||||
pub async fn create_or_update_book(book: BookMeili, client: &Client) {
|
||||
pub async fn create_or_update_book(book: BookMeili, userid: i32, client: &Client) {
|
||||
// An index is where the documents are stored.
|
||||
let books = client.index("books");
|
||||
let books = client.index(format!("books{}", userid));
|
||||
// Add some movies in the index. If the index 'movies' does not exist, Meilisearch creates it when you first add the documents.
|
||||
books.add_or_replace(&[
|
||||
book
|
||||
], Some("id")).await.unwrap();
|
||||
books.add_or_replace(&[book], Some("id")).await.unwrap();
|
||||
}
|
||||
|
||||
|
||||
pub async fn delete_book(bookid: i32, client: &Client) {
|
||||
pub async fn delete_book(bookid: i32, userid: i32, client: &Client) {
|
||||
// An index is where the documents are stored.
|
||||
let books = client.index("books");
|
||||
let books = client.index(format!("books{}", userid));
|
||||
books.delete_document(bookid).await.unwrap();
|
||||
}
|
||||
|
||||
|
||||
pub async fn search_book(search: &str, page: usize, client: &Client) -> Result<(Vec<BookMeili>, usize), meilisearch_sdk::errors::Error> {
|
||||
pub async fn search_book(
|
||||
search: &str,
|
||||
page: usize,
|
||||
userid: i32,
|
||||
client: &Client,
|
||||
) -> Result<(Vec<BookMeili>, usize), meilisearch_sdk::errors::Error> {
|
||||
// An index is where the documents are stored.
|
||||
let books = client.index("books");
|
||||
let results : SearchResults<BookMeili> = books.search().with_query(search).with_offset((page-1)*12)
|
||||
.execute::<BookMeili>().await.unwrap();
|
||||
let books = client.index(format!("books{}", userid));
|
||||
let results: SearchResults<BookMeili> = books
|
||||
.search()
|
||||
.with_query(search)
|
||||
.with_offset((page - 1) * 12)
|
||||
.execute::<BookMeili>()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let formatted_results : Vec<BookMeili> = (results.hits).iter().map(|r| r.result.clone()).collect();
|
||||
let formatted_results: Vec<BookMeili> =
|
||||
(results.hits).iter().map(|r| r.result.clone()).collect();
|
||||
//.iter()s.map(|r| r.formatted_result.unwrap()).collect();
|
||||
return Ok((formatted_results, results.estimated_total_hits));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
fn main() {
|
||||
booksman_api::main();
|
||||
}
|
||||
|
||||
|
||||
52
frontend/Cargo.lock
generated
52
frontend/Cargo.lock
generated
@@ -136,7 +136,6 @@ dependencies = [
|
||||
"serde-wasm-bindgen",
|
||||
"serde_json",
|
||||
"sycamore",
|
||||
"sycamore-router",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"wasm-logger",
|
||||
@@ -381,22 +380,6 @@ version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
||||
|
||||
[[package]]
|
||||
name = "minimal-lexical"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "7.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"minimal-lexical",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.13.1"
|
||||
@@ -565,9 +548,9 @@ checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1"
|
||||
|
||||
[[package]]
|
||||
name = "sycamore"
|
||||
version = "0.8.0"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3e4decd3fabbb4cfa8ef4d8b4469c7d35d65555806f6c6642a2733d892472ffa"
|
||||
checksum = "67817393b3c9828db84614f64db9a1ebb94729ce3a3751c41e7ff23d3f8e7f00"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"futures",
|
||||
@@ -631,31 +614,6 @@ dependencies = [
|
||||
"smallvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sycamore-router"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "123b34a150dac877d7bfae82dadfb0c586fd35a8f5fcdf1721dafa079fdc4c40"
|
||||
dependencies = [
|
||||
"sycamore",
|
||||
"sycamore-router-macro",
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sycamore-router-macro"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "92914a2f809b636d245b28d8a734801ecb8ff9c4996bbe6ea4176582e12503eb"
|
||||
dependencies = [
|
||||
"nom",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sycamore-web"
|
||||
version = "0.8.0"
|
||||
@@ -728,12 +686,6 @@ version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4f5b37a154999a8f3f98cc23a628d850e154479cd94decf3414696e12e31aaf"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04"
|
||||
|
||||
[[package]]
|
||||
name = "utf8-width"
|
||||
version = "0.1.6"
|
||||
|
||||
@@ -23,13 +23,13 @@ wasm-logger = "0.2.0"
|
||||
serde = { version = "^1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
serde-wasm-bindgen = "0.4"
|
||||
sycamore = {version = "0.8.0-beta.7", features = ["suspense"]}
|
||||
sycamore-router = "0.8.0-beta.7"
|
||||
sycamore = {version = "0.8.2", features = ["suspense"]}
|
||||
#sycamore-router = "0.8.2"
|
||||
wasm-bindgen = "0.2.79"
|
||||
#tokio = {version = "1.21.2", features = ["full"] }
|
||||
#yew = "0.19.3"
|
||||
#yew-router = "0.16.0"
|
||||
|
||||
[dependencies.web-sys]
|
||||
features = ["InputEvent", "KeyboardEvent", "Location", "Storage"]
|
||||
features = ["InputEvent", "EventTarget", "MouseEvent", "KeyboardEvent", "Location", "Storage", "Element", "Window", "Document"]
|
||||
version = "0.3.56"
|
||||
|
||||
@@ -3,5 +3,12 @@ target = "index.html"
|
||||
dist = "../dist"
|
||||
public_url = "/assets/"
|
||||
|
||||
|
||||
[[hooks]]
|
||||
stage = "build"
|
||||
command = "sh"
|
||||
command_arguments = ["-c", "npx tailwindcss -i src/tailwind.css -o $TRUNK_STAGING_DIR/tailwind.css"]
|
||||
|
||||
|
||||
[[proxy]]
|
||||
backend = "http://[::1]:8081/api/"
|
||||
|
||||
@@ -36,179 +36,3 @@ body {
|
||||
overflow: auto; /* Enable scroll if needed */
|
||||
background-color: #f1f1f1; /* Fallback color */
|
||||
}
|
||||
|
||||
/* Float three header columns side by side */
|
||||
.header {
|
||||
position: fixed; /* Stay in place */
|
||||
overflow: hidden;
|
||||
z-index: 1; /* Sit on top */
|
||||
width: 100%;
|
||||
top: 0; /* Position the navbar at the top of the page */
|
||||
padding: 0 10px;
|
||||
background-color: #f1f1f1;
|
||||
}
|
||||
|
||||
.main {
|
||||
margin-top: 50px; /* Add a top margin to avoid content overlay */
|
||||
}
|
||||
|
||||
/* Float three header columns side by side */
|
||||
.header-column {
|
||||
float: left;
|
||||
width: 28%;
|
||||
padding: 0 10px;
|
||||
}
|
||||
/* Float three header columns side by side */
|
||||
.header-page-column {
|
||||
float: left;
|
||||
margin-top: 0px;
|
||||
width: 15%;
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
/* Float four columns side by side */
|
||||
.column {
|
||||
float: left;
|
||||
width: 25%;
|
||||
padding: 0 10px;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
/* Remove extra left and right margins, due to padding in columns */
|
||||
.row {margin: 0 -5px;}
|
||||
|
||||
/* Clear floats after the columns */
|
||||
.row:after {
|
||||
content: "";
|
||||
display: table;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
/* Style the counter cards */
|
||||
.card {
|
||||
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2); /* this adds the "card" effect */
|
||||
padding: 16px;
|
||||
text-align: left;
|
||||
height: 400px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
background-color: #f1f1f1;
|
||||
}
|
||||
|
||||
/* Style the counter cards */
|
||||
.card-openlibrary {
|
||||
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2); /* this adds the "card" effect */
|
||||
padding: 16px;
|
||||
text-align: left;
|
||||
height: 200px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
background-color: #f1f1f1;
|
||||
}
|
||||
|
||||
.card img {
|
||||
float: right;
|
||||
width: 100px;
|
||||
padding: 0 20px 20px 0;
|
||||
}
|
||||
|
||||
.card-openlibrary img {
|
||||
float: right;
|
||||
width: 100px;
|
||||
padding: 0 20px 20px 0;
|
||||
}
|
||||
|
||||
|
||||
.card-title {
|
||||
padding: 16px;
|
||||
text-align: left;
|
||||
width: 60%;
|
||||
font-weight: bold;
|
||||
font-size: large;
|
||||
}
|
||||
|
||||
.card-authors {
|
||||
padding: 16px;
|
||||
text-align: left;
|
||||
width: 60%;
|
||||
font-size: large;
|
||||
}
|
||||
|
||||
.card-desc {
|
||||
padding: 2px;
|
||||
text-align: left;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
|
||||
.input-field {
|
||||
padding: 4px;
|
||||
float: left;
|
||||
text-align: left;
|
||||
width: 50%;
|
||||
font-size: large;
|
||||
}
|
||||
.input-field label {
|
||||
display: block;
|
||||
vertical-align: middle;
|
||||
min-width: 20%;
|
||||
max-width: 20%;
|
||||
width: 20%;
|
||||
padding: 4px;
|
||||
}
|
||||
.input-field textarea {
|
||||
vertical-align: middle;
|
||||
width: 80%;
|
||||
}
|
||||
.input-buttons {
|
||||
padding: 20px;
|
||||
float: left;
|
||||
text-align: left;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.more-info {
|
||||
width: 90%;
|
||||
}
|
||||
.more-info img {
|
||||
float: right;
|
||||
width: 200px;
|
||||
padding: 0 20px 20px 0;
|
||||
}
|
||||
|
||||
.more-info label {
|
||||
display:inline-block;
|
||||
text-align: left;
|
||||
width: 20%;
|
||||
font-weight: bold;
|
||||
font-size: large;
|
||||
}
|
||||
|
||||
.more-info-buttons {
|
||||
padding: 20px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.page-input {
|
||||
width: 40px;
|
||||
}
|
||||
|
||||
/* Responsive columns - one column layout (vertical) on small screens */
|
||||
@media screen and (max-width: 600px) {
|
||||
.column {
|
||||
width: 100%;
|
||||
display: block;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 800px) {
|
||||
.main {
|
||||
margin-top: 80px; /* Add a top margin to avoid content overlay */
|
||||
}
|
||||
|
||||
.input-field {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,13 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="shortcut icon" type="image/x-icon" href="data:image/x-icon;,">
|
||||
<link data-trunk rel="css" href="css/index.css" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="shortcut icon" type="image/x-icon" href="/images/favicon.svg">
|
||||
<link data-trunk rel="css" href="css/index.css" />
|
||||
<link data-trunk rel="copy-dir" href="static" />
|
||||
<link rel="stylesheet" href="/assets/tailwind.css"/>
|
||||
<link rel="stylesheet" href="/assets/static/fontawesome/css/fontawesome.css"/>
|
||||
<link rel="stylesheet" href="/assets/static/fontawesome/css/solid.css"/>
|
||||
<base data-trunk-public-url/>
|
||||
<title>Book Manager</title>
|
||||
</head>
|
||||
|
||||
1368
frontend/package-lock.json
generated
Normal file
1368
frontend/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
6
frontend/package.json
Normal file
6
frontend/package.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"devDependencies": {
|
||||
"@tailwindcss/forms": "^0.5.3",
|
||||
"tailwindcss": "^3.2.4"
|
||||
}
|
||||
}
|
||||
1960
frontend/src/main.rs
1960
frontend/src/main.rs
File diff suppressed because it is too large
Load Diff
3
frontend/src/tailwind.css
Normal file
3
frontend/src/tailwind.css
Normal file
@@ -0,0 +1,3 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
7946
frontend/static/fontawesome/css/all.css
vendored
Normal file
7946
frontend/static/fontawesome/css/all.css
vendored
Normal file
File diff suppressed because it is too large
Load Diff
6
frontend/static/fontawesome/css/all.min.css
vendored
Normal file
6
frontend/static/fontawesome/css/all.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
1516
frontend/static/fontawesome/css/brands.css
vendored
Normal file
1516
frontend/static/fontawesome/css/brands.css
vendored
Normal file
File diff suppressed because it is too large
Load Diff
6
frontend/static/fontawesome/css/brands.min.css
vendored
Normal file
6
frontend/static/fontawesome/css/brands.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
6369
frontend/static/fontawesome/css/fontawesome.css
vendored
Normal file
6369
frontend/static/fontawesome/css/fontawesome.css
vendored
Normal file
File diff suppressed because it is too large
Load Diff
6
frontend/static/fontawesome/css/fontawesome.min.css
vendored
Normal file
6
frontend/static/fontawesome/css/fontawesome.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
19
frontend/static/fontawesome/css/regular.css
vendored
Normal file
19
frontend/static/fontawesome/css/regular.css
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
/*!
|
||||
* Font Awesome Free 6.2.1 by @fontawesome - https://fontawesome.com
|
||||
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
|
||||
* Copyright 2022 Fonticons, Inc.
|
||||
*/
|
||||
:root, :host {
|
||||
--fa-style-family-classic: 'Font Awesome 6 Free';
|
||||
--fa-font-regular: normal 400 1em/1 'Font Awesome 6 Free'; }
|
||||
|
||||
@font-face {
|
||||
font-family: 'Font Awesome 6 Free';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: block;
|
||||
src: url("../webfonts/fa-regular-400.woff2") format("woff2"), url("../webfonts/fa-regular-400.ttf") format("truetype"); }
|
||||
|
||||
.far,
|
||||
.fa-regular {
|
||||
font-weight: 400; }
|
||||
6
frontend/static/fontawesome/css/regular.min.css
vendored
Normal file
6
frontend/static/fontawesome/css/regular.min.css
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
/*!
|
||||
* Font Awesome Free 6.2.1 by @fontawesome - https://fontawesome.com
|
||||
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
|
||||
* Copyright 2022 Fonticons, Inc.
|
||||
*/
|
||||
:host,:root{--fa-style-family-classic:"Font Awesome 6 Free";--fa-font-regular:normal 400 1em/1 "Font Awesome 6 Free"}@font-face{font-family:"Font Awesome 6 Free";font-style:normal;font-weight:400;font-display:block;src:url(../webfonts/fa-regular-400.woff2) format("woff2"),url(../webfonts/fa-regular-400.ttf) format("truetype")}.fa-regular,.far{font-weight:400}
|
||||
19
frontend/static/fontawesome/css/solid.css
vendored
Normal file
19
frontend/static/fontawesome/css/solid.css
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
/*!
|
||||
* Font Awesome Free 6.2.1 by @fontawesome - https://fontawesome.com
|
||||
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
|
||||
* Copyright 2022 Fonticons, Inc.
|
||||
*/
|
||||
:root, :host {
|
||||
--fa-style-family-classic: 'Font Awesome 6 Free';
|
||||
--fa-font-solid: normal 900 1em/1 'Font Awesome 6 Free'; }
|
||||
|
||||
@font-face {
|
||||
font-family: 'Font Awesome 6 Free';
|
||||
font-style: normal;
|
||||
font-weight: 900;
|
||||
font-display: block;
|
||||
src: url("../webfonts/fa-solid-900.woff2") format("woff2"), url("../webfonts/fa-solid-900.ttf") format("truetype"); }
|
||||
|
||||
.fas,
|
||||
.fa-solid {
|
||||
font-weight: 900; }
|
||||
6
frontend/static/fontawesome/css/solid.min.css
vendored
Normal file
6
frontend/static/fontawesome/css/solid.min.css
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
/*!
|
||||
* Font Awesome Free 6.2.1 by @fontawesome - https://fontawesome.com
|
||||
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
|
||||
* Copyright 2022 Fonticons, Inc.
|
||||
*/
|
||||
:host,:root{--fa-style-family-classic:"Font Awesome 6 Free";--fa-font-solid:normal 900 1em/1 "Font Awesome 6 Free"}@font-face{font-family:"Font Awesome 6 Free";font-style:normal;font-weight:900;font-display:block;src:url(../webfonts/fa-solid-900.woff2) format("woff2"),url(../webfonts/fa-solid-900.ttf) format("truetype")}.fa-solid,.fas{font-weight:900}
|
||||
635
frontend/static/fontawesome/css/svg-with-js.css
vendored
Normal file
635
frontend/static/fontawesome/css/svg-with-js.css
vendored
Normal file
@@ -0,0 +1,635 @@
|
||||
/*!
|
||||
* Font Awesome Free 6.2.1 by @fontawesome - https://fontawesome.com
|
||||
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
|
||||
* Copyright 2022 Fonticons, Inc.
|
||||
*/
|
||||
:root, :host {
|
||||
--fa-font-solid: normal 900 1em/1 'Font Awesome 6 Solid';
|
||||
--fa-font-regular: normal 400 1em/1 'Font Awesome 6 Regular';
|
||||
--fa-font-light: normal 300 1em/1 'Font Awesome 6 Light';
|
||||
--fa-font-thin: normal 100 1em/1 'Font Awesome 6 Thin';
|
||||
--fa-font-duotone: normal 900 1em/1 'Font Awesome 6 Duotone';
|
||||
--fa-font-sharp-solid: normal 900 1em/1 'Font Awesome 6 Sharp';
|
||||
--fa-font-brands: normal 400 1em/1 'Font Awesome 6 Brands'; }
|
||||
|
||||
svg:not(:root).svg-inline--fa, svg:not(:host).svg-inline--fa {
|
||||
overflow: visible;
|
||||
box-sizing: content-box; }
|
||||
|
||||
.svg-inline--fa {
|
||||
display: var(--fa-display, inline-block);
|
||||
height: 1em;
|
||||
overflow: visible;
|
||||
vertical-align: -.125em; }
|
||||
.svg-inline--fa.fa-2xs {
|
||||
vertical-align: 0.1em; }
|
||||
.svg-inline--fa.fa-xs {
|
||||
vertical-align: 0em; }
|
||||
.svg-inline--fa.fa-sm {
|
||||
vertical-align: -0.07143em; }
|
||||
.svg-inline--fa.fa-lg {
|
||||
vertical-align: -0.2em; }
|
||||
.svg-inline--fa.fa-xl {
|
||||
vertical-align: -0.25em; }
|
||||
.svg-inline--fa.fa-2xl {
|
||||
vertical-align: -0.3125em; }
|
||||
.svg-inline--fa.fa-pull-left {
|
||||
margin-right: var(--fa-pull-margin, 0.3em);
|
||||
width: auto; }
|
||||
.svg-inline--fa.fa-pull-right {
|
||||
margin-left: var(--fa-pull-margin, 0.3em);
|
||||
width: auto; }
|
||||
.svg-inline--fa.fa-li {
|
||||
width: var(--fa-li-width, 2em);
|
||||
top: 0.25em; }
|
||||
.svg-inline--fa.fa-fw {
|
||||
width: var(--fa-fw-width, 1.25em); }
|
||||
|
||||
.fa-layers svg.svg-inline--fa {
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
margin: auto;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0; }
|
||||
|
||||
.fa-layers-text, .fa-layers-counter {
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
text-align: center; }
|
||||
|
||||
.fa-layers {
|
||||
display: inline-block;
|
||||
height: 1em;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
vertical-align: -.125em;
|
||||
width: 1em; }
|
||||
.fa-layers svg.svg-inline--fa {
|
||||
-webkit-transform-origin: center center;
|
||||
transform-origin: center center; }
|
||||
|
||||
.fa-layers-text {
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
-webkit-transform: translate(-50%, -50%);
|
||||
transform: translate(-50%, -50%);
|
||||
-webkit-transform-origin: center center;
|
||||
transform-origin: center center; }
|
||||
|
||||
.fa-layers-counter {
|
||||
background-color: var(--fa-counter-background-color, #ff253a);
|
||||
border-radius: var(--fa-counter-border-radius, 1em);
|
||||
box-sizing: border-box;
|
||||
color: var(--fa-inverse, #fff);
|
||||
line-height: var(--fa-counter-line-height, 1);
|
||||
max-width: var(--fa-counter-max-width, 5em);
|
||||
min-width: var(--fa-counter-min-width, 1.5em);
|
||||
overflow: hidden;
|
||||
padding: var(--fa-counter-padding, 0.25em 0.5em);
|
||||
right: var(--fa-right, 0);
|
||||
text-overflow: ellipsis;
|
||||
top: var(--fa-top, 0);
|
||||
-webkit-transform: scale(var(--fa-counter-scale, 0.25));
|
||||
transform: scale(var(--fa-counter-scale, 0.25));
|
||||
-webkit-transform-origin: top right;
|
||||
transform-origin: top right; }
|
||||
|
||||
.fa-layers-bottom-right {
|
||||
bottom: var(--fa-bottom, 0);
|
||||
right: var(--fa-right, 0);
|
||||
top: auto;
|
||||
-webkit-transform: scale(var(--fa-layers-scale, 0.25));
|
||||
transform: scale(var(--fa-layers-scale, 0.25));
|
||||
-webkit-transform-origin: bottom right;
|
||||
transform-origin: bottom right; }
|
||||
|
||||
.fa-layers-bottom-left {
|
||||
bottom: var(--fa-bottom, 0);
|
||||
left: var(--fa-left, 0);
|
||||
right: auto;
|
||||
top: auto;
|
||||
-webkit-transform: scale(var(--fa-layers-scale, 0.25));
|
||||
transform: scale(var(--fa-layers-scale, 0.25));
|
||||
-webkit-transform-origin: bottom left;
|
||||
transform-origin: bottom left; }
|
||||
|
||||
.fa-layers-top-right {
|
||||
top: var(--fa-top, 0);
|
||||
right: var(--fa-right, 0);
|
||||
-webkit-transform: scale(var(--fa-layers-scale, 0.25));
|
||||
transform: scale(var(--fa-layers-scale, 0.25));
|
||||
-webkit-transform-origin: top right;
|
||||
transform-origin: top right; }
|
||||
|
||||
.fa-layers-top-left {
|
||||
left: var(--fa-left, 0);
|
||||
right: auto;
|
||||
top: var(--fa-top, 0);
|
||||
-webkit-transform: scale(var(--fa-layers-scale, 0.25));
|
||||
transform: scale(var(--fa-layers-scale, 0.25));
|
||||
-webkit-transform-origin: top left;
|
||||
transform-origin: top left; }
|
||||
|
||||
.fa-1x {
|
||||
font-size: 1em; }
|
||||
|
||||
.fa-2x {
|
||||
font-size: 2em; }
|
||||
|
||||
.fa-3x {
|
||||
font-size: 3em; }
|
||||
|
||||
.fa-4x {
|
||||
font-size: 4em; }
|
||||
|
||||
.fa-5x {
|
||||
font-size: 5em; }
|
||||
|
||||
.fa-6x {
|
||||
font-size: 6em; }
|
||||
|
||||
.fa-7x {
|
||||
font-size: 7em; }
|
||||
|
||||
.fa-8x {
|
||||
font-size: 8em; }
|
||||
|
||||
.fa-9x {
|
||||
font-size: 9em; }
|
||||
|
||||
.fa-10x {
|
||||
font-size: 10em; }
|
||||
|
||||
.fa-2xs {
|
||||
font-size: 0.625em;
|
||||
line-height: 0.1em;
|
||||
vertical-align: 0.225em; }
|
||||
|
||||
.fa-xs {
|
||||
font-size: 0.75em;
|
||||
line-height: 0.08333em;
|
||||
vertical-align: 0.125em; }
|
||||
|
||||
.fa-sm {
|
||||
font-size: 0.875em;
|
||||
line-height: 0.07143em;
|
||||
vertical-align: 0.05357em; }
|
||||
|
||||
.fa-lg {
|
||||
font-size: 1.25em;
|
||||
line-height: 0.05em;
|
||||
vertical-align: -0.075em; }
|
||||
|
||||
.fa-xl {
|
||||
font-size: 1.5em;
|
||||
line-height: 0.04167em;
|
||||
vertical-align: -0.125em; }
|
||||
|
||||
.fa-2xl {
|
||||
font-size: 2em;
|
||||
line-height: 0.03125em;
|
||||
vertical-align: -0.1875em; }
|
||||
|
||||
.fa-fw {
|
||||
text-align: center;
|
||||
width: 1.25em; }
|
||||
|
||||
.fa-ul {
|
||||
list-style-type: none;
|
||||
margin-left: var(--fa-li-margin, 2.5em);
|
||||
padding-left: 0; }
|
||||
.fa-ul > li {
|
||||
position: relative; }
|
||||
|
||||
.fa-li {
|
||||
left: calc(var(--fa-li-width, 2em) * -1);
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
width: var(--fa-li-width, 2em);
|
||||
line-height: inherit; }
|
||||
|
||||
.fa-border {
|
||||
border-color: var(--fa-border-color, #eee);
|
||||
border-radius: var(--fa-border-radius, 0.1em);
|
||||
border-style: var(--fa-border-style, solid);
|
||||
border-width: var(--fa-border-width, 0.08em);
|
||||
padding: var(--fa-border-padding, 0.2em 0.25em 0.15em); }
|
||||
|
||||
.fa-pull-left {
|
||||
float: left;
|
||||
margin-right: var(--fa-pull-margin, 0.3em); }
|
||||
|
||||
.fa-pull-right {
|
||||
float: right;
|
||||
margin-left: var(--fa-pull-margin, 0.3em); }
|
||||
|
||||
.fa-beat {
|
||||
-webkit-animation-name: fa-beat;
|
||||
animation-name: fa-beat;
|
||||
-webkit-animation-delay: var(--fa-animation-delay, 0s);
|
||||
animation-delay: var(--fa-animation-delay, 0s);
|
||||
-webkit-animation-direction: var(--fa-animation-direction, normal);
|
||||
animation-direction: var(--fa-animation-direction, normal);
|
||||
-webkit-animation-duration: var(--fa-animation-duration, 1s);
|
||||
animation-duration: var(--fa-animation-duration, 1s);
|
||||
-webkit-animation-iteration-count: var(--fa-animation-iteration-count, infinite);
|
||||
animation-iteration-count: var(--fa-animation-iteration-count, infinite);
|
||||
-webkit-animation-timing-function: var(--fa-animation-timing, ease-in-out);
|
||||
animation-timing-function: var(--fa-animation-timing, ease-in-out); }
|
||||
|
||||
.fa-bounce {
|
||||
-webkit-animation-name: fa-bounce;
|
||||
animation-name: fa-bounce;
|
||||
-webkit-animation-delay: var(--fa-animation-delay, 0s);
|
||||
animation-delay: var(--fa-animation-delay, 0s);
|
||||
-webkit-animation-direction: var(--fa-animation-direction, normal);
|
||||
animation-direction: var(--fa-animation-direction, normal);
|
||||
-webkit-animation-duration: var(--fa-animation-duration, 1s);
|
||||
animation-duration: var(--fa-animation-duration, 1s);
|
||||
-webkit-animation-iteration-count: var(--fa-animation-iteration-count, infinite);
|
||||
animation-iteration-count: var(--fa-animation-iteration-count, infinite);
|
||||
-webkit-animation-timing-function: var(--fa-animation-timing, cubic-bezier(0.28, 0.84, 0.42, 1));
|
||||
animation-timing-function: var(--fa-animation-timing, cubic-bezier(0.28, 0.84, 0.42, 1)); }
|
||||
|
||||
.fa-fade {
|
||||
-webkit-animation-name: fa-fade;
|
||||
animation-name: fa-fade;
|
||||
-webkit-animation-delay: var(--fa-animation-delay, 0s);
|
||||
animation-delay: var(--fa-animation-delay, 0s);
|
||||
-webkit-animation-direction: var(--fa-animation-direction, normal);
|
||||
animation-direction: var(--fa-animation-direction, normal);
|
||||
-webkit-animation-duration: var(--fa-animation-duration, 1s);
|
||||
animation-duration: var(--fa-animation-duration, 1s);
|
||||
-webkit-animation-iteration-count: var(--fa-animation-iteration-count, infinite);
|
||||
animation-iteration-count: var(--fa-animation-iteration-count, infinite);
|
||||
-webkit-animation-timing-function: var(--fa-animation-timing, cubic-bezier(0.4, 0, 0.6, 1));
|
||||
animation-timing-function: var(--fa-animation-timing, cubic-bezier(0.4, 0, 0.6, 1)); }
|
||||
|
||||
.fa-beat-fade {
|
||||
-webkit-animation-name: fa-beat-fade;
|
||||
animation-name: fa-beat-fade;
|
||||
-webkit-animation-delay: var(--fa-animation-delay, 0s);
|
||||
animation-delay: var(--fa-animation-delay, 0s);
|
||||
-webkit-animation-direction: var(--fa-animation-direction, normal);
|
||||
animation-direction: var(--fa-animation-direction, normal);
|
||||
-webkit-animation-duration: var(--fa-animation-duration, 1s);
|
||||
animation-duration: var(--fa-animation-duration, 1s);
|
||||
-webkit-animation-iteration-count: var(--fa-animation-iteration-count, infinite);
|
||||
animation-iteration-count: var(--fa-animation-iteration-count, infinite);
|
||||
-webkit-animation-timing-function: var(--fa-animation-timing, cubic-bezier(0.4, 0, 0.6, 1));
|
||||
animation-timing-function: var(--fa-animation-timing, cubic-bezier(0.4, 0, 0.6, 1)); }
|
||||
|
||||
.fa-flip {
|
||||
-webkit-animation-name: fa-flip;
|
||||
animation-name: fa-flip;
|
||||
-webkit-animation-delay: var(--fa-animation-delay, 0s);
|
||||
animation-delay: var(--fa-animation-delay, 0s);
|
||||
-webkit-animation-direction: var(--fa-animation-direction, normal);
|
||||
animation-direction: var(--fa-animation-direction, normal);
|
||||
-webkit-animation-duration: var(--fa-animation-duration, 1s);
|
||||
animation-duration: var(--fa-animation-duration, 1s);
|
||||
-webkit-animation-iteration-count: var(--fa-animation-iteration-count, infinite);
|
||||
animation-iteration-count: var(--fa-animation-iteration-count, infinite);
|
||||
-webkit-animation-timing-function: var(--fa-animation-timing, ease-in-out);
|
||||
animation-timing-function: var(--fa-animation-timing, ease-in-out); }
|
||||
|
||||
.fa-shake {
|
||||
-webkit-animation-name: fa-shake;
|
||||
animation-name: fa-shake;
|
||||
-webkit-animation-delay: var(--fa-animation-delay, 0s);
|
||||
animation-delay: var(--fa-animation-delay, 0s);
|
||||
-webkit-animation-direction: var(--fa-animation-direction, normal);
|
||||
animation-direction: var(--fa-animation-direction, normal);
|
||||
-webkit-animation-duration: var(--fa-animation-duration, 1s);
|
||||
animation-duration: var(--fa-animation-duration, 1s);
|
||||
-webkit-animation-iteration-count: var(--fa-animation-iteration-count, infinite);
|
||||
animation-iteration-count: var(--fa-animation-iteration-count, infinite);
|
||||
-webkit-animation-timing-function: var(--fa-animation-timing, linear);
|
||||
animation-timing-function: var(--fa-animation-timing, linear); }
|
||||
|
||||
.fa-spin {
|
||||
-webkit-animation-name: fa-spin;
|
||||
animation-name: fa-spin;
|
||||
-webkit-animation-delay: var(--fa-animation-delay, 0s);
|
||||
animation-delay: var(--fa-animation-delay, 0s);
|
||||
-webkit-animation-direction: var(--fa-animation-direction, normal);
|
||||
animation-direction: var(--fa-animation-direction, normal);
|
||||
-webkit-animation-duration: var(--fa-animation-duration, 2s);
|
||||
animation-duration: var(--fa-animation-duration, 2s);
|
||||
-webkit-animation-iteration-count: var(--fa-animation-iteration-count, infinite);
|
||||
animation-iteration-count: var(--fa-animation-iteration-count, infinite);
|
||||
-webkit-animation-timing-function: var(--fa-animation-timing, linear);
|
||||
animation-timing-function: var(--fa-animation-timing, linear); }
|
||||
|
||||
.fa-spin-reverse {
|
||||
--fa-animation-direction: reverse; }
|
||||
|
||||
.fa-pulse,
|
||||
.fa-spin-pulse {
|
||||
-webkit-animation-name: fa-spin;
|
||||
animation-name: fa-spin;
|
||||
-webkit-animation-direction: var(--fa-animation-direction, normal);
|
||||
animation-direction: var(--fa-animation-direction, normal);
|
||||
-webkit-animation-duration: var(--fa-animation-duration, 1s);
|
||||
animation-duration: var(--fa-animation-duration, 1s);
|
||||
-webkit-animation-iteration-count: var(--fa-animation-iteration-count, infinite);
|
||||
animation-iteration-count: var(--fa-animation-iteration-count, infinite);
|
||||
-webkit-animation-timing-function: var(--fa-animation-timing, steps(8));
|
||||
animation-timing-function: var(--fa-animation-timing, steps(8)); }
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.fa-beat,
|
||||
.fa-bounce,
|
||||
.fa-fade,
|
||||
.fa-beat-fade,
|
||||
.fa-flip,
|
||||
.fa-pulse,
|
||||
.fa-shake,
|
||||
.fa-spin,
|
||||
.fa-spin-pulse {
|
||||
-webkit-animation-delay: -1ms;
|
||||
animation-delay: -1ms;
|
||||
-webkit-animation-duration: 1ms;
|
||||
animation-duration: 1ms;
|
||||
-webkit-animation-iteration-count: 1;
|
||||
animation-iteration-count: 1;
|
||||
transition-delay: 0s;
|
||||
transition-duration: 0s; } }
|
||||
|
||||
@-webkit-keyframes fa-beat {
|
||||
0%, 90% {
|
||||
-webkit-transform: scale(1);
|
||||
transform: scale(1); }
|
||||
45% {
|
||||
-webkit-transform: scale(var(--fa-beat-scale, 1.25));
|
||||
transform: scale(var(--fa-beat-scale, 1.25)); } }
|
||||
|
||||
@keyframes fa-beat {
|
||||
0%, 90% {
|
||||
-webkit-transform: scale(1);
|
||||
transform: scale(1); }
|
||||
45% {
|
||||
-webkit-transform: scale(var(--fa-beat-scale, 1.25));
|
||||
transform: scale(var(--fa-beat-scale, 1.25)); } }
|
||||
|
||||
@-webkit-keyframes fa-bounce {
|
||||
0% {
|
||||
-webkit-transform: scale(1, 1) translateY(0);
|
||||
transform: scale(1, 1) translateY(0); }
|
||||
10% {
|
||||
-webkit-transform: scale(var(--fa-bounce-start-scale-x, 1.1), var(--fa-bounce-start-scale-y, 0.9)) translateY(0);
|
||||
transform: scale(var(--fa-bounce-start-scale-x, 1.1), var(--fa-bounce-start-scale-y, 0.9)) translateY(0); }
|
||||
30% {
|
||||
-webkit-transform: scale(var(--fa-bounce-jump-scale-x, 0.9), var(--fa-bounce-jump-scale-y, 1.1)) translateY(var(--fa-bounce-height, -0.5em));
|
||||
transform: scale(var(--fa-bounce-jump-scale-x, 0.9), var(--fa-bounce-jump-scale-y, 1.1)) translateY(var(--fa-bounce-height, -0.5em)); }
|
||||
50% {
|
||||
-webkit-transform: scale(var(--fa-bounce-land-scale-x, 1.05), var(--fa-bounce-land-scale-y, 0.95)) translateY(0);
|
||||
transform: scale(var(--fa-bounce-land-scale-x, 1.05), var(--fa-bounce-land-scale-y, 0.95)) translateY(0); }
|
||||
57% {
|
||||
-webkit-transform: scale(1, 1) translateY(var(--fa-bounce-rebound, -0.125em));
|
||||
transform: scale(1, 1) translateY(var(--fa-bounce-rebound, -0.125em)); }
|
||||
64% {
|
||||
-webkit-transform: scale(1, 1) translateY(0);
|
||||
transform: scale(1, 1) translateY(0); }
|
||||
100% {
|
||||
-webkit-transform: scale(1, 1) translateY(0);
|
||||
transform: scale(1, 1) translateY(0); } }
|
||||
|
||||
@keyframes fa-bounce {
|
||||
0% {
|
||||
-webkit-transform: scale(1, 1) translateY(0);
|
||||
transform: scale(1, 1) translateY(0); }
|
||||
10% {
|
||||
-webkit-transform: scale(var(--fa-bounce-start-scale-x, 1.1), var(--fa-bounce-start-scale-y, 0.9)) translateY(0);
|
||||
transform: scale(var(--fa-bounce-start-scale-x, 1.1), var(--fa-bounce-start-scale-y, 0.9)) translateY(0); }
|
||||
30% {
|
||||
-webkit-transform: scale(var(--fa-bounce-jump-scale-x, 0.9), var(--fa-bounce-jump-scale-y, 1.1)) translateY(var(--fa-bounce-height, -0.5em));
|
||||
transform: scale(var(--fa-bounce-jump-scale-x, 0.9), var(--fa-bounce-jump-scale-y, 1.1)) translateY(var(--fa-bounce-height, -0.5em)); }
|
||||
50% {
|
||||
-webkit-transform: scale(var(--fa-bounce-land-scale-x, 1.05), var(--fa-bounce-land-scale-y, 0.95)) translateY(0);
|
||||
transform: scale(var(--fa-bounce-land-scale-x, 1.05), var(--fa-bounce-land-scale-y, 0.95)) translateY(0); }
|
||||
57% {
|
||||
-webkit-transform: scale(1, 1) translateY(var(--fa-bounce-rebound, -0.125em));
|
||||
transform: scale(1, 1) translateY(var(--fa-bounce-rebound, -0.125em)); }
|
||||
64% {
|
||||
-webkit-transform: scale(1, 1) translateY(0);
|
||||
transform: scale(1, 1) translateY(0); }
|
||||
100% {
|
||||
-webkit-transform: scale(1, 1) translateY(0);
|
||||
transform: scale(1, 1) translateY(0); } }
|
||||
|
||||
@-webkit-keyframes fa-fade {
|
||||
50% {
|
||||
opacity: var(--fa-fade-opacity, 0.4); } }
|
||||
|
||||
@keyframes fa-fade {
|
||||
50% {
|
||||
opacity: var(--fa-fade-opacity, 0.4); } }
|
||||
|
||||
@-webkit-keyframes fa-beat-fade {
|
||||
0%, 100% {
|
||||
opacity: var(--fa-beat-fade-opacity, 0.4);
|
||||
-webkit-transform: scale(1);
|
||||
transform: scale(1); }
|
||||
50% {
|
||||
opacity: 1;
|
||||
-webkit-transform: scale(var(--fa-beat-fade-scale, 1.125));
|
||||
transform: scale(var(--fa-beat-fade-scale, 1.125)); } }
|
||||
|
||||
@keyframes fa-beat-fade {
|
||||
0%, 100% {
|
||||
opacity: var(--fa-beat-fade-opacity, 0.4);
|
||||
-webkit-transform: scale(1);
|
||||
transform: scale(1); }
|
||||
50% {
|
||||
opacity: 1;
|
||||
-webkit-transform: scale(var(--fa-beat-fade-scale, 1.125));
|
||||
transform: scale(var(--fa-beat-fade-scale, 1.125)); } }
|
||||
|
||||
@-webkit-keyframes fa-flip {
|
||||
50% {
|
||||
-webkit-transform: rotate3d(var(--fa-flip-x, 0), var(--fa-flip-y, 1), var(--fa-flip-z, 0), var(--fa-flip-angle, -180deg));
|
||||
transform: rotate3d(var(--fa-flip-x, 0), var(--fa-flip-y, 1), var(--fa-flip-z, 0), var(--fa-flip-angle, -180deg)); } }
|
||||
|
||||
@keyframes fa-flip {
|
||||
50% {
|
||||
-webkit-transform: rotate3d(var(--fa-flip-x, 0), var(--fa-flip-y, 1), var(--fa-flip-z, 0), var(--fa-flip-angle, -180deg));
|
||||
transform: rotate3d(var(--fa-flip-x, 0), var(--fa-flip-y, 1), var(--fa-flip-z, 0), var(--fa-flip-angle, -180deg)); } }
|
||||
|
||||
@-webkit-keyframes fa-shake {
|
||||
0% {
|
||||
-webkit-transform: rotate(-15deg);
|
||||
transform: rotate(-15deg); }
|
||||
4% {
|
||||
-webkit-transform: rotate(15deg);
|
||||
transform: rotate(15deg); }
|
||||
8%, 24% {
|
||||
-webkit-transform: rotate(-18deg);
|
||||
transform: rotate(-18deg); }
|
||||
12%, 28% {
|
||||
-webkit-transform: rotate(18deg);
|
||||
transform: rotate(18deg); }
|
||||
16% {
|
||||
-webkit-transform: rotate(-22deg);
|
||||
transform: rotate(-22deg); }
|
||||
20% {
|
||||
-webkit-transform: rotate(22deg);
|
||||
transform: rotate(22deg); }
|
||||
32% {
|
||||
-webkit-transform: rotate(-12deg);
|
||||
transform: rotate(-12deg); }
|
||||
36% {
|
||||
-webkit-transform: rotate(12deg);
|
||||
transform: rotate(12deg); }
|
||||
40%, 100% {
|
||||
-webkit-transform: rotate(0deg);
|
||||
transform: rotate(0deg); } }
|
||||
|
||||
@keyframes fa-shake {
|
||||
0% {
|
||||
-webkit-transform: rotate(-15deg);
|
||||
transform: rotate(-15deg); }
|
||||
4% {
|
||||
-webkit-transform: rotate(15deg);
|
||||
transform: rotate(15deg); }
|
||||
8%, 24% {
|
||||
-webkit-transform: rotate(-18deg);
|
||||
transform: rotate(-18deg); }
|
||||
12%, 28% {
|
||||
-webkit-transform: rotate(18deg);
|
||||
transform: rotate(18deg); }
|
||||
16% {
|
||||
-webkit-transform: rotate(-22deg);
|
||||
transform: rotate(-22deg); }
|
||||
20% {
|
||||
-webkit-transform: rotate(22deg);
|
||||
transform: rotate(22deg); }
|
||||
32% {
|
||||
-webkit-transform: rotate(-12deg);
|
||||
transform: rotate(-12deg); }
|
||||
36% {
|
||||
-webkit-transform: rotate(12deg);
|
||||
transform: rotate(12deg); }
|
||||
40%, 100% {
|
||||
-webkit-transform: rotate(0deg);
|
||||
transform: rotate(0deg); } }
|
||||
|
||||
@-webkit-keyframes fa-spin {
|
||||
0% {
|
||||
-webkit-transform: rotate(0deg);
|
||||
transform: rotate(0deg); }
|
||||
100% {
|
||||
-webkit-transform: rotate(360deg);
|
||||
transform: rotate(360deg); } }
|
||||
|
||||
@keyframes fa-spin {
|
||||
0% {
|
||||
-webkit-transform: rotate(0deg);
|
||||
transform: rotate(0deg); }
|
||||
100% {
|
||||
-webkit-transform: rotate(360deg);
|
||||
transform: rotate(360deg); } }
|
||||
|
||||
.fa-rotate-90 {
|
||||
-webkit-transform: rotate(90deg);
|
||||
transform: rotate(90deg); }
|
||||
|
||||
.fa-rotate-180 {
|
||||
-webkit-transform: rotate(180deg);
|
||||
transform: rotate(180deg); }
|
||||
|
||||
.fa-rotate-270 {
|
||||
-webkit-transform: rotate(270deg);
|
||||
transform: rotate(270deg); }
|
||||
|
||||
.fa-flip-horizontal {
|
||||
-webkit-transform: scale(-1, 1);
|
||||
transform: scale(-1, 1); }
|
||||
|
||||
.fa-flip-vertical {
|
||||
-webkit-transform: scale(1, -1);
|
||||
transform: scale(1, -1); }
|
||||
|
||||
.fa-flip-both,
|
||||
.fa-flip-horizontal.fa-flip-vertical {
|
||||
-webkit-transform: scale(-1, -1);
|
||||
transform: scale(-1, -1); }
|
||||
|
||||
.fa-rotate-by {
|
||||
-webkit-transform: rotate(var(--fa-rotate-angle, none));
|
||||
transform: rotate(var(--fa-rotate-angle, none)); }
|
||||
|
||||
.fa-stack {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
height: 2em;
|
||||
position: relative;
|
||||
width: 2.5em; }
|
||||
|
||||
.fa-stack-1x,
|
||||
.fa-stack-2x {
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
margin: auto;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
z-index: var(--fa-stack-z-index, auto); }
|
||||
|
||||
.svg-inline--fa.fa-stack-1x {
|
||||
height: 1em;
|
||||
width: 1.25em; }
|
||||
|
||||
.svg-inline--fa.fa-stack-2x {
|
||||
height: 2em;
|
||||
width: 2.5em; }
|
||||
|
||||
.fa-inverse {
|
||||
color: var(--fa-inverse, #fff); }
|
||||
|
||||
.sr-only,
|
||||
.fa-sr-only {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
padding: 0;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
clip: rect(0, 0, 0, 0);
|
||||
white-space: nowrap;
|
||||
border-width: 0; }
|
||||
|
||||
.sr-only-focusable:not(:focus),
|
||||
.fa-sr-only-focusable:not(:focus) {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
padding: 0;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
clip: rect(0, 0, 0, 0);
|
||||
white-space: nowrap;
|
||||
border-width: 0; }
|
||||
|
||||
.svg-inline--fa .fa-primary {
|
||||
fill: var(--fa-primary-color, currentColor);
|
||||
opacity: var(--fa-primary-opacity, 1); }
|
||||
|
||||
.svg-inline--fa .fa-secondary {
|
||||
fill: var(--fa-secondary-color, currentColor);
|
||||
opacity: var(--fa-secondary-opacity, 0.4); }
|
||||
|
||||
.svg-inline--fa.fa-swap-opacity .fa-primary {
|
||||
opacity: var(--fa-secondary-opacity, 0.4); }
|
||||
|
||||
.svg-inline--fa.fa-swap-opacity .fa-secondary {
|
||||
opacity: var(--fa-primary-opacity, 1); }
|
||||
|
||||
.svg-inline--fa mask .fa-primary,
|
||||
.svg-inline--fa mask .fa-secondary {
|
||||
fill: black; }
|
||||
|
||||
.fad.fa-inverse,
|
||||
.fa-duotone.fa-inverse {
|
||||
color: var(--fa-inverse, #fff); }
|
||||
6
frontend/static/fontawesome/css/svg-with-js.min.css
vendored
Normal file
6
frontend/static/fontawesome/css/svg-with-js.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
26
frontend/static/fontawesome/css/v4-font-face.css
vendored
Normal file
26
frontend/static/fontawesome/css/v4-font-face.css
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
/*!
|
||||
* Font Awesome Free 6.2.1 by @fontawesome - https://fontawesome.com
|
||||
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
|
||||
* Copyright 2022 Fonticons, Inc.
|
||||
*/
|
||||
@font-face {
|
||||
font-family: 'FontAwesome';
|
||||
font-display: block;
|
||||
src: url("../webfonts/fa-solid-900.woff2") format("woff2"), url("../webfonts/fa-solid-900.ttf") format("truetype"); }
|
||||
|
||||
@font-face {
|
||||
font-family: 'FontAwesome';
|
||||
font-display: block;
|
||||
src: url("../webfonts/fa-brands-400.woff2") format("woff2"), url("../webfonts/fa-brands-400.ttf") format("truetype"); }
|
||||
|
||||
@font-face {
|
||||
font-family: 'FontAwesome';
|
||||
font-display: block;
|
||||
src: url("../webfonts/fa-regular-400.woff2") format("woff2"), url("../webfonts/fa-regular-400.ttf") format("truetype");
|
||||
unicode-range: U+F003,U+F006,U+F014,U+F016-F017,U+F01A-F01B,U+F01D,U+F022,U+F03E,U+F044,U+F046,U+F05C-F05D,U+F06E,U+F070,U+F087-F088,U+F08A,U+F094,U+F096-F097,U+F09D,U+F0A0,U+F0A2,U+F0A4-F0A7,U+F0C5,U+F0C7,U+F0E5-F0E6,U+F0EB,U+F0F6-F0F8,U+F10C,U+F114-F115,U+F118-F11A,U+F11C-F11D,U+F133,U+F147,U+F14E,U+F150-F152,U+F185-F186,U+F18E,U+F190-F192,U+F196,U+F1C1-F1C9,U+F1D9,U+F1DB,U+F1E3,U+F1EA,U+F1F7,U+F1F9,U+F20A,U+F247-F248,U+F24A,U+F24D,U+F255-F25B,U+F25D,U+F271-F274,U+F278,U+F27B,U+F28C,U+F28E,U+F29C,U+F2B5,U+F2B7,U+F2BA,U+F2BC,U+F2BE,U+F2C0-F2C1,U+F2C3,U+F2D0,U+F2D2,U+F2D4,U+F2DC; }
|
||||
|
||||
@font-face {
|
||||
font-family: 'FontAwesome';
|
||||
font-display: block;
|
||||
src: url("../webfonts/fa-v4compatibility.woff2") format("woff2"), url("../webfonts/fa-v4compatibility.ttf") format("truetype");
|
||||
unicode-range: U+F041,U+F047,U+F065-F066,U+F07D-F07E,U+F080,U+F08B,U+F08E,U+F090,U+F09A,U+F0AC,U+F0AE,U+F0B2,U+F0D0,U+F0D6,U+F0E4,U+F0EC,U+F10A-F10B,U+F123,U+F13E,U+F148-F149,U+F14C,U+F156,U+F15E,U+F160-F161,U+F163,U+F175-F178,U+F195,U+F1F8,U+F219,U+F27A; }
|
||||
6
frontend/static/fontawesome/css/v4-font-face.min.css
vendored
Normal file
6
frontend/static/fontawesome/css/v4-font-face.min.css
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
/*!
|
||||
* Font Awesome Free 6.2.1 by @fontawesome - https://fontawesome.com
|
||||
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
|
||||
* Copyright 2022 Fonticons, Inc.
|
||||
*/
|
||||
@font-face{font-family:"FontAwesome";font-display:block;src:url(../webfonts/fa-solid-900.woff2) format("woff2"),url(../webfonts/fa-solid-900.ttf) format("truetype")}@font-face{font-family:"FontAwesome";font-display:block;src:url(../webfonts/fa-brands-400.woff2) format("woff2"),url(../webfonts/fa-brands-400.ttf) format("truetype")}@font-face{font-family:"FontAwesome";font-display:block;src:url(../webfonts/fa-regular-400.woff2) format("woff2"),url(../webfonts/fa-regular-400.ttf) format("truetype");unicode-range:u+f003,u+f006,u+f014,u+f016-f017,u+f01a-f01b,u+f01d,u+f022,u+f03e,u+f044,u+f046,u+f05c-f05d,u+f06e,u+f070,u+f087-f088,u+f08a,u+f094,u+f096-f097,u+f09d,u+f0a0,u+f0a2,u+f0a4-f0a7,u+f0c5,u+f0c7,u+f0e5-f0e6,u+f0eb,u+f0f6-f0f8,u+f10c,u+f114-f115,u+f118-f11a,u+f11c-f11d,u+f133,u+f147,u+f14e,u+f150-f152,u+f185-f186,u+f18e,u+f190-f192,u+f196,u+f1c1-f1c9,u+f1d9,u+f1db,u+f1e3,u+f1ea,u+f1f7,u+f1f9,u+f20a,u+f247-f248,u+f24a,u+f24d,u+f255-f25b,u+f25d,u+f271-f274,u+f278,u+f27b,u+f28c,u+f28e,u+f29c,u+f2b5,u+f2b7,u+f2ba,u+f2bc,u+f2be,u+f2c0-f2c1,u+f2c3,u+f2d0,u+f2d2,u+f2d4,u+f2dc}@font-face{font-family:"FontAwesome";font-display:block;src:url(../webfonts/fa-v4compatibility.woff2) format("woff2"),url(../webfonts/fa-v4compatibility.ttf) format("truetype");unicode-range:u+f041,u+f047,u+f065-f066,u+f07d-f07e,u+f080,u+f08b,u+f08e,u+f090,u+f09a,u+f0ac,u+f0ae,u+f0b2,u+f0d0,u+f0d6,u+f0e4,u+f0ec,u+f10a-f10b,u+f123,u+f13e,u+f148-f149,u+f14c,u+f156,u+f15e,u+f160-f161,u+f163,u+f175-f178,u+f195,u+f1f8,u+f219,u+f27a}
|
||||
2194
frontend/static/fontawesome/css/v4-shims.css
vendored
Normal file
2194
frontend/static/fontawesome/css/v4-shims.css
vendored
Normal file
File diff suppressed because it is too large
Load Diff
6
frontend/static/fontawesome/css/v4-shims.min.css
vendored
Normal file
6
frontend/static/fontawesome/css/v4-shims.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
22
frontend/static/fontawesome/css/v5-font-face.css
vendored
Normal file
22
frontend/static/fontawesome/css/v5-font-face.css
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
/*!
|
||||
* Font Awesome Free 6.2.1 by @fontawesome - https://fontawesome.com
|
||||
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
|
||||
* Copyright 2022 Fonticons, Inc.
|
||||
*/
|
||||
@font-face {
|
||||
font-family: 'Font Awesome 5 Brands';
|
||||
font-display: block;
|
||||
font-weight: 400;
|
||||
src: url("../webfonts/fa-brands-400.woff2") format("woff2"), url("../webfonts/fa-brands-400.ttf") format("truetype"); }
|
||||
|
||||
@font-face {
|
||||
font-family: 'Font Awesome 5 Free';
|
||||
font-display: block;
|
||||
font-weight: 900;
|
||||
src: url("../webfonts/fa-solid-900.woff2") format("woff2"), url("../webfonts/fa-solid-900.ttf") format("truetype"); }
|
||||
|
||||
@font-face {
|
||||
font-family: 'Font Awesome 5 Free';
|
||||
font-display: block;
|
||||
font-weight: 400;
|
||||
src: url("../webfonts/fa-regular-400.woff2") format("woff2"), url("../webfonts/fa-regular-400.ttf") format("truetype"); }
|
||||
6
frontend/static/fontawesome/css/v5-font-face.min.css
vendored
Normal file
6
frontend/static/fontawesome/css/v5-font-face.min.css
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
/*!
|
||||
* Font Awesome Free 6.2.1 by @fontawesome - https://fontawesome.com
|
||||
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
|
||||
* Copyright 2022 Fonticons, Inc.
|
||||
*/
|
||||
@font-face{font-family:"Font Awesome 5 Brands";font-display:block;font-weight:400;src:url(../webfonts/fa-brands-400.woff2) format("woff2"),url(../webfonts/fa-brands-400.ttf) format("truetype")}@font-face{font-family:"Font Awesome 5 Free";font-display:block;font-weight:900;src:url(../webfonts/fa-solid-900.woff2) format("woff2"),url(../webfonts/fa-solid-900.ttf) format("truetype")}@font-face{font-family:"Font Awesome 5 Free";font-display:block;font-weight:400;src:url(../webfonts/fa-regular-400.woff2) format("woff2"),url(../webfonts/fa-regular-400.ttf) format("truetype")}
|
||||
BIN
frontend/static/fontawesome/webfonts/fa-brands-400.ttf
Normal file
BIN
frontend/static/fontawesome/webfonts/fa-brands-400.ttf
Normal file
Binary file not shown.
BIN
frontend/static/fontawesome/webfonts/fa-brands-400.woff2
Normal file
BIN
frontend/static/fontawesome/webfonts/fa-brands-400.woff2
Normal file
Binary file not shown.
BIN
frontend/static/fontawesome/webfonts/fa-regular-400.ttf
Normal file
BIN
frontend/static/fontawesome/webfonts/fa-regular-400.ttf
Normal file
Binary file not shown.
BIN
frontend/static/fontawesome/webfonts/fa-regular-400.woff2
Normal file
BIN
frontend/static/fontawesome/webfonts/fa-regular-400.woff2
Normal file
Binary file not shown.
BIN
frontend/static/fontawesome/webfonts/fa-solid-900.ttf
Normal file
BIN
frontend/static/fontawesome/webfonts/fa-solid-900.ttf
Normal file
Binary file not shown.
BIN
frontend/static/fontawesome/webfonts/fa-solid-900.woff2
Normal file
BIN
frontend/static/fontawesome/webfonts/fa-solid-900.woff2
Normal file
Binary file not shown.
BIN
frontend/static/fontawesome/webfonts/fa-v4compatibility.ttf
Normal file
BIN
frontend/static/fontawesome/webfonts/fa-v4compatibility.ttf
Normal file
Binary file not shown.
BIN
frontend/static/fontawesome/webfonts/fa-v4compatibility.woff2
Normal file
BIN
frontend/static/fontawesome/webfonts/fa-v4compatibility.woff2
Normal file
Binary file not shown.
13
frontend/tailwind.config.js
Normal file
13
frontend/tailwind.config.js
Normal file
@@ -0,0 +1,13 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
content: [
|
||||
'./src/**/*.rs',
|
||||
],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [
|
||||
require('@tailwindcss/forms'),
|
||||
],
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user